Configuring OIDC Authorization code flow with PKCE
This tutorial describes how to configure OIDC Authorization code flow with PKCE in CAS access manager.
What is PKCE
PKCE stands for Proof of Key for Code Exchange and it is basically used for native mobile or desktop applications and/or single-page JavaScript web applications. The point of using PKCE is that no client secret needs to be placed in the application.
In the context of PKCE and client-side only applications, the Cross-Origin Resource Sharing (CORS) must usually be configured for everything to work properly.
PKCE Implementation
Building on top of the OIDC Authorization code flow, there are three new parameters used by PKCE:
-
The
code_verifier
is a cryptographically random string generated by your application. This dynamically created string is used to correlate the final access token request with the initial authorization request. -
The
code_challenge
is derived from thecode_verifier
using one of the two possible transformations:plain
andS256
. -
The
code_challenge_method
tells the server which function was used to transform thecode_verifier
(plain or S256). It defaults toplain
if not explicitly set.
See RFC 7636 for complete specifications.
CAS OIDC service with PKCE
{ "@class" : "org.apereo.cas.services.OidcRegisteredService", "clientId": "vueappjs", "serviceId" : "^http://testjs.example.com:8080/auth/signinwin/main", "name": "vueapp", "id": 202, "scopes" : [ "java.util.HashSet", [ "openid" ]], "supportedGrantTypes": [ "java.util.HashSet", [ "AUTHORIZATION_CODE", "REFRESH_TOKEN" ] ], "supportedResponseTypes": [ "java.util.HashSet", [ "CODE", "TOKEN" ] ], "attributeReleasePolicy" : { "@class" : "org.apereo.cas.services.ReturnAllAttributeReleasePolicy" } }
As you can see, clientSecret
is missing from the service definition.
CAS does not support both clientSecret and PKCE on the OIDC service at the same time.
|
Testing
-
First, generate
code_verifier
andcode_challenge
#!/bin/bash random=$(xxd -c 32 -u -l 32 -p /dev/urandom) code_verifier=$(echo -n $random | xxd -r -p | base64 | tr '+' '-' | tr '/' '_' | tr -d '=') code_challenge=$(echo -n $code_verifier | openssl dgst -binary -sha256 | base64 | tr '+' '-' | tr '/' '_' | tr -d '=') echo $code_verifier echo $code_challenge
-
Paste following line into your browser. Adjust values according to your deployment and do not forget to include generated
code_challenge
.https://iam-appliance.tld/cas/oidc/authorize?response_type=code&code_challenge=Whubzdv9zyTyeqdpEpouWE1QVQ0tGlMpbn3eJpTuHog&code_challenge_method=S256&client_id=vueappjs&redirect_uri=http://testjs.example.com:8080/auth/signinwin/main&scope=openid&state=789456
-
If you are not already logged in, CAS displays a login screen. Log in with your test user.
-
After successful login, CAS issues the
code
and redirects you to the SP. The URL will look like this:http://testjs.example.com:8080/auth/signinwin/main?code=OC-1-151iQqGjQW3em0UWQ-Zvp9iChhxIc&state=789456
-
Now, copy the value of
code
parameter. Validity ofcode
is time-limited to about 30 seconds so you have to be quick. -
And paste the
code
andcode_verifier
into the token request…curl -k -XPOST -H 'Origin: http://testjs.example.com:8080' --data "code_verifier=JkMRw1qO7jjucdmuQGTdsmDEGivltSJU4qs01GFa4aU&grant_type=authorization_code&redirect_uri=http://testjs.example.com:8080/auth/signinwin/main&client_id=vueappjs&scope=openid&code=OC-1-eRHH8zUs-qesawmcDECSZEEmf0sl6drQ" "https://iam-appliance.tld/cas/oidc/token"
-
…and obtain the
access token
and OIDCid_token
.{ "access_token":"AT-1-j3EPvr3jfHIVXEPnn4M8L-vMzg5L", "id_token":"eyJhbG... abbreviated ...8x8nAs6CaojdCw1YkFTv3qQu5VYCos_kn_f8uoHzCVkA", "token_type":"bearer", "expires_in":28800, "scope":"openid" }
-
Finally, use the
access_token
to obtain user profile information. The response will contain attributes from all scopes the access token was issued for.curl -k -XGET --header "Authorization: Bearer AT-1-j3EPvr3jfHIVXEPnn4M8L-vMzg5L" https://iam.appliance.tld/cas/oidc/profile
-
User profile is returned.
{ "sub":"idmtestuser", "service":"http://testjs.example.com:8080", "auth_time":1670487660, "id":"idmtestuser", "client_id":"vueappjs", "attributes":{ ... user attributes listed here ... } }