# OpenID Connect
Page contents
# Introduction to OpenID Connect
OpenID Connect (OIDC) is an authentication protocol based on the OAuth 2.0 protocol. OAuth 2.0 is a framework designed to support the development of authentication and authorisation protocols. It provides a variety of standardised message flows based on JSON and HTTP, used by OIDC to provide identity services. OIDC aims to support end-user authentication and identity information on top of the OAuth 2.0 protocol.
# Signicat OIDC server
Signicat offers the Signicat OIDC server as part of our Digital Trust Platform. The principles for the Signicat OIDC server is that it should be standards-compliant and at the same time support all the benefits and powerful features of our solutions.
Signicat is a certified OpenID provider!
Signicat is certified by the OpenID Connect Foundation as a Basic and Hybrid OpenID Provider of the OpenID ConnectTM protocol.
With this certification, we can assure you that the Signicat OpenID server is standards-compliant, which should lead to an easier and more secure implementation for you.
To view our certification details, please see OpenID's list of Certified OpenID Providers (opens new window).
# Set up an OIDC client
There are three ways you can connect to our services: You can use two different protocols, either OIDC or SAML 2.0, or you can integrate via our Authentication API.
If you decide to use OIDC, you will need to set up an OIDC client before you start building the integration. This page shows you how to do so.
- Log into the Dashboard (opens new window).
- Click Authentication, then OIDC clients and finally Create client.
- Enter a name for the client.
- In Primary grant type, select which grant type you want to use with this client. We strongly recommend AuthorizationCode. For further information about the different grant types, refer to the OIDC specification (opens new window) and the OAuth 2.0 specification (opens new window).
- In the Redirect URI field, enter the URL that you want to redirect your end-user to when the authentication process is finished. Note that if you want to use more than one redirect URI, you can still add them after the client has been created.
- In Scope, enter the scopes you want for this client.
openid
is mandatory.profile
andnin
are necessary in most cases. You can see the definition ofprofile
and other predefined scopes in the OIDC specification (opens new window). As fornin
, this scope requests access to the end-user's national identification number. - Click Submit.
Now your client is created. However, there are still some settings you must configure before you can start using it. In most cases, this means that you must obtain a client secret, but not always. Most typically this would be if you intend to use the client in a frontend flow but then you always need to use PKCE (opens new window). We strongly recommend using PKCE, even when you are using a client secret.
# How to create a client secret
Important
Make sure you save your client secret. You will only be able to view your client secret once, right after you create it. Therefore, it is important that you copy it and store it safely so that you can retrieve it later on. If you ever forget it or lose it, you have to create a new one.
- Select the name of the client you want to create a secret for.
- In the menu for the client, click Secrets and then Add secret.
- Enter a name for your client secret and click Create.
- Copy your new client secret and store it safely.
- Click Back to Secrets.
# Further OIDC configuration options
Here's an overview of other configuration options you might find useful:
- If you want to enable PKCE, click Security > Require PKCE. We strongly recommend using PKCE.
- If your setup requires encryption, you can upload a public key in Public keys. If the particular ID method you want to use requires encryption, you can find more details about how to upload the required public key in the configuration instructions.
- To configure additional redirect URIs, go to URIs.
- To add or remove scopes from your client, go to Access.
# Try it out!
The website https://oauth.tools/ (opens new window) is an excellent resource made by one of our commercial partners, Curity. As part of this partnership, you can now load directly into a Signicat Playground which is preconfigured for Signicat's Dashboard.
You can enter the Signicat Playground directly at https://oauth.tools/signicat (opens new window).
The website allows you to graphically explore and play around with any number of OIDC and OAuth2 flows. This intuitive no-code approach is helpful for understanding all of the different flows and options available in our Dashboard.
# Using your own specific account and clients
You can test a specific client by navigating to the client overview on the Signicat Dashboard, and selecting the Try out this client on oauth.tools! button. This will preconfigure the Signicat Playground with this client.
It is also possible to add and manage your clients manually from within the Signicat Playground. To do this, you can:
- Open OAuth Tools.
- On the left-hand side, right-click the 'Signicat Playground' (SP) symbol and select Settings from the menu.
- Click the Clients tab at the top.
- Manage your clients as you please.
- Close this settings view. The clients are now updated.
# About OIDC
OIDC lets developers authenticate users across websites and apps without having to own and manage password files. Authentication can be done in three different ways using OIDC:
- The authorization code flow
- The implicit flow
- The hybrid flow
We have implemented two of them, the authorization code flow and the hybrid flow. These flows will be the ones described in this document. To learn more about the different flows and their differences, refer to the OIDC documentation (opens new window).
# Authorization code flow
The authorization code flow provides you with an authorization code. You can exchange the authorization code for an ID token, an access token and a refresh token (optional) at the token
endpoint of our OIDC server.
This process provides the benefit of not exposing any tokens to the user agent (and possibly other malicious applications with access to the user agent). For the authentication server to trust the client before exchanging any authentication data regarding the end-user, the server can authenticate the client. The authentication code flow is therefore suitable for clients that can securely maintain a client secret between themselves and the OIDC authentication server.
The authorization code flow steps for performing authentication to log in the end-user (or to determine that the end-user is already logged in) are as follows:
- The user agent requests to be authenticated.
- The client prepares an authentication request and sends the request to the OIDC server.
- The authorisation server authenticates the end-user.
- The authorisation grant code is requested.
- The OIDC server obtains end-user consent (optional).
- If consent is asked for but denied, the end-user will not be authorised and will not be able to access their resources. The login will be stopped. The process will end at this step.
- If consent is asked for and given, the end-user will be authorised. The process continues with step 6.
- The OIDC server sends the end-user back to the client with an authorization code.
- The client requests a response using the authorization code at the
token
endpoint of our OIDC server. - The client receives a response that contains an ID token, an access token and a refresh token (optional) in the response body (among other parameters).
- The client validates the ID token, retrieves the end-user's subject identifier and requests resources.
# Hybrid flow
The hybrid flow combines the implicit flow with the authorization code flow. With the hybrid flow, some tokens are returned from the authorization
endpoint and others are returned from the token
endpoint.
Here's a brief summary of how the hybrid flow works:
- The user agent requests resources.
- The client prepares an authentication request and sends it to the OIDC server.
- The authorisation server authenticates the end-user.
- The authorisation grant code is requested.
- The OIDC server obtains end-user consent.
- The authorisation server sends the end-user back to the client with an authorization code and, depending on the response type, one or more additional parameters.
- The client requests a response using the authorization code at the
token
endpoint of our OIDC server. - The client receives a response that contains an ID token and an access token in the response body (among other parameters).
- The client validates the ID token and retrieves the end-user's subject identifier.
# Claims
At the heart of OIDC are claims; an attempt to standardise all the different pieces of information about an end-user. A claim may, for instance, be an end-user's surname or email address, which makes claims very similar to the concept of assertions in SAML. However, claims in OIDC are represented (as everything else in OAuth 2.0/OIDC) as JSON key-value pairs.
Where to get claims?
The OIDC specification details sources of claims: the ID token and the UserInfo
endpoint.
The ID token is a signed JSON object (a JSON web token, or JWT) containing the authentication result. You can learn more about the ID token in the OIDC documentation (opens new window). Some claims in the ID token are optional and can be left out depending on how much information you want it to contain.
The UserInfo
endpoint is a separate endpoint that accepts access tokens and returns information about the end-user. An access token represents an authorisation from the end-user to the client to perform actions on the end-user's behalf. Therefore, only information which the end-user has authorised should be returned. The UserInfo
claims that can be returned are largely standardised but can be infinitely extended.
Note
We strongly recommend that you use only ID tokens for obtaining claims. Using UserInfo
usually adds complexity for very little benefit.
# Signicat-specific OIDC
# Key rotation
The signing keys ("use": "sig") published through https://api.signicat.com/auth/open/.well-known/openid-configuration/jwks (opens new window) are used for token signing. An undetermined number of keys will be available. If token signature validation fails, fetching the current key set will give a key set where one of the keys is the correct one.
A new key for token signing will be published at the aforementioned endpoint in advance of any changes to which key is used, to let caching solutions prefetch the updated key set.
Remember that caching solutions on your side must regularly refresh the data they cache – the keys on our side will be rotated frequently.
# Default signing and encryption
- Encryption algorithm: AES-128
- Supported key length: 2048-bit (4069-bit also supported)
- Signature hash: SHA-256
What? | Signing | Encryption |
---|---|---|
ID token | Always | Optional, off by default |
Access token | Always | Never |
UserInfo response | Optional, off by default | Optional, off by default |
Elliptic-curve cryptography (ECC) is currently not supported in the Signicat OIDC server, but we aim to add support for this at a later date.
# Endpoints
The following endpoint URLs are available for communicating with the OpenID Connect provider through Signicat.
# Authorization
The authorization
endpoint performs the authentication of the end-user. You can find the full list of available request parameters in the 'Authentication Request' section (opens new window) of the OIDC documentation.
Example authorization request
"https://<YOUR SIGNICAT DOMAIN>/auth/open/connect/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=code&scope=openid%20profile&state=123&code_challenge=ABC123&code_challenge_method=S256&response_mode=query"
# acr_values
You can use acr_values
to adjust how the authentication flow behaves. There are generic parameters (listed below) that work for all IdPs (identity providers), as well as IdP-specific parameters.
The format is a space-separated list of key-value pairs: acr_values=key1:value1 key2:value2
, and so on.
Example: acr_values=idp:idp_name
Result: IdP will be pre-selected for the end-user.
Available parameters:
Name | Description |
---|---|
idp | Defines which IdP to use. This works in conjunction with login_hint . |
theme | Specifies which theme to use. |
# login_hint
You can use this login_hint
to prefill end-user information that we send to the IdP, such as the end-user's national identification number. The format is a space-separated list of key-value pairs: login_hint=key1:value1 key2:value2
, and so on.
The following options are available:
Name | Description |
---|---|
nin | Prefilled National identification number. |
mobile | Prefilled mobile phone number. |
dateOfBirth | Prefilled date of birth (YYYY-MM-DD). |
email | Prefilled email address. |
username | Prefilled username information. |
Note
You can find specific configuration examples for the following ID methods here:
# Token
To obtain an access token, an ID token and optionally a refresh token, the client sends a token request to the token
endpoint to obtain a token response.
Example token request
curl -XPOST "https://<YOUR SIGNICAT DOMAIN>/auth/open/connect/token" -H "Content-Type: application/x-www-form-urlencoded" -H "Authorization: Basic czZCaGRSa3FOMzpnWDFmQmFOM2JW" -d "grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https://example.com"
Example token response
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token": "eyJ ... Klmmm.emo ... NzBKFf.gaW8g ... Mzn5"
"token_type": "Bearer",
"refresh_token": "eyJ ... zcuUk.mow ... 7HmKmn.ggW8h ... Gz4g",
"expires_in": 3600,
"id_token": "eyJ ... zcifQ.ewo ... NzAKfQ.ggW8h ... Mzqg"
}
# UserInfo
To obtain the requested claims from the UserInfo
endpoint, the client makes a request using an access token obtained through OpenID Connect authentication.
Example UserInfo
request
curl -XPOST "https://<YOUR SIGNICAT DOMAIN>/auth/open/connect/userinfo" -H "Accept: application/json" -H "Authorization: Bearer fAAdL01c6QWDbPs9HrWHz5e7nRWVAnxqTTP7i88G"
Example UserInfo
response for valid access token
HTTP/1.1200OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"sub": "bob"
"given_name": "Bob",
"name": "Bob Smith",
"email": "bob@mycompany.com",
"phone_number": "+1 (604) 55-555-66-777",
"address": {"formatted": "123 Main St., Anytown, TX 77777"}}
# Error codes
We follow the OIDC and OAuth standards as closely as possible, which is also true when conveying error messages.
Errors on the authorization
endpoint are primarily passed along as parameters on the redirect URI. These URL parameters are error
and error_description
. You can see a list of the Signicat-specific error codes here.
There will be some situations where it's not possible to use the redirect URI to pass along errors. In these cases we present an error page (opens new window). These types of errors are typically the result of an incorrect implementation or invalid configuration. These errors are typically not caused by our end-users.
Errors from the token
and UserInfo
endpoints are strictly defined in the OpenID Connect and OAuth protocol specifications:
Finally, there are standard HTTP status error codes which can, for instance, appear if there is a serious error in the solution (typically a 5xx server error) or if you are trying to access an incorrect URL (404). These will be what you would expect from any web service or API.
# Finnish Trust Network (FTN) specifics
Due to requirements from Traficom, you are required to use Full Message-Level Encryption (MLE) for authentication with the Finnish Trust Network (FTN).
There are two different ways to achieve this. The first is required and the second is only required in certain circumstances:
- Receiving encrypted responses from Signicat (required)
- Sending encrypted requests to Signicat (optional)
Important
If you are sending personally identifiable information (PII) as part of your request, you will also need to send encrypted requests. This is typically relevant if you are prefilling the end-user's national identification number (NIN).
Both these mechanisms are described in the next section: Additional security considerations.
# Additional security considerations
The base implementation of OIDC should be secure enough in most cases. This page mentions a few measures that can be taken to increase security, if desired.
# PKCE: Proof Key for Code Exchange
Note
We recommend that you always use PKCE, no matter what use case or setup you have. It's a very low-effort implementation detail that adds a lot of extra security.
Most OIDC libraries support PKCE, and there are also libraries and code snippets that implement PKCE. See oauth.net: PKCE (opens new window) for further details.
# Encrypted responses from Signicat
As per OIDC specification, Signicat can send encrypted responses. This means we will always encrypt the ID tokens and UserInfo
responses for a given client.
Of course responses from Signicat are fully encrypted on the transport layer with HTTPS, but adding message-level encryption can be used as an additional security layer.
This section explains how to set up and process encrypted responses.
Important
For FTN you are required to receive encrypted responses. In fact, FTN will fail if you do not set this up correctly and you will not be able to obtain any FTN authentication results.
# Prerequisites
Typically you won't need to create your own keys, as you can generate key pairs in the Signicat dashboard. If you do want to fully manage your own keys, you will need to first generate one and then import it and store it in our system so that we can use it.
If you wish to create your own key pair, these are the technical requirements:
- Generate a JWK RSA key pair (2048-bit) suitable for encryption. We also support 4096-bit if required. It is important that you create a JWK or an X509 certificate. We recommend using the JWK format.
- Store the public part of your key pair in a text file (typically
.json
).
If you chose to create your own key pair, then in the next section click Import public key instead of Add public key.
# Required configuration
- Log into the Dashboard (opens new window).
- Click Authentication and then OIDC clients. Click on the client you wish to use. If you haven't configured a client, see Set up an OIDC client.
- In the menu for the client, click Advanced > Public keys and then Add public key.
- Pick a Name, Not valid before and Not valid after for your key.
- Select Encryption as the Usage.
- Now click Create.
- A key pair is generated for you. This contains a public key and a private key. Take a copy of the private key and store it somewhere safe.
You now have an encryption key which we can use to encrypt the responses we send to you, and that you should be able to decrypt.
Finally, you will need to configure the encryption on your client:
- Go back into the Dashboard (opens new window) and find your client again.
- In the menu for the client, click Advanced > Security.
- Under User Info Response Type select SignedAndEncrypted. (If you prefer you can also select Encrypted, which is out of scope for this guide.)
- Enable Encrypt ID Tokens.
You are now ready to move on to the implementation.
# Implementation
You should send your requests as normal OIDC requests. However, the differences will be in the responses from Signicat:
The ID token and the response from UserInfo
is a nested JWT which is encrypted and signed.
- Decrypt it with your private key from the key pair given to Signicat.
- Deserialise the resulting signed JWT and verify it exactly the same as for a normal token.
# Example code
Important
This code example is for illustrative purposes only. It is not intended for production usage. Signicat takes no responsibility if this code example is used inappropriately.
from jwcrypto import jwk, jwe
# ATTENTION: Insert your nested JWT (that you got from Signicat OIDC server) here!!!
jwe_serialized = "eyJh...QifQ.Qq5TQR...xN-PQ.ojSGehTV7xw_xE078sFnTg.in8a...SITqA.SyVNrXe0C3UkKBQ_AVyQiw"
# For the sake of simplicity, the JWKs has been manually loaded into a dictionary.
# In a real life scenario you should do this in a secure manner: The jwe_private_key should be stored securely on your premise.
jwe_private_key = {
"alg": "RSA-OAEP",
"d": "MODJkQVnKx-txB11QSkz2roacDL9z-BqcDq-dYM2OHrI-x4iNpdBWtoS2Hi4OfH6ETmzMnFNVY3oIYoaMTuuWKc0r_QeIIqbkt8MozALusETiB2VtiAiiZU6G2DyAOFaWaoiIlRYO5BciDCM6z4ytfnDFYM7-K-YSA3_V7jDSGjRroGC112lVs9BzA3qjCP8jnb9VvNKUhdcFqsqGU8eRw8fZp73uHyEm9tVHimSPjyLda3XlcxPSEkhqlP4aaleWhkUQJM3bc2YDnpWBjlNuIHsacEA28xrwOqHSqK-b8klvxk-e_w-C-SdlcYhgB5DeSB0bEuCpmV9GKwOp0KhuQ",
"dp": "hkPXP00qlIrioShp3FiXp0_OahJXe4cVNAXLJYgKRsqv72I7AXZltAOXNRanpQDQutDADFsMTx1XxoEQm_GUvTUe-dS-PkBGKJMLjp28zs9G0L4yXUXenKu_vs-3wD4_06WtT_kOyJD48LaQno6SYdUm4JBvS5A1ve2NYI7xDEk",
"dq": "WGmNHsCzREbHgwQCStW9munP16NJzdeXJ0ys7QbLwpOxMEP1BkSznoRw4_0IkSxTtyvWPG9Eo-bPCKfkqILUowG7tniIerf0F6LkCjssPk5wNxEK_Ktt-59_3Wrvs9Iik4tYF0bEqlXyidFq7ayBoRdiPZ0-ELjU-hEqY0GR0Ok",
"e": "AQAB",
"kid": "mykey-123",
"kty": "RSA",
"n": "zOeJ1RJOgP6NlgwEcqa-BtHUC5nNNa2UbsQQsNDQa6KioVcNfiz5WWmQF3FAR7HTAxQveXfgT7PqOmPgiDVHTLcdYlOjcOAESCTFYELsuh58xnA99agM9vPuLVo2x-hwjE_b-1dC3Ph2_gpXwqS6JDjKa5VF-5zMLbVLQJ96yhKGqzQ7TrfXrEcgfzXMipTbQTxpDAFnTyYYQ7lOVp_ms0Kbz4fpRbyGUtzYsgkYBCNwfWgtfXZW7ahMeb84ukbH2nXXQKQsxuSYU-gbK44yVvcvltqg5wMjG39Xo-BUGlJGQzEN0-6QEIqGrMXgTx5B147IV22vT7demmNx2_RXDQ",
"p": "-3lVxpmyecuu7-tceX7eoVjHmUlbHzUkhehkV_axQx5FtX5NRWdzqg8jhmPBBRpMtvG7g55ZQihVTi3sphfP0k1czEPsgJt8diZ4KIMDgimcksyJ8yPP0dZuvdnBVFVdlJDWfimmLxmFwlmw-p3gCAPePbqRjD1vo25Bwi5Dujk",
"q": "0JeicVX3YgVhUXuY_f3BX-VDKR0LH_SLaIaEimuvpleo5AM4El_An8SR-_Z1GXZuzsOvff7d_E79NjwsdhtKKxQiW93awsFD9Fd5fqNUOpB0ikAf-gNz6MCujt9nhS_jvbMsNI6gCRexZ4gD-RB9dj_qXsWWJUJQpcu7QMmeE3U",
"qi": "-MNE31_wnD2aRLiZz0piLD6lHJwSYpGFinBKWp5VPuLdeXAruSdKW82oRTapA-y6s87fB85jcjvW3dAYjzFFsFNpjDzrpCwctIsQVzZt_vVu5Mdon365tFHWyH2xAcTmVxdJGDunR6BKMmnciCdXTgZP8wSAEWrjmVop_ontfM8",
"use": "enc"
}
# Loading JWK key
jwk_jwe_private_key = jwk.JWK()
jwk_jwe_private_key.import_key(**jwe_private_key)
jwetoken = jwe.JWE()
jwetoken.deserialize(jwe_serialized)
jwetoken.decrypt(jwk_jwe_private_key)
jws_serialized = jwetoken.payload
print("\nSIGNED JWT (just like a normal ID token):\n")
print(jwetoken.payload)
# Encryption of the request object
As per OIDC specification you can optionally use the request
parameter to enable signed and optionally encrypted requests. This usually isn't required, but can be used as an additional security measure. It can also be used to comply with any possible Full Message-Level Encryption (MLE) requirements. A common use case is to mask any personal information contained in the request, and also to send requests that cannot be tampered with by any third parties.
# Additional hardening of request object mechanism
There are two things you can do to additionally harden the security:
# Requires Request Object
Log in to the Dashboard (opens new window).
Click Authentication and then OIDC clients. Click on the client you wish to use. If you haven't configured a client, see Set up an OIDC client.
In the menu for the client, click Advanced > Security and tick Requires Request Object.
This will reject any request that does not use a request object for this client. Effectively, it forces all requests on this client to be at least signed.
# Unique/One-Time-Use Request Object
If you submit a request object token which contains the jti
claim, we will ensure that it is unique for this client within 24 hours. This will work automatically, there is no configuration required.
If you do not wish to have unique request objects enforced, you simply omit the jti
claim.
This mechanism can effectively prevent “replay attacks” when using request objects, as each request object token can only be used once.
# Type of request objects
The Signicat OIDC solution will only accept two types of request objects:
- Signed JWT: A valid JWS token.
- Nested JWT: A token which is first signed (JWS) and then encrypted afterwards (JWE). This guide will focus on nested JWTs.
# Prerequisites
Typically you won't need to create your own keys, as you can generate key pairs in the Signicat dashboard. If you do want to fully manage your own keys, you will need to first generate one and then import it and store it in our system so that we can use it.
If you wish to create your own key pair, these are the technical requirements:
- Generate a JWK RSA key pair (2048-bit) suitable for encryption. We also support 4096-bit if required. It is important that you create a JWK or an X509 certificate. We recommend using the JWK format.
- Store the public part of your key pair in a text file (typically
.json
).
If you chose to create your own key pair, then in the next section click Import public key instead of Add public key.
# Required configuration
- Log into the Dashboard (opens new window).
- Click Authentication and then OIDC clients. Click on the client you wish to use. If you haven't configured a client, see Set up an OIDC client.
- In the menu for the client, click Advanced > Public keys and then Add public key.
- Pick a Name, Not valid before and Not valid after for your key.
- Select Signing as the Usage.
- Now click Create.
- A key pair is generated for you. This contains a public key and a private key. Take a copy of the private key and store it somewhere safe.
You are now ready to move on to the implementation.
# Implementation
Begin crafting the OIDC authorisation request. The parameters are standard OIDC parameters. The requirements are as follows:
- The HTTP request can be made using either GET or POST.
- The payload must be a request object (per OIDC Core specifications, section 6.1). An example of a typical payload request object looks as follows:
{
"client_id": "dev-annoyed-sloth-492",
"response_type": "code",
"redirect_uri": "https://oauth.tools/callback/code",
"acr_values": "idp:ftn",
"state": "ABCDEF012345",
"scope": "openid profile",
"iss": "dev-annoyed-sloth-492",
"aud": "https://team-connect-demo.test.signicat.dev/auth/open",
"iat": 1673955575,
"exp": 1673961575,
}
As seen above, the JWT requires the following additional claims to be valid:
Claim | Explanation |
---|---|
iss | Issuer - needs to be the same as your client_id . |
aud | Audience - needs to be equal to your 'Issuer URL'. (Check your client on the Dashboard (opens new window) to find it.) |
iat | Issued At - A UNIX timestamp (in seconds) for the time at which you created the token. |
exp | Expire - A UNIX timestamp (in seconds) for the time at which your token will no longer be valid. We recommend now + 10 minutes . |
- Create a signed JWT using a payload similar to the above. Serialise this as a compact format JWT, which will essentially be a long string.
- Now create an encrypted JWT using the serialised signed JWT from above as the payload. This should again be serialised as a compact format JWT.
- Use this nested JWT as your request object towards Signicat (e.g.
/auth/open/connect/authorize?client_id={CLIENT_ID}&request={REQUEST_OBJECT_JWT}
).
# Example code
Important
This code example is for illustrative purposes only. It is not intended for production usage. Signicat takes no responsibility if this code example is used inappropriately.
import json
from time import time
from jwcrypto import jwk, jwe, jws
# For the sake of simplicity, the JWKs has been manually loaded into a dictionary.
# In a real life scenario you should do this in a secure manner: The jws_private_key should be stored securely on your premise.
# The jwe_public_key should be dynamically fetched from Signicat's JWKS endpoint.
jws_private_key = {
"alg": "RS256",
"d": "IfHsADmK2xsxbie2Q4pX5cOEfWEeD8efIC82_PtK7AT-ZO3vwll9WUQ_VMWXZuUkmDDwuLukp-GCAq871YfsJKTEXYibArYZSB6yPo7FHMxtEtsfOqDGEjCJHd76AE99ZH8qA2H_0j_xGMGbAP44x5DoTAidj1B3fKEF_IQfoHNfvQBwLaE2lyJVxQp_Xzay_VbdeGRlacmzc8ccGWU3lpAYDONMPxh0Z9tx2aexarDOjFbZ0jKN9b9-Hyntnz7iOFdiOWSHo41NOd5l2BG4SARdRPUwoMxcEexxVVPjpr1smNwLHJZdAvOMiHSAKDAwKh4Vgu4p5CfD6O6JM_PoSQ",
"dp": "TSi83Xlf9XP9DMeqCQgMXUcrxt4nSh56C0RnqRPneGShlObkUZmxJoaEh5TL5OJ-9MQKcGCBN0_LOFls1p4NOvdhZxxwHi2hEZisdd40IY37Z-4JN0L5Xs4jqLDrZAP_paEW1SOI33sqhHCeLi034DZw0-4BxKXm5Fhn-uS2WZ8",
"dq": "jSvo3MsPzigQN_20txhQFxqQaw6M7udXgt51QW50tnM896DlpEd0hbcryOncpfc8AVhjyxLnGG2sYQRLswcq838STu4wDEhuPfhgKAak_5cc38N_TSyRJWLfnwF9pTTWPgkk0Io8MwWpsIpf9qIbyBOl0jHnBdfSuuLKoYM6QfU",
"e": "AQAB",
"kid": "abc123321cba",
"kty": "RSA",
"n": "muNJBpq6VtZbGj0kb5YeMe5lE14CZ4OAKnY6Epzholp61rVrrWRlErmyV9C8J4G6jCgDKrGnjQp143gD71ATVLIus__wO1YtRplKCyz2hypvhvId8GwYOLm1k3TCjKeSa6DwKiKZZOUg011NWN9TSSkCdWb-xNgSV5gZesC_JngofTyrAXT92MDIzGoCMpA1D6tDIzadIigHA7_FznpT1eN5cAHZqMeRC8MHRH2_K8erUxx4QFuencrADmaf4vIWjTlmPioxa4XLRrYcXsnKrrKeVp8CXRFqMwb0fAtRmvQu3RHUqZ02dlTER7ocIbrHeYx2_VAxhqFtLhR9J2ih0w",
"p": "zpfR9mwYoPB-r1wTy5owD8RaoufPeVnabefe7IK9nwGUOkgINHDGTRPvXx2f0UuKz9kAVB6ZIsPgwCfhoMrbdQemNLXu5VZ_MRny6Uesk_5Awox5QIBbYvzF6PnMfV-pwLtieNLpiN_sP-pF6jfvJ8cy3XFBUnF5IDy0zeuI4Lc",
"q": "v-3u72-IPZpVuY8hkqi8gPpc5FTSWh-MJ5BvOcifDFjVYEE-KCTL5I2MJ2R9u-1sVceeFwfa-HfgV3ArizO91-pSJSqk-Py3KqHDXXdno30wwwJWR8noSwH8eRwqdQHBQQPa0ClywQJ2bbqiiw30CpjXDHknL5qXQEskpoJq88U",
"qi": "SGo4PyG344Uxddy_g_YGTt5pa_lvq0G42jGWU-fOPQfjSiNWhzuyflys9YoSf_x3kzwf1oJosdUdCpfUXzLX1LHsWrUld-52JFPtuFT3nNVZiFXCnboUHJSIlgaP1GlA1rHhh491gMIPqr4uJY3BSKp7aj2efL61Z-1l-LvwrSw",
"use": "sig"
}
jwe_public_key = {
"kty": "RSA",
"use": "enc",
"kid": "sandbox-encryption-key-0eb954d0e12b824b80a3c5664232320a",
"e": "AQAB",
"n": "trU6figprTeSBBTdW7Lqm6OjgNg4neNSvtEUs__bf5iK59W9oH2oeZLPx7ixmuJ3Cane6WHCR4BumxtnoIixWlNAJgdu_xosI-zO_7fhUVIgS-qH5kY9rj8GUa6GUquAV92_L3nnEWgKJ_220jV8_kdaqab1Pm5QMJ9RSC73BWuvCZ5fWb57okFN5-dTtjZ-WCmgij-9axPKjlM0PTp4c8Lm8KJDO-B-6n8DO9JEfdBpa6ejEIGi3tNJAyveluTzZ5YyF_WFD4rJZ2JIJIuGth7-o1Myq5QhmaXBTrL01aY-bLgiqWrtBNpM2rnU3MMUQn0TOpzKVvRaTl-x5HIpvw",
"alg": "RSA-OAEP"
}
# Loading keys
jwk_jws_private_key = jwk.JWK()
jwk_jws_private_key.import_key(**jws_private_key)
jwk_jwe_public_key = jwk.JWK()
jwk_jwe_public_key.import_key(**jwe_public_key)
# Token payload (JSON)
payload = {
"client_id": "dev-annoyed-sloth-492",
"response_type": "code",
"redirect_uri": "https://oauth.tools/callback/code",
"state": "ABCDEF012345",
"scope": "openid profile",
"iss": "dev-annoyed-sloth-492",
"aud": "https://team-connect-demo.test.signicat.dev/auth/open",
"iat": int(time()),
"exp": int(time()+6000),
}
payload_bytes = json.dumps(payload).encode('utf-8')
# Token headers
jws_header = {
"alg": jws_private_key["alg"],
"kid": jws_private_key["kid"],
}
jwe_header = {
"alg": jwe_public_key["alg"],
"enc": "A128CBC-HS256",
"kid": jwe_public_key["kid"],
}
# Creating JWS token
jwstoken = jws.JWS(payload_bytes)
jwstoken.add_signature(jwk_jws_private_key, protected=jws_header)
jws_serialized = jwstoken.serialize(compact=True)
# Encoding JWE token
jwetoken = jwe.JWE(jws_serialized, recipient=jwk_jwe_public_key, protected=jwe_header)
jwe_serialized = jwetoken.serialize(compact=True)
print(f"https://team-connect-demo.test.signicat.dev/auth/open/connect/authorize?client_id={payload['client_id']}&request={jwe_serialized}")
# External references
# OpenID Connect
- OpenID Connect Core 1.0 (opens new window)
- OpenID Connect Discovery 1.0 (opens new window)
- OpenID Connect Session Management 1.0 draft 28 (opens new window)
- OpenID Connect Front-Channel Logout 1.0 draft 02 (opens new window)
- OpenID Connect Back-Channel Logout 1.0 draft 04 (opens new window)
# OAuth 2.0
- OAuth 2.0 - RFC 6749 (opens new window)
- OAuth 2.0 Bearer Token Usage - RFC 6750 (opens new window)
- OAuth 2.0 Multiple Response Types (opens new window)
- OAuth 2.0 Form Post Response Mode (opens new window)
- OAuth 2.0 Token Revocation - RFC 7009 (opens new window)
- OAuth 2.0 Token Introspection - RFC 7662 (opens new window)
- Proof Key for Code Exchange - RFC 7636 (opens new window)
- JSON Web Tokens for Client Authentication - RFC 7523 (opens new window)
- OAuth 2.0 Device Authorization Grant - RFC 8628 (opens new window)