@@ -51,9 +51,9 @@ catches both surfaces without importing the one you do not use.
5151
5252## Hosted Auth
5353
54- Hosted Auth redirects the user to a branded NamoID sign-in page and returns a
55- one-time code. The Client ID resolves the application, its environment, and its
56- Hosted Auth domain, so there is no issuer or application UUID to configure .
54+ Hosted Auth uses standard OpenID Connect Authorization Code flow with S256 PKCE.
55+ The Client ID resolves the application and issuer; discovery supplies the
56+ authorization, token, UserInfo, revocation, JWKS, and logout endpoints .
5757
5858``` python
5959from namoid import NamoIDClient
@@ -63,32 +63,32 @@ namoid = NamoIDClient(
6363 client_secret = os.environ[" NAMOID_CLIENT_SECRET" ], # server-side only
6464)
6565
66- # 1. Start a state-bound transaction and keep the verifier in the user's session .
67- transaction = namoid.create_transaction( )
66+ # 1. Start a state-, nonce-, and PKCE-bound transaction. Keep it server-side .
67+ transaction = namoid.create_oidc_transaction( " https://app.example/auth/callback " )
6868session[" namoid_state" ] = transaction.state
69+ session[" namoid_nonce" ] = transaction.nonce
6970session[" namoid_verifier" ] = transaction.code_verifier
7071
71- # 2. Send the browser to the application's own hosted sign-in page.
72- url = namoid.hosted_auth_url(
73- return_to = " https://app.example/auth/callback" ,
74- state = transaction.state,
75- completion_mode = " confidential" ,
76- code_challenge = transaction.code_challenge,
77- )
72+ # 2. Send the browser to the discovered authorization endpoint.
73+ url = namoid.authorization_url(transaction)
7874
79- # 3. On the callback, compare state, then exchange the code on the server .
75+ # 3. On the callback, compare state, then exchange using the same redirect URI .
8076tokens = namoid.exchange_code(
8177 code = request.args[" code" ],
8278 code_verifier = session.pop(" namoid_verifier" ),
79+ redirect_uri = " https://app.example/auth/callback" ,
8380)
8481
85- # 4. Confirm the token and create your own application session.
86- result = namoid.validate_access_token(tokens.access_token)
87- if not result.valid:
88- raise Unauthorized()
82+ # 4. Verify the ID token signature and callback-bound nonce, then fetch UserInfo.
83+ claims = namoid.validate_id_token(tokens.raw[" id_token" ],
84+ nonce = session.pop(" namoid_nonce" ))
85+ user = namoid.user_info(tokens.access_token)
86+ assert claims[" sub" ] == user[" sub" ]
8987
90- # 5. On sign-out, revoke the NamoID session too.
91- namoid.revoke_session(access_token = tokens.access_token, refresh_token = tokens.refresh_token)
88+ # 5. On sign-out, revoke the refresh token and redirect through provider logout.
89+ namoid.revoke_token(tokens.refresh_token, token_type_hint = " refresh_token" )
90+ url = namoid.logout_url(id_token_hint = tokens.raw[" id_token" ],
91+ post_logout_redirect_uri = " https://app.example/signed-out" )
9292```
9393
9494` AsyncNamoIDClient ` has exactly the same methods with ` await ` , for FastAPI,
@@ -98,25 +98,36 @@ Starlette, or any async framework:
9898from namoid import AsyncNamoIDClient
9999
100100async with AsyncNamoIDClient(client_id = ... , client_secret = ... ) as namoid:
101- tokens = await namoid.exchange_code(code = code, code_verifier = verifier)
101+ tokens = await namoid.exchange_code(
102+ code = code, code_verifier = verifier,
103+ redirect_uri = " https://app.example/auth/callback" ,
104+ )
102105```
103106
104107Both accept an ` http_client ` if you want to supply your own configured
105108` httpx.Client ` / ` httpx.AsyncClient ` , and cache the auth config after the first
106109fetch.
107110
108- For a browser-only public client, redirect with ` completion_mode="public" ` and
109- exchange with ` confidential=False ` — PKCE protects the flow and no secret is
110- involved. Never put a Client Secret anywhere a browser can reach.
111+ For a public client, omit ` client_secret ` ; PKCE protects the code exchange. For
112+ a confidential web application, the SDK sends the secret using HTTP Basic
113+ authentication at the discovered token endpoint. Never put a Client Secret
114+ anywhere a browser can reach.
111115
112116| Method | Endpoint |
113117| ---| ---|
114118| ` get_auth_config() ` | ` GET /v1/auth/config ` |
115- | ` hosted_auth_url(...) ` | builds the URL, no request |
116- | ` exchange_code(...) ` | ` POST /v1/auth/hosted/exchange ` |
117- | ` refresh(...) ` | ` POST /v1/auth/refresh ` |
119+ | ` get_oidc_discovery() ` | issuer ` /.well-known/openid-configuration ` |
120+ | ` authorization_url(...) ` | discovered authorization endpoint |
121+ | ` exchange_code(...) ` | discovered token endpoint |
122+ | ` refresh(...) ` | discovered token endpoint |
123+ | ` user_info(...) ` | discovered UserInfo endpoint |
124+ | ` validate_id_token(...) ` | discovered JWKS endpoint; local verification |
125+ | ` revoke_token(...) ` | discovered revocation endpoint |
126+ | ` logout_url(...) ` | discovered end-session endpoint |
118127| ` validate_access_token(...) ` | ` POST /v1/auth/tokens/validate ` |
119- | ` revoke_session(...) ` | ` POST /v1/auth/logout ` |
128+
129+ The older ` hosted_auth_url(...) ` and ` revoke_session(...) ` helpers remain for
130+ applications using NamoID's legacy Hosted Auth contract.
120131
121132Every failure raises ` NamoIDError ` , carrying ` status ` , ` code ` (the API's own
122133error code when present), and the parsed ` detail ` .
0 commit comments