Skip to content

OIDC Scopes — Controlling Access to User Claims in OpenID Connect

DodaTech Updated 2026-06-28 4 min read

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

OIDC scopes are space-separated identifiers in the authentication request that determine which claims about the user are returned in the ID token and UserInfo response, with the openid scope being mandatory.

What You'll Learn

  • The five standard OIDC scopes and the claims they return
  • How scopes interact with the UserInfo endpoint
  • Custom scopes for provider-specific claims

Why It Matters

Requesting unnecessary scopes makes the consent screen overwhelming and may reduce conversion rates. Requesting too few scopes means missing data requiring additional API calls. Understanding scopes helps you request exactly what you need.

Real-World Use

Doda Browser requests only openid profile email during sign-up. This returns name, avatar, and email — enough to create a profile. It does not request phone or address unless the user explicitly needs those features.

flowchart LR
    AuthReq["Authorization Request\nscope=openid profile email"] --> Provider["OIDC Provider"]
    Provider --> IDToken["ID Token\nsub, name, email, picture"]
    Provider --> UserInfo["UserInfo\nname, email, picture, locale"]
    style AuthReq fill:#dbeafe,stroke:#2563eb

Standard OIDC Scopes

scope_claims = {
    "openid": ["sub"],  # REQUIRED - no other claims without this scope
    "profile": [
        "name", "family_name", "given_name", "middle_name",
        "nickname", "preferred_username", "profile", "picture",
        "website", "gender", "birthdate", "zoneinfo", "locale",
        "updated_at"
    ],
    "email": ["email", "email_verified"],
    "address": ["address"],
    "phone": ["phone_number", "phone_number_verified"],
}

Building the Scope Parameter

import urllib.parse

def build_auth_url(client_id, redirect_uri, scope, provider_auth_endpoint):
    params = {
        "client_id": client_id,
        "response_type": "code",
        "scope": scope,
        "redirect_uri": redirect_uri,
        "state": "random-state-value",
        "nonce": "random-nonce-value",
    }
    return f"{provider_auth_endpoint}?{urllib.parse.urlencode(params)}"

# Examples
minimal_url = build_auth_url(
    client_id="my-app",
    redirect_uri="https://app.com/callback",
    scope="openid",
    provider_auth_endpoint="https://provider.com/auth"
)
print(f"Minimal: {minimal_url}")

profile_url = build_auth_url(
    client_id="my-app",
    redirect_uri="https://app.com/callback",
    scope="openid profile email",
    provider_auth_endpoint="https://provider.com/auth"
)
print(f"Profile: {profile_url}")

Expected output:

Minimal: https://provider.com/auth?client_id=my-app&response_type=code&scope=openid&...
Profile: https://provider.com/auth?client_id=my-app&response_type=code&scope=openid+profile+email&...

Checking Available Claims After Authentication

def check_available_claims(id_token_claims, userinfo, requested_scopes):
    available = {}

    if "profile" in requested_scopes:
        available["name"] = id_token_claims.get("name") or userinfo.get("name")
        available["picture"] = id_token_claims.get("picture") or userinfo.get("picture")

    if "email" in requested_scopes:
        available["email"] = id_token_claims.get("email") or userinfo.get("email")
        available["email_verified"] = id_token_claims.get("email_verified") or userinfo.get("email_verified")

    if "address" in requested_scopes:
        available["address"] = userinfo.get("address")

    if "phone" in requested_scopes:
        available["phone"] = userinfo.get("phone_number")

    return available

Custom Scopes

Providers can define custom scopes:

# Google custom scopes
google_scopes = [
    "https://www.googleapis.com/auth/drive.readonly",  # Google Drive
    "https://www.googleapis.com/auth/calendar",         # Google Calendar
]

# Auth0 custom scopes
auth0_scopes = [
    "read:users",
    "write:users",
    "admin",
]

# Combining OIDC and custom scopes
combined_scope = "openid profile email https://www.googleapis.com/auth/drive.readonly"

Common Mistakes

1. Forgetting the openid Scope

Without openid, the provider treats the request as plain OAuth2. No ID token is returned, and you cannot authenticate the user.

2. Requesting Too Many Scopes

Each scope adds claims to the ID token or UserInfo response. Excessive scopes slow down the token exchange and overwhelm the user's consent screen.

3. Assuming All Providers Support All Scopes

The address and phone scopes are optional. Check scopes_supported in the discovery document before requesting them.

4. Missing Scope for UserInfo Claims

If you request openid without profile, the UserInfo endpoint returns only the sub claim. Request the scopes that cover the claims you need.

5. Not Handling Missing Claims Gracefully

Even with the correct scope, some claims may be missing if the user did not provide that data. Always handle missing claims.

Practice Questions

  1. Why is the openid scope mandatory for OIDC?
  2. What claims does the profile scope provide access to?
  3. How do you request both identity and API access scopes?
  4. Why might a UserInfo response be missing claims even with the correct scope?
  5. How can you check which scopes a provider supports?

Answers:

  1. The openid scope signals to the provider that this is an OIDC request and an ID token should be returned.
  2. profile includes name, given_name, family_name, picture, birthdate, gender, zoneinfo, locale, and other basic profile data.
  3. Combine OIDC scopes (openid profile email) with API scopes (drive.file) in a single space-separated scope parameter.
  4. The user may not have provided the data, or the provider may not support all claims within that scope.
  5. Check scopes_supported in the provider's discovery document at /.well-known/openid-configuration.

Challenge: Design a scope Strategy for a healthcare app that needs: user identity (name, email), medical records access, and calendar scheduling. Minimize the consent screen while meeting all requirements.

FAQ

Can I define my own scopes for my OIDC provider?

: If you run your own provider (e.g., Keycloak, Auth0), yes. Custom scopes can bundle custom claims.

What happens if I request a scope the provider does not support?

: The provider returns an error. Always check scopes_supported in the discovery document.

Is there a limit to the number of scopes I can request?

: No technical limit, but longer scope parameters create longer URLs and more complex consent screens.

Do scopes affect both ID token and UserInfo?

: Yes. Scopes determine which claims appear in the ID token AND which are available from the UserInfo endpoint.

Can I request scopes after authentication?

: No. Scopes are specified in the initial authorization request. To add scopes, re-authenticate the user.

Mini Project

Build a Python function that accepts a list of desired claims and returns the minimal set of OIDC scopes needed. For example, ["name", "email"] should return "openid profile email". Include validation against a provider's scopes_supported.

What's Next

Continue with Authentication Request to build complete OIDC authorization URLs, or explore OIDC Response Types for different token delivery methods.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro