# Advanced security considerations

The base implementation of OIDC should be secure enough in most cases. This page suggests advanced measures to increase the security of your implementation.

# PKCE: Proof Key for Code Exchange

Use PKCE

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. Code libraries and snippets can also help with implementing PKCE. See oauth.net: PKCE (opens new window) for further details.

# Private key JWT client authentication

When requesting tokens you can use a signed assertion as authentication instead of the basic authentication with a client ID and client secret pair. In Private Key JWT your application creates and signs a JWT using a private key.

Private Key JWT is a client authentication method that is more secure than the default “Client Secret” method. Note that Private Key JWT involves additional steps on your end.

# Prerequisites

Before configuring an application that authenticates using Private Key JWT, you must generate an RSA key pair. You can either:

  • Import the public part of an existing key pair into the Signicat Dashboard, or
  • Generate an RSA key pair directly in the Signicat Dashboard.

# Import the public key

If you wish to generate your own key pair, make sure you meet these technical requirements:

  1. 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.
  2. Store the public part of your key pair in a text file (or .json).

If you chose to create your own key pair, then in the next section click Import public key instead of Add public key in step 2.

# Generate the key pair

To generate a key pair in the Signicat Dashboard:

  1. Go to eID Hub > OIDC clients (opens new window). Click on the client you wish to use. If you haven't created a client yet, see Set up an OIDC client.
  2. In the menu for the client, click Advanced > Public keys and then Add public key.
  3. Pick a Name, Not valid before and Not valid after for your key.
  4. Select Signing as the Usage.
  5. Now click Create. If you want to upload your public key, either paste your key content or upload a file with the key.
  6. 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

Before you continue

Private Key JWT modifies how your application sends the token endpoint request, therefore this section assumes that you first start an OIDC authentication request as for any client authentication method.

Before you continue, your application should have already completed these steps:

  • Start an OIDC authentication request.
  • Authenticate the end-user.
  • Receive an authorization code on your chosen redirect URI.

After these steps, the Private Key JWT implementation below applies.

Authenticating using Private Key JWT consists of two steps:

  1. Building the assertion. This is a JWT signed by the private key that you generated.
  2. Exchanging the assertion for an access token to authenticate with Signicat.

# Step 1. Building the assertion

Begin by crafting an assertion in the form of a JSON Web Token (JWT).

What is a JWT?

A JSON Web Token (JWT) consists of three parts separated by dots (.), which correspond to:

  • Header
  • Payload
  • Signature

A JWT typically looks like:

xxxxx.yyyyy.zzzzz

The payload of the JWT should contain the following valid claims:

Claim Explanation
iss Issuer - Your OIDC client ID.
sub Subject ID - Your OIDC client ID.
aud Audience - must be the same as your 'Token URL'. (You can find the value in your OIDC client in the Dashboard (opens new window).)
jti JWT ID - a unique identifier for this specific JWT. Mitigates replay attacks.
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.

An example of JWT payload would look like:

{
    "iss": "<OIDC_CLIENT_ID>",
    "sub": "<OIDC_CLIENT_ID>",
    "aud": "https://<YOUR_DOMAIN_WITH_SIGNICAT>/auth/open/connect/token",
    "jti": <unique string, for instance a GUID>,
    "iat": 1673955575,
    "exp": 1673961575,
}

Now, create a signed JWT using a payload similar to the one above. Serialise this as a compact format JWT. The serialised JWT is a long string that looks like: eyJhbGciOiJSUzI...AiOiJKV1QifQ.eyJpc3Mi...J1ZX0.nmupzTs...H9whojA

# Code example

In the example below, the Python script shows how to generate the assertion:

Click to expand

import json
from time import time
from uuid import uuid4
from jwcrypto import jwk, 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. 
jws_private_key = {
    "kty": "RSA",
    "kid": "my-kid-123",
    "use": "sig",
    "alg": "RS256",
    "e": "AQAB",
    "n": "2sZ0sfbK7bZWOZgAYGWvEkL-9HWt-UadCKRyZHgIRNxvS1KdNZE4chcXF7sXfuAim6Ec_WbcEqGTOzOmQF-VejluB6jO-banEKl3pTGxRj2xj5tBFbtpWyj_AENHxpzbb7EPR_ahIPyvQeCU3VFOEw8juVd9X3gn_iJdmxbN7yIWzhuMRimAleT04AR8SP-g9NUauaEZSrIwny_JbeZ_Fb1m2XfPTB8KbbnYPeyQTQRy_2H5vMLRYGhOFyF9DS_6TguJlSc2VdPLsfW_uhbS4X1fd8-RVbF8UJ6lSanOhmIw1Choh_ncy90-tmmsk6T2QPKBwY0UqgU6bAv5iD2pdw",
    "d": "eRHSipn0-1Aor3661p3vIMAKr-Zf_M9jH-FBnPAAQ3tp69kwPvC6uAinMu7Ktd_7xvyGOoWtzHG2NNEEdCNxaU5W4c49nFvEYKgoGjdBz4lctghJIGmyiExLsi2JjxRHK6xktIJ78PFlW6OZPlE8T7fVIUCVlTu9hhomiyk3ldnVjrwJ5ESzaJs_zh6dqNcbfP-8APbG74ACejG3ojp9DWadOwA6kDtKIGdBAHZxObDJtG8qKQDcqvrxBUDXkifl-7t3b97HhxMRyR-d6wvrmz5mkYEzNZJt6LYkCyeGkE-IoQmPM0qYiWI7F3H4E1YjCoGlueoAQCoGOkFCMLYMQQ",
    "p": "8_fKHLlwG_t57jsYG97ggGiYnFV9P6FMU3xe6dptMMrqhuPl_XbMic8CA-vOVP_nTgxDfaEnv_wlGcYgVLwdlIICjvoLB1Fma4SguAVCloIZbYsiXBwpfXiTWn-RgSe1HtTbfzpmBpt4RJiHEPoWx34ZUDZhcSpREuO2sIG_U18",
    "q": "5ZCYsdA2yJmGJ_quksnNVIa8cby_a_SfFKg_5-CYSKyoZFaZttaNL064f0DCCO4tEp31W37_SankFUkdMDN8ccyW6xwnOj3PJRTX2J79sXdKaJDGleEXCKzuF7Rbi0nouP-1YFxjRmRh5WjBRDvbyCc-m_1ofxbwRskw3CPiOOk",
    "dp": "GU9YoXg_gDero6Jv0txhcBDp3DYmQ0apk3OwqRQnBcvXXt0fzBbaC2X1cJCzHDBcP8WX7t2cMReoha7_RasqanC-cTTRlhXEyVy-C7lH-jNPDgVEMEgfqcurhdT8NGj5KlSs3NsjIIZaiMtGH-XCHTogyCiMHWBlfs8u8crUHYM",
    "dq": "WiqSHv0mF2JdlCRdHyCOOo31REMbeH6LYSS4fQ31Ik5WkZqGI49fwt4Lj0fTLojGQVKzhS17feZxxH6ELWN7lIMEH_Jd4f1W-DyYjufbwzGUkz-SEFppnqm1lq_raOkttEQTbHa9M2_IF8AucOuF5rarW7-LpKdQ1qy9OSoK98k",
    "qi": "rR6CplTi8roFIPwTes4puAABvN-agWh6jRxVVll1SfjqBFoTQvBHp8bUhJO7zmAEhOh-oDQX5Prn24CDKu_kr7emD-WeF9qaGJG1Gltrnh_-nj2XWpfMFmeJWOcflAWJPeltRn3f9PUrnujMDHNDg4EsD8f-ckzPOldqTH_zF_Y"
}

# Loading key
rsa_jws_private_key = jwk.JWK()
rsa_jws_private_key.import_key(**jws_private_key)

# Token payload (JSON)
payload = {
    "iss": "<OIDC_CLIENT_ID>",
    "sub": "<OIDC_CLIENT_ID>",
    "aud": "https://<YOUR_DOMAIN_WITH_SIGNICAT>/auth/open/connect/token",
    "jti": str(uuid4()),
    "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"],
}

# Creating JWS token
jwstoken = jws.JWS(payload_bytes)
jwstoken.add_signature(rsa_jws_private_key, protected=jws_header)
jws_serialized = jwstoken.serialize(compact=True)

print(f"JWT: '{jws_serialized}'")

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.

# Step 2. Exchanging the assertion

After you build and sign the JWT with the required claims, you are ready to authenticate your application to receive an access token.

Use the signed JWT as your assertion towards Signicat token endpoint. For example, &client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion={JWT}.

The HTTP request would be:

POST /auth/open/connect/token HTTP/2
Host: yourdomain.signicat.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 1000

grant_type=authorization_code&scope=openid+profile&redirect_uri=https%3A%2F%2Fyourdomain.com%2Fredirect&client_id={OIDC_CLIENT_ID}&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion={JWT}

Note that the response you receive back is the same as from any other token endpoint request.

# Encrypted/signed responses from Signicat

As per OIDC specification, Signicat can send encrypted responses. This means we always encrypt the ID tokens and UserInfo responses for a given client.

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.

Finnish Trust Network (FTN)

For FTN you are required to receive encrypted responses. In fact, FTN fails if you do not set this up correctly and you are not able to obtain any FTN authentication results.

# Prerequisites

Typically you don'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 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:

  1. 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.
  2. 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

  1. In the Dashboard, go to eID Hub > OIDC clients (opens new window). Click on the client you wish to use. If you haven't configured a client, see Set up an OIDC client.
  2. In the menu for the client, click Advanced > Public keys and then Add public key.
  3. Pick a Name, Not valid before and Not valid after for your key.
  4. Select Encryption as the Usage.
  5. Now click Create.
  6. 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 that we can use to encrypt the responses we send to you, and that you should be able to decrypt.

Finally, you need to configure the encryption on your client:

  1. Go back into the Dashboard (opens new window) and find your client again.
  2. In the menu for the client, click Advanced > Security.
  3. Under User Info Response Type select SignedAndEncrypted. (If you prefer you can also select Encrypted, which is out of scope for this guide.)
  4. 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 are in the responses from Signicat. The ID token and the response from UserInfo is a nested JWT which is encrypted and signed:

  1. Decrypt it with your private key from the key pair given to Signicat.
  2. Deserialise the resulting signed JWT and verify it exactly the same as for a normal token.

# Code example

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/signing 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

  1. In the Dashboard, go to eID Hub > OIDC clients (opens new window). Click on the client you wish to use. If you haven't created a client yet, see Set up an OIDC client.
  2. In the menu for the client, click Advanced > Security and tick Requires Request Object.

This configuration rejects 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 that contains the jti claim, we will ensure that it is unique for this client within 24 hours. This will work automatically without additional configuration required.

If you do not wish to have unique request objects enforced, 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 accepts two types of request objects:

  1. Signed JWT: A valid JWS token.
  2. Nested JWT: A token that is first signed (JWS) and then encrypted (JWE).

This guide focuses on nested JWTs.

# Prerequisites

Before configuring an application that authenticates using nested JWTs, you must generate an RSA key pair. You can either:

  • Import the public part of an existing key pair into the Signicat Dashboard, or
  • Generate an RSA key pair directly in the Signicat Dashboard.

# Import the public key

If you wish to generate your own key pair, make sure you meet these technical requirements:

  1. 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.
  2. Store the public part of your key pair in a text file (or .json).

If you chose to create your own key pair, then in the next section click Import public key instead of Add public key in step 2.

# Generate the key pair

To generate a key pair in the Signicat Dashboard:

  1. Go to eID Hub > OIDC clients (opens new window). Click on the client you wish to use. If you haven't created a client yet, see Set up an OIDC client.
  2. In the menu for the client, click Advanced > Public keys and then Add public key.
  3. Pick a Name, Not valid before and Not valid after for your key.
  4. Select Signing as the Usage.
  5. Now click Create. If you want to upload your public key, either paste your key content or upload a file with the key.
  6. 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:

{
    "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.
  1. Create a signed JWT using a payload similar to the one above. Serialise this as a compact format JWT. The serialised JWT is a long string.
  2. 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.
  3. Use this nested JWT as your request object towards Signicat. For example, /auth/open/connect/authorize?client_id={CLIENT_ID}&request={REQUEST_OBJECT_JWT}.

# Code example

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}")

# Detailed tutorial and code example

For an even more detailed description of how to set this up, including code examples, see Code examples > Signed and encrypted tokens (Advanced).

Last updated: 11/04/2024 07:47 UTC