Authorization Code with PKCE
Web Applications (Server) | Single Page Applications | Mobile Applications |
---|---|---|
Recommended | Mandatory | Mandatory |
Authorization Code with PKCE is the recommended flow for Single Sign-On with Eduplaces on all platforms. It is an extension of the Authorization Code Flow with an additional security mechanism to prevent authorization code interception attacks. Initially, this flow was designed for native applications, but it is also suitable for web applications and single page applications. OAuth 2.1 recommends to use this flow for all applications that are capable of using it.
The Authorization Code with PKCE flow is very similar to the Authorization Code Flow. The only difference is that you have to generate a code verifier and a code challenge before you start the authentication flow. You will then include the code challenge in the authentication request you send to our authorization server. At the end of the authentication flow, you will have to send the code verifier to our token endpoint to receive an access token. This way, we can verify that you are the one who started the authentication flow and not an attacker.
Overview
The Authorization Code with PKCE flow consists of five major steps:
- The user clicks on your Tile in the Eduplaces platform, opening your Login URL in a new tab or the user clicks on a "Login with Eduplaces" button in your application.
- The user is redirected to our authorization server to authenticate and authorize your application.
- The user is redirected back to your application with an authorization code.
- Your application exchanges the authorization code for an ID and access token.
- Your application uses the ID token to identify the user and the access token to make API requests on behalf of the user.
The following diagram shows the flow in detail:
Step 1: Start an Authentication
Authentication is always started by an user interaction. This could be:
- The user clicking on your Tile in the Eduplaces platform, opening your Login URL in a new tab.
- The user clicking on a "Login with Eduplaces" button on your applications login page.
Step 2: Request User Authorization
Your application redirects the user to our authorization server to authenticate and authorize your application.
This is done by sending an authentication request to our authorization endpoint.
The GET
request can include the following query parameters:
Query Parameter | Value |
---|---|
response_type | required Always set to code to indicate the flow to be used. |
client_id | required The client ID of your application. |
redirect_uri | required The URL we will redirect the user back to after they authorized your application. This must exactly match one of your pre-registered allowed redirect URIs for your client. |
code_challenge | required A code challenge derived from the code verifier. See Code Challenge for additional information. |
code_challenge_method | required The method used to derive the code challenge. Always set to S256 . |
scope | required A space-separated list of scopes your application is requesting access to. You have to include openid to receive an ID token. |
state | strongly recommended A random string generated by your application. This will be included in the response from our authorization server and can be used to verify the response. We provide some tips below. |
nonce | strongly recommended A random string generated by your application. This will be included in the ID token and can be used to verify the token. |
login_hint | optional or required A string telling our authorization server to make sure a specific user is logged in. This is only required if the flow was started via your Login URL and we included a login_hint parameter. |
prompt | optional A space-separated list of strings that define whether the user should be prompted for reauthentication and consent. Values are none , login , consent . See prompt Parameter for more information. |
id_token_hint | optional A previously issued ID token. If the user identified by the ID Token is logged in or is logged in by the request, then the Authorization Server returns a positive response; otherwise, it returns an error. This could be used in combination with prompt=none to check if the user is still logged in. |
max_age | optional Maximum Authentication Age. Specifies the allowable elapsed time in seconds since the last time the End-User was actively authenticated by Eduplaces. If the elapsed time is greater than this value, we will reauthenticate the user. Usage is discouraged! |
HTTP/1.1 302 Found
Location: https://auth.eduplaces.io/oauth2/auth
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://your.awesome-education.app/sso/callback
&code_challenge=YOUR_CODE_CHALLENGE
&code_challenge_method=S256
&scope=openid school profile
&state=YOUR_STATE
&nonce=YOUR_NONCE
The user will be redirected to our authorization server where they can log in and authorize your application. If the user is already logged in, they will be asked to authorize your application right away. We remember the user's decision and will not ask them again for the same application.
Step 3: Redirect back to your Application
After the user authorized your application, they will be redirected back to your application with an authorization code. The authorization code is a short-lived, one-time code that can be exchanged for an ID and access token.
To which URI we redirect the user depends on the redirect_uri
parameter you included in the authentication request.
Just keep in mind that the redirect URI must exactly match one of your pre-registered allowed redirect URIs for your client.
We will also redirect the user to the redirect_uri
if they denied your application access to their data or if they canceled the authentication process.
If the user authorized your application, we will redirect them back to your application with an authorization code and the state you included in the authentication request.
The GET
request will include the following query parameters:
Query Parameter | Value |
---|---|
code | The authorization code your application can use to obtain an ID and access token. |
state | The state you included in the authentication request. |
GET https://your.awesome-education.app/sso/callback
?code=ory_ac_MLN1fCSPt1v85onDiZM5FbBhXq2SVFPBQITrGYk4dr8.766I7lY1jKYKadkBikQjcugyt5ki8lDT3u8-JVei-Sc
&state=YOUR_STATE
Error Response
If the user denied your application access to their data or if they canceled the authentication process, we will redirect them back to your application with an error.
The GET
request will include the following query parameters:
Query Parameter | Value |
---|---|
error | The error code. |
error_description | A human-readable text providing additional information. |
state | The state you included in the authentication request. |
GET https://your.awesome-education.app/sso/callback
?error=access_denied
&error_description=The+resource+owner+or+authorization+server+denied+the+request.
&state=YOUR_STATE
Refer to the OpenID Connect specification for a list of error codes.
The error_description
is optional and might not be included in the response.
We will try to include it if possible, but you should not rely on its presence.
state
parameter is only included in the response if you included it in the authentication request.
You should always verify that the state
parameter in the response matches the state
parameter you included in the authentication request.Step 4: Exchange Authorization Code for Tokens
The user is now back on your application and you can exchange the authorization code for an ID and access token.
To do so, you have to send a POST
request to our token endpoint.
Confidential clients like server-side web applications have to authenticate themselves by including their client ID and secret in the Authorization
header.
To exchange the authorization code for an ID and access token, you have to make a POST
request to our token endpoint.
The POST
request must include the following parameters in the body:
Request Body Parameter | Value |
---|---|
grant_type | required Always set to authorization_code to indicate you are exchanging an authorization code for an ID and access token. |
client_id | required for public clients The client ID of your application. Confidential clients have to authenticate themselves by including their client ID and secret in the Authorization header (see below). |
code | required The authorization code you received in the previous step. |
code_verifier | required The code verifier you used to generate the code challenge. See Code Challenge for additional information. |
redirect_uri | required The Redirect URI used while requesting the authorization code. |
In addition to the request body parameters, you have to set the following headers:
Header | Value |
---|---|
Content-Type | required Set to application/x-www-form-urlencoded . This will most likely be set automatically by your HTTP client. |
Authorization | required for confidential clients Set to Basic <base64(client_id:client_secret)> . Never include the client secret in a request from a public client like a single page application or a mobile application. |
POST /oauth2/token HTTP/1.1
Host: auth.eduplaces.io
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=ory_ac_MLN1fCSPt1v85onDiZM5FbBhXq2SVFPBQITrGYk4dr8.766I7lY1jKYKadkBikQjcugyt5ki8lDT3u8-JVei-Sc
&code_verifier=YOUR_CODE_VERIFIER
&redirect_uri=https://your.awesome-education.app/sso/callback
If the request was successful, we will respond with a 200 OK
status code and a JSON body containing the following parameters:
Key | Value |
---|---|
access_token | The access token your application can use to make API requests on behalf of the user. |
token_type | The type of the access token. Always set to bearer . Note: It should be Bearer but unfortunately, our OAuth 2.0 server returns it in lowercase. Make sure to be able to handle both as this might change. |
expires_in | The remaining lifetime of the access token in seconds. |
scope | The scopes the user authorized your application to access. |
id_token | The ID token your application can use to identify the user. Requires the openid scope. |
refresh_token | The refresh token your application can use to obtain a new access token when the current one expired and the user is not present to re-authorize your application. Requires the offline_access scope. Learn more about Refresh Tokens. |
HTTP/1.1 200
Content-Type: application/json
{
"access_token": "ory_at_FYfL9jJTZO15HI6EQCO-0uZg8W_KMTJHunpmXTLp8lg.uGGOCQEGOlunY9mdsWrZA3uGT03JYWdTTj8iar5MIvs",
"token_type": "bearer"
"expires_in": 3599,
"scope": "openid offline_access",
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg4MzIwZjlmLTBhZjQtNDFjMC1iMzAwLTczMGFmZmRjYjVhMCIsInR5cCI6IkpXVCJ9.eyJhdF9oYXNoIjoiSXdDQmFTc0tEZTI3R1JQQS12NG1JUSIsImF1ZCI6WyJkYzYxZjk5ZC1hZGZlLTQ1OGEtODZiZi0zYTMxNmYyYTc4OWMiXSwiYXV0aF90aW1lIjoxNjkzMzAyNzAwLCJleHAiOjE2OTMzMDYzMTQsImlhdCI6MTY5MzMwMjcxNCwiaXNzIjoiaHR0cHM6Ly9zaWxseS1lbGlvbi13ZWN4dHhtY2c1LnByb2plY3RzLm9yeWFwaXMuY29tIiwianRpIjoiMWMyZDUzMmQtMWUzNi00ZmFiLTkwMjgtMjc1MDcwM2FiMTczIiwicmF0IjoxNjkzMzAyNjkzLCJzaWQiOiIzM2RiOWU3Ny1iNGRiLTQ2M2YtOTc4My1kZmY3NmU5NDQxNDciLCJzdWIiOiI3NGQ3YjYyNS01YzA4LTRjYmItOWQwZC1lZWQxZTgzNjQ2YzMifQ.EPWFwVioQZYxmaZVy_3drs2-3jr2DIszkrI9TZpz6dJluDPQ6F6M2Kq7q-zKthnHM7rWvb-bfBJh6Olo1UF6TG0F93WXQIdI7HgIhtQH5a9Cfwbv-h8GQieVc2cQmxzqyiqIxa4e0Z3VoLvsqCE76JVfdo0tmI1SfnauYx3BSxlu6LeVzYWWclQU9fJg1pfurvw6hzVdtJTjdyUvh45bNMAb_QWFDMhkF36yMX7CqzVd8eHX5jnMuk2e1WDLKS6qZxJ3oGux_VDqKg9Qu0x8rb1e4WPAJJ35n2AcqmsO0ex14yxUfhCZDRM31MMC11MfIH1wiTrZKTF9EqZ0yys_vZoQ8DBoHvnDA7cjYZ-0FXO1cirDflzo0jMxihwFaES-_E1pAr4vkiInIigSc4YmTMfOyTttlT-U31zlOh5tiaLhzLO9ImmyLenj9CGCi_GYPqoLf0JTbeSKA9J7DkrO-ed_xMYn-fHt1oqukJmEVg7KJz3Ey5G4h_mL8wMR2Vj-wopFC01RIYpo3a-KjEPdFUktyNB678-iY-n0JuEdDLJ743RfWsR4zQOKpfy3XMK3jIfcE_sAg5Yz3rgUWtVdZ_NSy1rvOCzbJ3-PoxzGNS1qbp6Ma0aK-FU_ZHyUnLRoRa5O6q1vwLe-DX5nfsfIX9S2FapS6nTC_hPITyq9YRI",
"refresh_token": "ory_rt_XfPjrVDSXtVXK-WA9rbjAp5g7ZkAG-hXR76nN38VIAM.disRRBbHn71r2JfpLKuiq3F5EyJPKg7dmWQZdsFm-rw",
}
Error Response
If the request was not successful, we will respond with an error.
The authorization server will respond with a 400 Bad Request
status code and a JSON body containing the following parameters:
Key | Value |
---|---|
error | The error code. |
error_description | A human-readable text providing additional information. |
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "invalid_request",
"error_description": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed."
}
Refer to the OAuth 2.0 specification for a list of error codes.
The error_description
is optional and might not be included in the response. We will try to include it if possible, but you should not rely on its presence.
Step 5: Use the ID Token to Identify the User
To Authenticate the user, you have to verify the ID token. The ID token is a JSON Web Token (JWT) that contains information about the user and is signed by Eduplaces. Part of the information is the user's unique ID, which you can use to identify the user in your application. The ID token can also contain information about the user's school and role. Head over to our Scopes section for more information on the available additional claims.
Validating the ID token is a two-step process:
- Verify the signature of the ID token to ensure it was signed by Eduplaces.
- Verify the claims in the ID token to ensure the token is valid.
How to verify the signature is out of scope for this documentation. We recommend using one of the many open source libraries available that can help you with the ID token validation. In our Authenticate Users Guide, we provide a full example you can use as a reference.
On our Endpoints page, you can find the URLs containing the public keys you can use to verify the signature of the ID token. Common libraries will be able to automatically fetch the public keys automatically by using the OpenID Connect Discovery endpoint we provide.
The ID token contains a JSON object with at least the following claims:
Claim | Description |
---|---|
iss | The issuer identifier for the issuer of the response. Always set to https://auth.eduplaces.io . |
aud | Array containing the client ID of your application. |
sub | The unique identifier for the user. |
nonce | The nonce you included in the authentication request. |
jti | A unique identifier for the token. |
sid | The Eduplaces session ID. |
at_hash | The access token hash. |
auth_time | The time the user authenticated. |
rat | The time the ID token was requested. |
iat | The time the ID token was issued. |
exp | The expiration time of the ID token. |
If you requested additional scopes, the ID token can contain additional claims. Head over to our Scopes section for more information on the available additional claims.
{
"iss": "https://auth.eduplaces.io",
"aud": [
"YOUR_CLIENT_ID"
],
"sub": "f80763f1-96da-4071-b29d-1f15d83ca8b6",
"nonce": "YOUR_NONCE",
"jti": "30e58376-ed53-4c5b-91c1-9fd5517e7f55",
"sid": "7ddc71e6-e932-43e5-9d86-2ada68a490af",
"at_hash": "cw8qL0ENYRYrslktjHXEtw",
"auth_time": 1692706900,
"rat": 1692706896,
"iat": 1692706914,
"exp": 1692710514
}
The most important claim when authenticating a user is the sub
claim.
It contains the unique identifier for the user.
You can use this identifier to identify the user in your application and log them in automatically.
Just like you would with a username and password.
If you receive an ID token with a sub
claim you don't recognize, you should create a new user account on-the-fly in your database and associate it with the user's Eduplaces ID.
The next time the user logs in to your application with Eduplaces, you can use the ID token to identify the user and log them in automatically.
Additional Information
Code Challenge
The code challenge is a security measure to prevent authorization code interception attacks. This is especially important for native applications, but it is also recommended for web applications and single page applications. It guarantees that an authorization code can only be exchanged by your application and not by an attacker who intercepted the authorization code and knows your (public) client ID.
The code challenge is derived from a code verifier you generate before you start the authentication flow. The code verifier is a cryptographically random string with a minimum length of 43 characters and a maximum length of 128 characters.
The code challenge is derived from the code verifier by using a hash function.
The hash function is specified by the code_challenge_method
parameter you include in the authentication request.
The only supported method is S256
, which is the SHA-256 hash function.
When requesting an authorization code, you have to include the code challenge and code challenge method in the authentication request. It is stored in our authorization server along with the authorization code. When exchanging the authorization code for an ID and access token, you have to include the code verifier in the token request. Only if we can derive the same code challenge from the code verifier, we will issue an ID and access token.
As it is not possible to reverse the hash function and to derive the code verifier from the code challenge, an attacker who intercepted the authorization code will not be able to send a valid code verifier with the token request. Therefore, the attacker will not be able to exchange the stolen authorization code for an ID and access token.
state
Parameter
The state
parameter is a random string generated by your application.
It is included in the response from our authorization server and can be used to verify the response.
You should always verify that the state
parameter in the response matches the state
parameter you included in the authentication request.
It is strongly recommended to include the state
parameter in the authentication request as it can prevent CSRF attacks.
Some documentation you might find on the internet recommends to use the state
parameter to store the user's session.
This is not recommended as it can lead to session fixation attacks.
Instead, you should use a session cookie to store the user's session and store the state
parameter in the session.
This is how we recommend to use the state
parameter:
- Create a random string when the user starts the authentication flow.
- Store the random string in a protected cookie (
secure
andhttpOnly
) for server-side web applications, in Local Storage for single page applications, or in a protected key-value store for mobile applications. - Include the random string as
state
in the authentication request. - Verify that the
state
parameter in the response matches the random string you stored in the cookie, Local Storage, or key-value store. - Delete the random string from the cookie, Local Storage, or key-value store.
As the state is stored in a cookie, Local Storage, or key-value store on the user's device, it is not possible to resume a flow in another browser or device by knowing the state. This is a good thing as it prevents session fixation attacks.
prompt
Parameter
The prompt
parameter is a space-separated list of strings that define whether the user should be prompted for reauthentication and consent.
There is no need to include the prompt
parameter if you only want to authenticate the user.
Only include it if you have a specific use case for it.
The following values are supported:
none
: The user should not be prompted for reauthentication or consent. If the user is not logged in or if they are logged in but haven't authorized your application yet, we will respond with an error.login
: The user should be prompted for reauthentication even if they are currently logged in.consent
: The user should be prompted for consent.
The prompt
parameter is useful in combination with the id_token_hint
parameter if you want to make sure the user is still logged in on Eduplaces.
This could be the case if the user is logged in to your application but hasn't used it for a while.
In this case, you could include the prompt=none
parameter in the authentication request and the id_token_hint
parameter with the ID token you received the last time the user logged in even if it is expired.
This concept is also known as Silent Authentication as the user is not prompted for reauthentication or consent.