OPENID CONNECT · LAB
Configure lab-collage from the issuer alone, and follow a moved endpoint
Derive every endpoint from one issuer value, check the discovery document the way the lesson requires, and watch a discovered configuration follow an endpoint move that breaks a hard-coded one.
Partly readyUses both lab tenants
The lesson
Builds on: When validation fails.
New to the labs? Start with the lab toolkit and the shared cast and names every lab uses.
Partly ready. Most of this lab runs today. Steps that wait on platform features are marked, and Missing infrastructure says what they need.
- G66 Second lab tenant for every learner: additional tenants need a paid subscription or a BTL grant, so labs that use Lab Mail cannot be completed by an ordinary learner yet
Needs a second tenant. This lab also uses Lab Mail, a second tenant. Additional tenants currently need a paid subscription or a BTL grant, so you may not be able to do the Lab Mail steps yet (gap G66).
Your progress
Press Start before you begin. Only events your tenant records after that count, in the order below. Checking reads your tenant's Audit, so you need Audit read access in it.
Sign in to start this lab and check your progress. Log in or create an account.
Sign in with endpoints taken from discovery
Recorded as
oauth.tokensucceeded forlab-collageabout[email protected].Move the UserInfo endpoint in Metadata Management
Recorded as
tenant.oauth.metadata.updatesucceeded.Call UserInfo at its rediscovered address
Recorded as
oidc.userinfosucceeded (userinfo_served) forlab-collage.Move the UserInfo endpoint back
Recorded as
tenant.oauth.metadata.updatesucceeded.
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
You need
lab-collage, Ava,Lab MailwithISSUER2, andEXPECTED_AUD,ID_ALGSandJWKS_CACHEfrom the Validation labs. Keepbtl-lab callbackrunning.Write your relying party's discovery step. It takes the issuer from your own configuration, fetches the document, refuses it unless it names exactly that issuer and has the required entries, and only then exports the endpoints:
rp_discover() {
local doc; doc=$(curl -sf "${1%/}/.well-known/openid-configuration") || { echo "REJECT: discovery fetch failed"; return 1; }
[ "$(jq -r .issuer <<<"$doc")" = "$1" ] || { echo "REJECT: document issuer does not match $1"; return 1; }
for f in authorization_endpoint token_endpoint jwks_uri; do [ -n "$(jq -r ".$f // empty" <<<"$doc")" ] || { echo "REJECT: missing $f"; return 1; }; done
export EXPECTED_ISSUER="$1" AUTHORIZATION_ENDPOINT=$(jq -r .authorization_endpoint <<<"$doc") TOKEN_ENDPOINT=$(jq -r .token_endpoint <<<"$doc") \
JWKS_URI=$(jq -r .jwks_uri <<<"$doc") USERINFO_ENDPOINT=$(jq -r '.userinfo_endpoint // empty' <<<"$doc") REVOCATION_ENDPOINT=$(jq -r '.revocation_endpoint // empty' <<<"$doc")
echo "discovered $1"
}
Redefine the track's helpers so they use the discovered endpoints instead of fixed paths:
signin_url() { eval "$(btl-lab pkce)"; eval "$(btl-lab state)"; echo "$AUTHORIZATION_ENDPOINT?response_type=code&client_id=$CLIENT_ID&redirect_uri=http%3A%2F%2F127.0.0.1%3A8765%2Fcallback&scope=$1&state=$STATE&nonce=$NONCE&code_challenge=$CHALLENGE&code_challenge_method=S256$2"; }
exchange() { RESP=$(curl -s -u "$CLIENT_ID:$CLIENT_SECRET" "$TOKEN_ENDPOINT" -d grant_type=authorization_code --data-urlencode "code=$1" --data-urlencode "redirect_uri=http://127.0.0.1:8765/callback" --data-urlencode "code_verifier=$VERIFIER"); TOKEN=$(jq -r '.access_token // empty' <<<"$RESP"); ID_TOKEN=$(jq -r '.id_token // empty' <<<"$RESP"); jq '{token_type, expires_in, scope, error}' <<<"$RESP"; }
In Lab Photos, press Start.
Walkthrough
Fetch the document by hand, built from the issuer plus
/.well-known/openid-configuration:
GET$ISSUER/.well-known/openid-configuration
Open in console
GET $ISSUER/.well-known/openid-configuration HTTP/1.1
Accept: application/jsonNote the status, Content-Type and any Cache-Control header, then read the entries this lesson needs: issuer, authorization_endpoint, token_endpoint, userinfo_endpoint, jwks_uri and id_token_signing_alg_values_supported. For a quick summary of the document and the key set together, run btl-lab discover "$ISSUER".
Why it matters: every address you have used in this track is published here, found from one value.
Run
rp_discover "$ISSUER"and print the exported variables. Run a complete sign-in with the redefined helpers (signin_url 'openid%20profile', thenexchange), and validate the token with--issuer "$EXPECTED_ISSUER". Everything works unchanged, now driven by discovery.
One value, three places. Compare
$EXPECTED_ISSUER, the document'sissuerand theissin your new ID token. All three are identical, character for character.
Move an endpoint. In Lab Photos, open Metadata Management and change the UserInfo path from
/oidc/userinfoto/oidc/me. Then call it both ways:
curl -si "$ISSUER/oidc/userinfo" -H "Authorization: Bearer $TOKEN" | head -1
rp_discover "$ISSUER"; curl -s "$USERINFO_ENDPOINT" -H "Authorization: Bearer $TOKEN" | jq .sub
The hard-coded address now gets 404. The rediscovered endpoint returns Ava's sub.
Why it matters: a relying party that copied values from documentation breaks when the provider changes. One that refreshes discovery, checking every refreshed copy the same way, follows the change.
Restore: in Metadata Management, set the UserInfo path back to /oidc/userinfo, then run rp_discover "$ISSUER" again.
Where trust starts. Write your list of configured providers, with short names you chose:
jq -n --arg p "$ISSUER" --arg m "$ISSUER2" '{photos: $p, mail: $m}' > providers.json
Your sign-in button sends photos or mail, and the relying party looks the issuer up here. Nothing in a request, a callback or a token chooses the issuer.
Break it
A document from the wrong address. A setup guide gives you the Lab Mail document's address while your configured issuer is Lab Photos. Check it with your expectation unchanged:
[ "$(curl -s "$ISSUER2/.well-known/openid-configuration" | jq -r .issuer)" = "$ISSUER" ] || echo "REJECT: document names another issuer"
The document is genuine but names a different issuer, so the whole document is discarded.
A trailing slash. Run
rp_discover "$ISSUER/". It builds the right address, because it strips the slash, and then refuses the document:document issuer does not match. An issuer is compared exactly.
Taking the expected issuer from the document. Imagine
rp_discoverhad exported whateverissuerthe fetched document named. Point it, in your head, at the Lab Mail address from Break it 1: the expected issuer, the key set and every endpoint would all be Lab Mail's, and the Lab Mail token you kept in the previous lab would pass every check up to the audience. Write down which line ofrp_discoverprevents that.
Check your work
Press Check my progress in Lab Photos. It looks for, in order: a lab-collage token request using discovered endpoints, tenant.oauth.metadata.update for the move, oidc.userinfo with userinfo_served at the moved address, and tenant.oauth.metadata.update for the restore.
The 404 from the old address does not reach Audit, because no endpoint answered there. In Logs you find the discovery document requests for both tenants as oidc.discovery.
Cleanup
Confirm that the UserInfo path in Lab Photos is /oidc/userinfo, then run rp_discover "$ISSUER" so the exported variables point at Lab Photos. Keep providers.json, rp_discover and the redefined helpers for the next labs.
Missing infrastructure
G66 Second lab tenant: this lab uses Lab Mail, a second tenant. Additional tenants currently need a paid subscription or a BTL grant, so an ordinary learner can do only the Lab Photos steps until every learner can have a second lab tenant.