Skip to main content

API authentication

Forge ID exposes a REST API at https://cri.epita.fr/api/v2/. The list of endpoints, their parameters and their responses are documented in the Swagger UI (or the ReDoc version).

This page explains how to authenticate your application against that API with an OIDC access token (Authorization: Bearer ...) instead of sending a login and a password on every request.

Bearer tokens will soon be the only supported method

For security reasons, authenticating on the API with a login and a password (HTTP Basic authentication) is being phased out and will be removed. Access tokens, as described on this page, will then be the only way to authenticate on the API.

If one of your applications still sends credentials on every request, please migrate it now. We will announce the removal date in advance, and you can contact us if you need help planning the migration.

Why Bearer tokens?

Using a token means your application never stores nor transmits a user password. Tokens are short-lived, limited to the scopes you were granted, and they keep working if the user signs in with Office365 or any other authentication method we may require in the future (such as a second factor). Authentication methods based on a password cannot offer any of these guarantees.

Overview​

The API accepts an access token issued by the Forge ID OIDC provider. Getting one follows the regular authorization code flow described in the OIDC page: your application sends the user to Forge ID, the user signs in and approves your application, and you exchange the resulting code for a token.

Your application never sees the user credentials: everything happens on https://cri.epita.fr, which is also why this flow keeps working whatever the sign-in method of the user is.

Requirements​

An access token is accepted by the API when all of the following are true:

  • it was issued by https://cri.epita.fr;
  • it has not expired (access tokens are valid for one hour);
  • the account it belongs to is still active;
  • it includes the api scope.
The api scope is not granted by default

The api scope is what allows a client to query the API on behalf of a user. It must be enabled on your client, so please mention it (and what you intend to do with it) when you request a client. The public test client can request it during your development phase.

Endpoints​

PurposeURL
Discoveryhttps://cri.epita.fr/.well-known/openid-configuration
Authorizationhttps://cri.epita.fr/authorize
Tokenhttps://cri.epita.fr/token
User infohttps://cri.epita.fr/userinfo
APIhttps://cri.epita.fr/api/v2/

Whenever possible, read these from the discovery document rather than hardcoding them.

Getting a token​

1. Send the user to the authorization endpoint​

https://cri.epita.fr/authorize
?client_id=<CLIENT_ID>
&response_type=code
&scope=openid%20api
&redirect_uri=<REDIRECT_URI>
&state=<RANDOM_STATE>

Add any other scope your application needs (profile, email, ...) to the scope parameter. The redirect_uri must be one of the URIs registered on your client.

The user signs in and is asked to approve your application. Forge ID then redirects to your redirect_uri with a code parameter. Always check that the state you receive is the one you sent.

2. Exchange the code for a token​

curl https://cri.epita.fr/token \
-u "<CLIENT_ID>:<CLIENT_SECRET>" \
-d grant_type=authorization_code \
-d code="<CODE>" \
-d redirect_uri="<REDIRECT_URI>"
{
"access_token": "a1b2c3...",
"refresh_token": "d4e5f6...",
"token_type": "bearer",
"expires_in": 3600,
"id_token": "eyJhbGciOi...",
"scope": "openid api"
}

3. Call the API​

curl https://cri.epita.fr/api/v2/users/ \
-H "Authorization: Bearer a1b2c3..."

In Python:

import requests

response = requests.get(
"https://cri.epita.fr/api/v2/users/",
headers={"Authorization": f"Bearer {access_token}"},
timeout=10,
)
response.raise_for_status()
print(response.json())

4. Renew the token​

Access tokens expire after one hour. Use the refresh token you received to get a new one, without involving the user again:

curl https://cri.epita.fr/token \
-u "<CLIENT_ID>:<CLIENT_SECRET>" \
-d grant_type=refresh_token \
-d refresh_token="<REFRESH_TOKEN>"

Each refresh returns a new access token and a new refresh token: store the new refresh token, the previous one is no longer usable.

caution

A refresh token grants access to the user data for as long as it is valid. Store it like you would store a password: encrypted at rest, never in a repository, never in a URL and never in client-side code. If you suspect one has leaked, tell us so that we can revoke it.

Permissions​

A token acts on behalf of the user who approved it. The API applies exactly the same permissions as the web interface: the api scope opens the door, but it never grants more rights than the user already has.

In practice this means an endpoint can answer 403 even with a perfectly valid token, simply because that user is not allowed to read or write this particular resource.

Error reference​

When authentication fails, the API answers with a detail field:

detailMeaning
invalid_tokenUnknown token. Check that you send the access_token value.
expired_tokenThe token is older than one hour, refresh it.
user_is_not_allowedThe account is disabled.
insufficient_scopeThe token was issued without the api scope.

A request sent without any Authorization header is treated as anonymous and is rejected on the permission check instead.

Scripts and automated jobs​

The authorization code flow also works for command line tools: run it once with a loopback redirect URI (for example http://localhost:8000/callback) to open a browser, then keep the refresh token in the local configuration of the tool.

The client_credentials grant is not supported: every token is tied to a user account. If you need an unattended job that is not tied to a human account, contact us so that we can look at your use case together.

Reviewing authorized applications​

Users can see the applications they approved and the data these applications can read on their profile page, under the OIDC tab (https://cri.epita.fr/users/<login>/oidc/). Removing an application from that list means it will have to ask for approval again the next time a user signs in with it.