Enterprise Authentication Integration
Reproducing the OIDC Authorization Code Flow by Hand
Goal
You perform the authorization code flow by hand against a practice OIDC provider, decode and signature-verify an ID token, and write a verification checklist.
Why it matters
Someone who has used OIDC only through a library does not know where to look when an outage occurs.
redirect_uri mismatch, unverified state, missing nonce, reuse of an expired code —
all of these make sense only if you have seen the flow with your own eyes.
In particular, it is important to confirm by hand that an ID token is base64 and anyone can decode it.
If you reproduce yourself that, even if you tamper with the payload, it passes as is without signature verification,
why you must pin alg stays with you in your bones.
Steps
- Start the practice IdP.
python3 /opt/lab/fixtures/auth/oidc/idp.py 9000(in the background) Fetch the discovery document and save it to/root/oidc/discovery.json. (http://127.0.0.1:9000/.well-known/openid-configuration) It must containissuer,authorization_endpoint,token_endpoint, andjwks_uri. - Write one authorization URL line in
/root/oidc/auth-url.txt. Required parameters:response_type=code,client_id=labhub-web,redirect_uri=http://127.0.0.1:9100/callback,scope=openid profile email,state=<16자 이상>,nonce=<16자 이상>(the placeholders mean at least 16 characters). - Call that URL without following the redirect, save the response's
Locationheader to/root/oidc/callback.txt, extract only thecodevalue from it, and save it to/root/oidc/code.txt. Thestatein the Location must equal the value sent in step 2. - Exchange the code at the token endpoint and save the whole response JSON to
/root/oidc/token.json. Sendclient_id=labhub-webandclient_secret=labhub-secretalong with it. It must containaccess_token,id_token, andtoken_type, andtoken_typeisBearer. - Decode the payload of the
id_tokenand save it to/root/oidc/claims.json. It must containiss,aud,sub,exp, andnonce, andaudmust belabhub-webandnoncemust equal the value sent in step 2. - Create
/root/oidc/verify.sh. It takes two arguments (ID토큰 공개키PEM, that is, the ID token and the public key PEM) and ends with exit code 0 if the signature is valid and non-zero otherwise. The public key is in/opt/lab/fixtures/auth/oidc/idp-public.pem. - Call the userinfo endpoint with the
access_tokenand save the result to/root/oidc/userinfo.json. Thesubvalue must equal that in the step 5claims.jsonfile'ssubfield. - Create
/root/oidc/checklist.csv. The first line isitem,risk. The six items서명(signature),iss,aud,exp,nonce, andalgmust be initem, and inriskwrite, in 10 or more characters, the attack or incident that becomes possible if that item is not verified.
Notes
- Not following the redirect:
curl -s -D - -o /dev/null "<URL>", then check theLocation:line - base64url decoding: replace
-with+and_with/, fill in the padding (=), thenbase64 -d - Signature verification: the signed data is the string
<헤더>.<페이로드>(header, dot, payload) and the algorithm is RS256 (SHA-256)openssl dgst -sha256 -verify <공개키> -signature <서명파일> <데이터파일>(the placeholders are the public key, the signature file, and the data file) - Common mistake 1: using the authorization code twice. It is single-use, so the second attempt fails.
- Common mistake 2: passing base64url straight to
base64 -dand getting an error. - Common mistake 3: sending
redirect_uriat token exchange differently from the authorization request. The two values must be exactly the same.
Start the IdP and the discovery document
Start the practice IdP.
python3 /opt/lab/fixtures/auth/oidc/idp.py 9000 (in the background)
Fetch the discovery document and save it to /root/oidc/discovery.json.
(http://127.0.0.1:9000/.well-known/openid-configuration)
It must contain issuer, authorization_endpoint, token_endpoint, and jwks_uri.
An OIDC provider puts its configuration document at a standard path. That one document tells you all the endpoint addresses. Try extracting just the values you need with jq.
Assemble the authorization URL
Write one authorization URL line in /root/oidc/auth-url.txt.
Required parameters: response_type=code, client_id=labhub-web,
redirect_uri=http://127.0.0.1:9100/callback,
scope=openid profile email, state=<16자 이상>, nonce=<16자 이상> (the placeholders mean at least 16 characters).
If you leave out a required parameter, the IdP returns an error. state and nonce have different roles - one is CSRF prevention and the other is token replay prevention.
Receive the authorization code
Call that URL without following the redirect, save the response's Location header to
/root/oidc/callback.txt, extract only the code value from it, and save it to
/root/oidc/code.txt.
The state in the Location must equal the value sent in step 2.
Even without a browser, you can see the code by reading the Location header of the redirect response. You must make curl not follow the redirect.
Token exchange
Exchange the code at the token endpoint and save the whole response JSON to
/root/oidc/token.json.
Send client_id=labhub-web and client_secret=labhub-secret along with it.
It must contain access_token, id_token, and token_type, and token_type is Bearer.
The token endpoint takes a POST in form format. The authorization code is single-use, so using it twice fails. If it fails, redo steps 2–3.
Decode the ID token payload
Decode the payload of the id_token and save it to /root/oidc/claims.json.
It must contain iss, aud, sub, exp, and nonce,
and aud must be labhub-web and nonce must equal the value sent in step 2.
A JWT is three parts separated by dots, each base64url. base64url differs from standard base64 by two characters and may lack padding.
Signature verification script
Create /root/oidc/verify.sh. It takes two arguments (ID토큰 공개키PEM, that is, the ID token and the public key PEM)
and ends with exit code 0 if the signature is valid and non-zero otherwise.
The public key is in /opt/lab/fixtures/auth/oidc/idp-public.pem.
The signed data is the whole string "header.payload". You can do public-key verification with openssl dgst, and the signature value needs base64url decoding.
Call userinfo
Call the userinfo endpoint with the access_token and save the result to
/root/oidc/userinfo.json.
The sub value must equal that in the step 5 claims.json file's sub field.
Send the access token in the Authorization header in Bearer form. Checking that the returned sub equals the ID token's sub is the core of this step.
ID token verification checklist
Create /root/oidc/checklist.csv. The first line is item,risk.
The six items 서명 (signature), iss, aud, exp, nonce, and alg must be in item,
and in risk write, in 10 or more characters, the attack or incident that becomes possible if that item is not verified.
Write not why each item is needed but "what attack becomes possible if you do not verify it." That is what gives you persuasiveness in a later review.