OAUTH 2.0 · LAB
Ask for a signed response in each JARM response mode
Confirm JARM support from metadata, request query.jwt and form_post.jwt responses for lab-printer, and read each JWT's header and claims, including the algorithm the client agreed.
PlannedUses your lab tenant
The lesson
Builds on: Protecting an authorization response.
New to the labs? Start with the lab toolkit and the shared cast and names every lab uses.
Planned. The core of this lab waits on platform features that are not built yet. The planned walkthrough shows exactly how it will run; Do today is a real exercise you can do now.
- G13 JARM
Request console
Requests in this lab can be sent from this page to your tenant: open one and choose Send. Fill in the values below first. They stay in this page's memory and are gone when you leave; secrets are never stored or sent anywhere except the request you send.
Setup
Use
lab-printer, the shell variables and the two listeners from Read a plain authorization response, then plan a signed one:btl-lab callbackfor query responses andnc -l 8765for form posts.Planned (G13): allow the JWT response modes as in that lab, but leave
lab-printer's authorization response signing algorithm empty at first.
Planned walkthrough
Confirm support before relying on it.
GET$ISSUER/.well-known/oauth-authorization-server
Open in console
GET $ISSUER/.well-known/oauth-authorization-serverExpect the *.jwt modes in response_modes_supported and authorization_signing_alg_values_supported listing ES256 and RS256.
Why it matters: "Asking for a signed response". The client checks the server's metadata and agrees an algorithm before it changes its requests.
With no algorithm set on the client, run a code flow with
&response_mode=jwtand decode the response withbtl-lab decode "$RESPONSE". The header says"alg":"RS256". Now setlab-printer's algorithm toES256and repeat: the header says"alg":"ES256"with thekidof the tenant's ES256 key.
Why it matters: the lesson's warning about JARM's RS256 default is a real per-client setting, and an unset value can break interoperability for reasons unrelated to security.
Note where the
jwtresponse arrived: as?response=...in the query string, with nocode,stateorissbeside it.
Why it matters: "Choosing a response mode". For the code flow, jwt means query.jwt.
Repeat with
&response_mode=form_post.jwtwhilenc -l 8765listens. The POST body holdsresponse=eyJ...and nothing else.
Why it matters: the JWT stays out of the address bar, history and the Referer header. Your pending-attempt cookie must survive a cross-site POST, or the client will not find it at its callback.
Read the claims of both responses:
iss,aud(your client ID),exp,code,state. Compareaudwith theaudof the request object claims from the JAR labs, which named the issuer.
Why it matters: "Reading the response". Each signed message names the party that should accept it.
Do today
Read the metadata request above against your tenant.
response_modes_supportedlists onlyquery,fragmentandform_post, and there is noauthorization_signing_alg_values_supported.List the keys a JARM client would verify with.
curl -s "$ISSUER/oauth/jwks" | jq '.keys[] | {kid, alg, kty}'
A new tenant shows an ES256 key for access tokens and an RS256 key for ID tokens. Write down which key a client with no algorithm setting would need, and what goes wrong if the server only signs with the other.
Run a
form_postflow withnc -l 8765listening and the browser's developer tools open on the Network tab. The tenant's answer to the consent step is a small HTML page with one form that submits itself, andncshows its POST body with three fields:code,stateandiss. Aform_post.jwtpage has the same shape with one field,response.Ask for a JARM mode by name.
curl -s -o /dev/null -w '%{redirect_url}\n' "$ISSUER/oauth/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=$(urlenc $REDIRECT)&scope=openid&state=jarm-2&response_mode=query.jwt&code_challenge=$CHALLENGE&code_challenge_method=S256"
The redirect carries error=invalid_request and the description "response_mode must be query, fragment or form_post." Audit shows oauth.authorize rejected unsupported_response_mode.
See the tenant apply the rule behind the lesson's warning about
query.jwt: tokens from the authorization endpoint never travel in a query string. This needs a response type that returns a token, so widen the tenant briefly. In OAuth > Flow policy, allow theimplicitgrant and the response typecode id_token; onlab-printer, allowcode id_tokentoo.
btl-lab state # export STATE and NONCE
curl -s -o /dev/null -w '%{redirect_url}\n' "$ISSUER/oauth/authorize?response_type=$(urlenc 'code id_token')&client_id=$CLIENT_ID&redirect_uri=$(urlenc $REDIRECT)&scope=openid&state=$STATE&nonce=$NONCE&response_mode=query&code_challenge=$CHALLENGE&code_challenge_method=S256"
The tenant refuses before any sign-in, and the error comes back in the fragment, not the query: unsupported_response_mode again.
Restore: remove code id_token from lab-printer, and remove code id_token and the implicit grant from the Flow policy.
Break it
Once G13 exists, allow code id_token again and request response_mode=query.jwt. The tenant refuses with invalid_request, because a response that carries an ID token may not travel in a query unless it is encrypted.
Restore: remove code id_token and implicit again afterwards.
Check your work
Today, Audit shows two oauth.authorize rejections with unsupported_response_mode for lab-printer, and tenant.oauth.policy.update and tenant.oauth.clients.update for the widening and its restore. Once G13 exists, Audit also shows oauth.authorize code_issued with a JWT response mode, and tenant.oauth.clients.update for the algorithm change.
Cleanup
Confirm the Flow policy no longer allows
implicitorcode id_token, andlab-printerno longer allowscode id_token.Stop
ncand the listener.
Missing infrastructure
G13: JARM response modes, a per-client
authorization_signed_response_algwith the RS256 default, signing with the tenant's existing keys, the self-submitting page forform_post.jwt, andauthorization_signing_alg_values_supportedin discovery. Once these exist, the Planned walkthrough runs as written.