Skip to content

Well-Known Configuration — Complete OIDC Provider Metadata Reference

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Well. We cover key concepts, practical examples, and best practices to help you master this topic.

The well-known configuration document is a JSON file at /.well-known/openid-configuration that describes every aspect of an OIDC provider's capabilities, from endpoint URLs to supported signing algorithms and claim types.

What You'll Learn

  • Complete field reference for the discovery document
  • Provider extensions and custom metadata
  • Using the document for secure client initialization

Why It Matters

Two OIDC providers may support different features. One may support ES256 signing, another only RS256. One may support form_post response mode, another only query. The discovery document tells your client exactly what the provider supports.

Real-World Use

Before Doda Browser sends an authentication request, it checks the provider's discovery document for supported response types, scopes, and token endpoint auth methods. This prevents runtime errors from unsupported features.

flowchart LR
    Config["Well-Known Config"] --> Auth["Authorization\nEndpoint"]
    Config --> Token["Token\nEndpoint"]
    Config --> JWKS["JWKS\nURI"]
    Config --> UserInfo["UserInfo\nEndpoint"]
    Config --> Scopes["Supported\nScopes"]
    Config --> Algs["Supported\nAlgorithms"]
    Config --> Claims["Supported\nClaims"]
    style Config fill:#dbeafe,stroke:#2563eb

Complete Field Reference

config = {
    # REQUIRED
    "issuer": "https://provider.example.com",
    "authorization_endpoint": "https://provider.example.com/auth",
    "token_endpoint": "https://provider.example.com/token",
    "jwks_uri": "https://provider.example.com/certs",
    "response_types_supported": ["code", "id_token", "token id_token"],
    "subject_types_supported": ["public", "pairwise"],
    "id_token_signing_alg_values_supported": ["RS256", "ES256"],

    # RECOMMENDED
    "scopes_supported": ["openid", "profile", "email", "address", "phone"],
    "claims_supported": ["sub", "iss", "aud", "exp", "iat", "name", "email"],
    "userinfo_endpoint": "https://provider.example.com/userinfo",
    "registration_endpoint": "https://provider.example.com/register",

    # OPTIONAL
    "token_endpoint_auth_methods_supported": [
        "client_secret_basic",
        "client_secret_post",
        "private_key_jwt"
    ],
    "grant_types_supported": [
        "authorization_code",
        "implicit",
        "refresh_token",
        "urn:ietf:params:oauth:grant-type:jwt-bearer"
    ],
    "response_modes_supported": ["query", "fragment", "form_post"],
    "acr_values_supported": [
        "urn:mace:incommon:iap:silver",
        "urn:mace:incommon:iap:bronze"
    ],
    "id_token_encryption_alg_values_supported": ["RSA-OAEP", "RSA1_5"],
    "id_token_encryption_enc_values_supported": ["A128GCM", "A256GCM"],
    "request_object_signing_alg_values_supported": ["RS256", "ES256"],
    "display_values_supported": ["page", "popup", "touch", "wap"],
    "claim_types_supported": ["normal", "distributed", "aggregated"],
    "claims_locales_supported": ["en-US", "fr-FR", "es-ES"],
    "ui_locales_supported": ["en-US", "fr-FR"],
    "service_documentation": "https://provider.example.com/docs",
    "op_policy_uri": "https://provider.example.com/policy",
    "op_tos_uri": "https://provider.example.com/tos",
}

Using the Config for Security Checks

def validate_config_for_client(config, client_config):
    errors = []

    # Check response type support
    if client_config["response_type"] not in config.get("response_types_supported", []):
        errors.append(f"Response type {client_config['response_type']} not supported")

    # Check signing algorithm
    if client_config.get("id_token_algorithm") not in config.get("id_token_signing_alg_values_supported", []):
        errors.append(f"Algorithm {client_config['id_token_algorithm']} not supported")

    # Check scopes
    requested_scopes = set(client_config["scope"].split())
    supported_scopes = set(config.get("scopes_supported", []))
    unsupported = requested_scopes - supported_scopes
    if unsupported:
        errors.append(f"Unsupported scopes: {unsupported}")

    return errors

Provider Extensions

Providers add custom fields to the discovery document:

# Google extensions
google_config_extras = {
    "claims_parameter_supported": True,
    "request_parameter_supported": True,
    "request_uri_parameter_supported": True,
}

# Microsoft extensions
microsoft_config_extras = {
    "cloud_instance_name": "microsoftonline.com",
    "tenant_region_scope": "NA",
    "cloud_graph_host_name": "graph.windows.net",
    "msgraph_host": "graph.microsoft.com",
}

Common Mistakes

1. Assuming All Fields Are Present

Many providers omit optional fields. Always check with .get() and provide defaults.

2. Ignoring subject_types_supported

pairwise subject type means the sub claim differs per client. If your app expects the same sub across clients, verify support for public.

3. Not Checking response_modes_supported

If your app uses form_post mode but the provider only supports query, the auth response will not be delivered correctly.

4. Skipping Timeout on Discovery Fetch

A hanging discovery request blocks authentication for all users. Always set a timeout (e.g., 5 seconds).

5. Using Env-Specific URLs Without Checks

A provider's discovery URL for production differs from staging. Validate the issuer matches the expected environment.

Practice Questions

  1. What are the four REQUIRED fields in the discovery document?
  2. Why should you check response_types_supported before sending an auth request?
  3. What is the difference between public and pairwise subject types?
  4. How does grant_types_supported affect your token exchange Strategy?
  5. Why might a provider add custom fields to the discovery document?

Answers:

  1. issuer, authorization_endpoint, token_endpoint, jwks_uri, and response_types_supported.
  2. If your response_type is not supported, the provider returns an error instead of an auth code or token.
  3. public means the sub claim is the same across all clients. pairwise means each client gets a different sub for privacy.
  4. If the provider does not support refresh_token, you cannot obtain refresh tokens for long-lived sessions.
  5. Custom fields advertise additional features (e.g., claims_parameter_supported, request_parameter_supported) that providers offer beyond the standard.

Challenge: Write a validation script that checks whether a provider's discovery document supports all the features your OIDC client needs. Include checks for response types, signing algorithms, scopes, and grant types.

FAQ

What happens if the discovery document changes after my client initialized?

: The client should re-fetch periodically. Changes are rare but possible when providers upgrade infrastructure.

Can I use the discovery document for non-OIDC OAuth2 providers?

: Some OAuth2 providers host a similar document, but it is not standardized. OIDC mandates the discovery endpoint.

How do I discover the discovery URL?

: The issuer URL is published in the provider's documentation. Append /.well-known/openid-configuration to get the config.

What is the `registration_endpoint`?

: This endpoint allows dynamic client registration. New applications can register without manual configuration.

Is the discovery document the same for all tenants?

: For multi-tenant providers, each tenant has its own issuer URL and discovery document with tenant-specific endpoints.

Mini Project

Build a Python CLI tool that takes an issuer URL, fetches the well-known configuration, and prints a formatted report of all standard fields, provider extensions, and any missing required fields. Include validation warnings for unsupported features.

What's Next

Continue with OIDC Scopes to control which claims you receive, or explore Authentication Request for building OIDC authorization URLs.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro