{
  "type": "record",
  "Aspect": {
    "name": "dataHubOAuthClientInfo",
    "schemaVersion": 3
  },
  "name": "DataHubOAuthClientInfo",
  "namespace": "com.linkedin.pegasus2avro.oauth.client",
  "fields": [
    {
      "type": {
        "type": "enum",
        "symbolDocs": {
          "CONFIDENTIAL": "Confidential client \u2014 authenticates with a client secret.\nSuitable for server-side applications that can securely store credentials.",
          "PUBLIC": "Public client \u2014 cannot securely store a client secret.\nMust use PKCE for authorization code flow. No client secret is issued.\nSuitable for SPAs, native apps, and CLI tools."
        },
        "name": "OAuthClientType",
        "namespace": "com.linkedin.pegasus2avro.oauth.client",
        "symbols": [
          "CONFIDENTIAL",
          "PUBLIC"
        ],
        "doc": "Whether an OAuth2 client is confidential (has a secret) or public (relies on PKCE only)."
      },
      "name": "clientType",
      "default": "CONFIDENTIAL",
      "doc": "Whether this client is confidential (has a secret) or public (PKCE only).\nDefaults to CONFIDENTIAL for backward compatibility."
    },
    {
      "Searchable": {
        "fieldType": "TEXT_PARTIAL"
      },
      "type": "string",
      "name": "name",
      "doc": "Human-readable display name for the client."
    },
    {
      "type": [
        "null",
        "string"
      ],
      "name": "description",
      "default": null,
      "doc": "Optional description of the client's purpose."
    },
    {
      "type": [
        "null",
        "string"
      ],
      "name": "encryptedSecret",
      "default": null,
      "doc": "AES-encrypted client secret via SecretService \u2014 same storage pattern as\ndataHubSecretValue.value. Protected by category:internal entity access\nrestrictions + encryption at rest. Never returned by OAuthClientController."
    },
    {
      "type": [
        "null",
        {
          "type": "array",
          "items": "string"
        }
      ],
      "name": "redirectUris",
      "default": null,
      "doc": "Allowed redirect URIs (for authorization_code flow)."
    },
    {
      "type": "string",
      "name": "scopes",
      "doc": "Comma-separated list of granted OAuth scopes."
    },
    {
      "type": "string",
      "name": "grantTypes",
      "doc": "Comma-separated list of enabled grant types.\nValues: client_credentials, authorization_code, refresh_token"
    },
    {
      "Searchable": {
        "fieldType": "URN"
      },
      "java": {
        "class": "com.linkedin.pegasus2avro.common.urn.Urn"
      },
      "type": "string",
      "name": "ownerUrn",
      "doc": "URN of the actor (user) who created this client.",
      "Urn": "Urn"
    },
    {
      "Searchable": {
        "fieldType": "COUNT",
        "queryByDefault": false
      },
      "type": "long",
      "name": "createdAt",
      "doc": "When this client was created."
    },
    {
      "Searchable": {
        "fieldType": "KEYWORD"
      },
      "type": [
        "null",
        "string"
      ],
      "name": "softwareId",
      "default": null,
      "doc": "RFC 7591 software_id \u2014 opaque identifier asserted by the client software.\nUsed to correlate registrations across instances of the same software."
    },
    {
      "type": [
        "null",
        "string"
      ],
      "name": "softwareVersion",
      "default": null,
      "doc": "RFC 7591 software_version \u2014 version of the software identified by softwareId."
    },
    {
      "type": [
        "null",
        "string"
      ],
      "name": "jwksUri",
      "default": null,
      "doc": "RFC 7591 jwks_uri \u2014 URL where the client publishes its JSON Web Key Set,\nused for private_key_jwt token endpoint auth."
    },
    {
      "type": [
        "null",
        "string"
      ],
      "name": "jwks",
      "default": null,
      "doc": "RFC 7591 jwks \u2014 inline JWKS as a serialized JSON string.\nMutually exclusive with jwksUri."
    },
    {
      "type": [
        "null",
        {
          "type": "array",
          "items": "string"
        }
      ],
      "name": "contacts",
      "default": null,
      "doc": "RFC 7591 contacts \u2014 array of email addresses for client administrators."
    },
    {
      "type": [
        "null",
        "string"
      ],
      "name": "clientUri",
      "default": null,
      "doc": "RFC 7591 client_uri \u2014 URL of the client's home page."
    },
    {
      "type": [
        "null",
        "string"
      ],
      "name": "logoUri",
      "default": null,
      "doc": "RFC 7591 logo_uri \u2014 URL of the client's logo."
    },
    {
      "type": [
        "null",
        "string"
      ],
      "name": "policyUri",
      "default": null,
      "doc": "RFC 7591 policy_uri \u2014 URL of the client's privacy policy."
    },
    {
      "type": [
        "null",
        "string"
      ],
      "name": "tosUri",
      "default": null,
      "doc": "RFC 7591 tos_uri \u2014 URL of the client's terms of service."
    },
    {
      "type": [
        "null",
        {
          "type": "enum",
          "symbolDocs": {
            "CLIENT_SECRET_BASIC": "Client secret sent via HTTP Basic auth header.",
            "CLIENT_SECRET_JWT": "Client authenticates with an HMAC-signed JWT assertion derived from the secret.",
            "CLIENT_SECRET_POST": "Client secret sent in the request body.",
            "NONE": "Public client \u2014 no authentication. PKCE is required for authorization_code.",
            "PRIVATE_KEY_JWT": "Client authenticates with an asymmetric-key JWT assertion verified via jwks/jwksUri."
          },
          "name": "TokenEndpointAuthMethod",
          "namespace": "com.linkedin.pegasus2avro.oauth.client",
          "symbols": [
            "NONE",
            "CLIENT_SECRET_BASIC",
            "CLIENT_SECRET_POST",
            "CLIENT_SECRET_JWT",
            "PRIVATE_KEY_JWT"
          ],
          "doc": "Client authentication method used at the token endpoint, per RFC 7591 \u00a72 and OIDC Core \u00a79.\nDetermines how a registered client proves its identity when calling /token."
        }
      ],
      "name": "tokenEndpointAuthMethod",
      "default": null,
      "doc": "Client authentication method used at the token endpoint.\nDefaults to CLIENT_SECRET_BASIC for confidential clients and NONE for public clients."
    },
    {
      "type": [
        "null",
        "string"
      ],
      "name": "registrationAccessTokenHash",
      "default": null,
      "doc": "SHA-256 hash of the registration access token (RFC 7592). The raw token is returned\nto the client exactly once at registration; only this hash is persisted for later\nvalidation against GET/PUT/DELETE on the registration management endpoint."
    },
    {
      "type": [
        "null",
        "long"
      ],
      "name": "clientIdIssuedAt",
      "default": null,
      "doc": "RFC 7591 client_id_issued_at \u2014 Unix epoch when the client_id was issued.\nFor DCR-registered clients this matches createdAt; for admin-created clients it may be unset."
    },
    {
      "type": [
        "null",
        "long"
      ],
      "name": "clientSecretExpiresAt",
      "default": null,
      "doc": "RFC 7591 client_secret_expires_at \u2014 Unix epoch when the client_secret expires,\nor 0 to indicate no expiry. Unset means no secret (public client)."
    },
    {
      "Searchable": {
        "fieldType": "BOOLEAN"
      },
      "type": "boolean",
      "name": "dcrRegistered",
      "default": false,
      "doc": "Whether this client was created via Dynamic Client Registration (RFC 7591) rather than\nthe admin OAuthClientController. Used to differentiate policy and audit handling."
    },
    {
      "type": [
        "null",
        "int"
      ],
      "name": "accessTokenTimeToLiveSeconds",
      "default": null,
      "doc": "Per-client override for the access token TTL, in seconds. When set, must be in\n[1, 3600] \u2014 i.e. the registry-wide 1h max is the ceiling, this knob can only\nshorten it. When unset, the registry default of 3600s applies. The cap means\nthis can never extend a token beyond the policy maximum; it exists so operators\ncan register short-lived clients (e.g. CI integrations) and to make\nexpiry-and-refresh behaviour testable in smoke tests without a 1-hour wait."
    },
    {
      "Searchable": {
        "fieldType": "COUNT",
        "queryByDefault": false
      },
      "type": [
        "null",
        "long"
      ],
      "name": "lastTokenIssuedAt",
      "default": null,
      "doc": "Unix epoch ms of the most recent successful token issuance for this client.\nUpdated opportunistically by DataHubOAuth2AuthorizationService.save() with a\nper-client debounce so heavy use stays bounded to ~1 MCP/day on this aspect.\nUsed by oauth_client_cleanup to switch GC from age-based to inactivity-based:\nabandoned registrations are reaped while active clients are preserved\nindefinitely. Searchable so SREs can query \"clients with no activity in N days\"."
    }
  ],
  "doc": "Metadata for a DataHub OAuth2 registered client (used by the GMS authorization server).\nThe client secret is stored encrypted via SecretService and never returned plaintext."
}