OIDCStrategy¶
Experimental
New in 0.6: its API may still change in any release, as its use shows what's missing. Scope and stability lists the tools that are settled.
Logins with an OpenID Connect provider (Google, Microsoft Entra ID, Keycloak, Okta, Auth0,
Zitadel…), whose ID token is verified before your code sees its claims:
pip install "fastapi-alcazaba[auth-strategy-oidc]", which installs httpx2 and PyJWT.
In an app: the example. How strategies are tried: Strategies. Every argument: the API reference.
OIDCStrategy is OAuth2Strategy's login (the authorization code flow, PKCE and
a state in a __Host- cookie), plus what OpenID Connect adds: a nonce, the provider's
endpoints and keys from its issuer, and the ID token verified as OpenID Connect Core says.
from fastapi_alcazaba.auth.strategies.oidc import OIDCStrategy
def find_or_create_user(claims: dict[str, Any], userinfo: dict[str, Any] | None) -> User | None:
user_id = f"{claims['iss']}#{claims['sub']}" # unique and stable; an e-mail address isn't
return db.users.get(user_id) or db.users.create(user_id, email=claims.get("email"))
google = auth.use(
OIDCStrategy(
name="google",
issuer="https://accounts.google.com",
client_id=settings.google_client_id,
client_secret=settings.google_client_secret,
redirect_uri="https://example.com/auth/google/callback",
verify=find_or_create_user,
)
)
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
await google.discover() # its endpoints and keys: the app doesn't start without them
yield
- Discovery:
await discover()reads the issuer's/.well-known/openid-configuration, whoseissuermust beissuerexactly: a document of another issuer would have its tokens accepted as this one's. Its endpoints are HTTPS (orhttp://localhost); those given to the strategy (token_url=, a proxy…) win, and withauthorization_url,token_urlandjwks_urlthere's nothing to discover. - The ID token: refused unless it's signed by a key of the provider's JWKS with one of
algorithms(RS256, ES256, PS256, EdDSA; nevernone, nor HS256 with the client secret, which the provider knows too); itsissis the issuer; itsaudhasclient_id, and itsazpis it when there are several audiences or anazp;exphasn't passed andiatisn't in the future (leeway, 60 s); it carries thenonceof this login, kept in the state cookie; itsat_hash, when it has one, is the access token's; and withmax_age=600, itsauth_timeis at most 10 minutes old. A provider that sends no ID token is refused. - The keys: fetched from
jwks_uriand kept forjwks_lifespan(an hour). A token whosekidisn't among them makes them fetched again, the provider having rotated its keys, but at most every 30 seconds: made-upkids don't make the app fetch them on every request. Keys for encryption (use: enc) don't verify signatures. - Userinfo: with
userinfo=True, the userinfo endpoint is read too, for claims the ID token doesn't carry, and refused when itssubisn't the ID token's. - The user:
verify(claims, userinfo)gets the verified claims, and returns your user or None to refuse the login; by default the user is the claims. Key users byissandsub, never by e-mail address, and refuseemail_verified: falsewhen you use the address. - Failures: each check that fails refuses the login with a 401, the reason in the log (at
INFO, by
FastAPIAuth); a provider that doesn't answer is a 502.
OAuth2Strategy doesn't verify the id_token it may get: for an OpenID Connect provider, use
this one.