Skip to content

JSON Web Tokens

Par mesure de sécurité, la création d'identité et le chiffrement anonyme nécessitent des JSON Web tokens.

Pour la création d'identité, cette fonctionnalité remplace les anciens User License Tokens qui sont maintenant dépréciés.

Génération des secrets de JSON Web Token

Pour créer un JSON Web Token, il vous faut tout d'abord un JWTSecret. À chaque secret est associé un ID JWTSecretId. Vous devrez récupérer ces deux valeurs pour permettre à votre serveur de créer des JSON Web Tokens.

Chaque JWTSecret possède des permissions. Pour plus de détails, référez-vous à la section permissions.

TIP

JWTSecret est, comme son nom l'indique, secret. Veuillez prendre toutes les précautions nécessaires pour le stocker de manière sécurisée.

Au contraire, JWTSecretId n'est lui pas secret.

Via le dashboard

Lors de la création de votre compte, un secret de JWT incluant toutes les permissions (permission : -1) est automatiquement créé. Celui si s'affiche sur la page d'accueil.

Pour générer un autre secret, connectez vous sur votre dashboard d'administration et suivez les étapes suivantes :

  • Allez dans les paramètres, onglet secret de JWT.
  • Cliquez sur créer un secret.
  • Sélectionnez les permissions souhaitées.
  • Confirmez la création du secret.

Une fois le secret créé, récupérez son ID, JWTSecretId, et sa valeur : JWTSecret.

De manière programmatique

Afin de créer un JWTSecret, vous pouvez faire un POST /dashboardapi/v2/jwtsharedsecret/, avec dans le corps de la requête :

json
{
  "permissions": Array<Permission>
}

Exemple de code

Exemple de requête pour créer un JWTSecret :

shell
curl -X POST https://dashboard.seald.io/dashboardapi/v2/jwtsharedsecret/ \
  -H 'X-DASHBOARD-API-KEY: VOTRE_JETON_D_ACCES' \
  -H 'Content-Type: application/json' \
  --data-raw '{"permissions": [1]}'

Exemple de retour :

json
{
  "id": "32266d8c-2085-490a-8ef5-259ea35e1501", // UUID : JWTSecretId
  "created": "2021-11-09T10:49:09.490338Z", // Timestamp ISO
  "shared_secret": "c9kijQo1kJgieXZ9TAHFj9R0TgHb4bgLhDnWWRgjq4TmBzUdSB5mzuOcBb0gQMSi", // String : JWTSecret
  "permissions": [ 1 ] // Array<Permission>
}

Ajout d'un connector

Il est possible d'associer un connector à une identité du SDK. Ce connector est un string qui caractérise un utilisateur de façon unique pour votre application. Vous pouvez choisir tout identifiant unique. Selon le modèle d'un utilisateur dans votre application, vous pouvez par exemple choisir : le nom d'utilisateur, son adresse email, son ID interne dans votre application, ...

Le connector doit être au format ${IDENTIFIANT}@${APP_ID}. Le type du connector doit toujours être 'AP'.

Une identité peut avoir plusieurs identifiants.

TIP

L'ancien nom, aujourd'hui déprécié, de cette fonctionnalité, était userId. Le userId correspondait à la partie IDENTIFIANT du format de connector expliqué ci-dessus, sans le @${APP_ID}.

Pour cela, le secret de JWT doit avoir la permission PERMISSION_ADD_CONNECTOR. Ce connector sera à spécifier par la clé connector_add du JWT.

WARNING

Le connector est stocké en clair dans notre base de données. Pour minimiser les données que Seald enregistre sur vos utilisateurs, il est donc déconseillé d'utiliser une adresse email ou toute autre donnée personnelle. La solution optimale est d'utiliser un UUID.

Permissions des JSON Web Token

Chaque JWTSecret possède des permissions, et il peut assigner tout ou partie des permissions qu'il possède à chacun des JSON Web Token qu'il crée.

Les Permission sont des nombres entiers. Les valeurs possibles sont les suivantes :

  • PERMISSION_ALL = -1 : toutes les permissions.
  • PERMISSION_ANONYMOUS_CREATE_SESSION = 0 : permet de créer des JSON Web Tokens pouvant créer une Encryption Session anonymement.
  • PERMISSION_ANONYMOUS_FIND_KEYS = 1 : permet de créer des JSON Web Tokens pouvant récupérer des clés de chiffrement des destinataires.
  • PERMISSION_ANONYMOUS_FIND_SIGCHAIN = 2 : permet de créer des JSON Web Tokens pouvant récupérer la SigChain des destinataires. Non-utilisé.
  • PERMISSION_JOIN_TEAM = 3 : permet de créer des JSON Web Tokens permettant à une instance de SDK de rejoindre votre team.
  • PERMISSION_ADD_CONNECTOR = 4 : permet de créer des JSON Web Tokens permettant d'ajouter un identifiant personnalisé à une identité du SDK.
  • PERMISSION_ANONYMOUS_FIND_SYMENCKEY = 5: permet de créer des JSON Web Tokens permettant de récupérer une Encryption Session anonymement via une SymEncKey.

Création d'un JSON Web Token

Le JSON Web Token, ou JWT, est créé avec le JWTSecret et son JWTSecretId : il a comme iss le JWTSecretId, et est signé avec le JWTSecret avec l'algorithme HS256.

Ses données sont :

NomTypeDomaine d'utilisationDescription
issstringGénéral"Issuer" : le JWTSecretId
iatnumberGénéral"Issued at" : horodatage (en seconde) du moment de création du JWT. Si exp n'est pas défini, le JWT expire au bout de 10 min après création.
exp?numberGénéral"Expire" : horodatage (en seconde) du moment où le JWT expire. Optionnel. Si non défini, le JWT expire au bout de 10 min après création.
jti?numberGénéral"JWT ID" : nonce unique. Ne doit jamais être réutilisé. Optionnel. Si défini, ce JWT n'est utilisable qu'une seule fois. Sinon, il est utilisable jusqu'à son expiration éventuelle.
scopes?Array<Permission>GénéralListe des permissions de ce JWT. Optionnel. Doit être un sous-ensemble des permissions du JWTSecret. Si défini, ce JWT est limité à ces permissions uniquement. Sinon, il possède toutes les permissions du JWTSecret créateur.
recipients?Array<string>Chiffrement anonymeListe des sealdIds des destinataires pour qui ce JWT est autorisé à effectuer des opérations.
owner?stringChiffrement anonymeOptionnel pour la récupération de clés de destinataires. Nécessaire pour la création de session. sealdId de l'utilisateur qui sera propriétaire de la session créée.
join_team?booleanGestion des identitésPermet à l'identité de rejoindre votre team. Chaque identité ne peux être que dans une seule team à la fois.
connector_add?{ value: connectorValue, type: 'AP' }Gestion des identitésPermet l'ajout d'un identifiant personnalisé connector à une identité. La value doit être de la forme ${IDENTIFIANT}@${APP_ID}.
sym_enc_keys?Array<string>Récupération de session anonymeNécessaire pour la récupération de session anonyme. Tableau des IDs de SymEncKeys que ce JWT sera autorisé à utiliser pour récupérer une session.

Exemples de code

JWT d'inscription

Exemple de création d'un JWT d'inscription, permettant à une identité de rejoindre votre équipe, sur votre backend :

javascript
import { SignJWT } from 'jose'

// Un secret de JWT qui a `PERMISSION_JOIN_TEAM`
const JWTSecretId = 'JWT SHARED SECRET ID'
const JWTSecret = 'JWT SHARED SECRET'

const token = new SignJWT({
  iss: JWTSecretId,
  jti: random(), // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
  iat: Math.floor(Date.now() / 1000), // Validité limitée à 10 minutes. `Date.now()` donne la date en millisecondes, il faut la date en secondes.
  scopes: [3], // PERMISSION_JOIN_TEAM
  join_team: true
})
  .setProtectedHeader({ alg: 'HS256' })

const signupJWT = await token.sign(Buffer.from(JWTSecret, 'ascii'))
swift
import JWT

// Un secret de JWT qui a `PERMISSION_JOIN_TEAM`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"

let now = Date()

let headers = ["typ" : "JWT"]
let payload = ["iss" : JWTSharedSecretId,
               "jti" : UUID().uuidString, // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
               "iat" : NSNumber(value: now.timeIntervalSince1970), // Validité limitée à 10 minutes.
               "join_team": true, 
               "scopes": [3], // PERMISSION_JOIN_TEAM
               ] as [String : Any]
               
let signupJWT = JWT.encodePayload(payload, withSecret: JWTSharedSecret, withHeaders: headers, algorithm: JWTAlgorithm)
kotlin
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.security.Keys
import java.util.*
import javax.crypto.SecretKey

// Un secret de JWT qui a `PERMISSION_JOIN_TEAM`
val JWTSecretId: String = "JWT SHARED SECRET ID"
val JWTSecret: String = "JWT SHARED SECRET"

JWTSecretKey = Keys.hmacShaKeyFor(JWTSecret.toByteArray())
val date = Date()
val expiryDate = Date(date.time + 2 * 60 * 60 * 1000)

val signupJWT = Jwts.builder()
    .setHeaderParam("alg", "HS256")
    .setHeaderParam("typ", "JWT")
    .claim("join_team", true)
    .claim("scopes", listOf(3)) // PERMISSION_JOIN_TEAM
    .setId(UUID.randomUUID().toString()) // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(date) // Validité limitée à 10 minutes.
    .setExpiration(expiryDate)
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact()
java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import java.util.Date;
import java.util.UUID;

import javax.crypto.SecretKey;

// Un secret de JWT qui a `PERMISSION_JOIN_TEAM`
final String JWTSecretId = "JWT SHARED SECRET ID";
final String JWTSecret = "JWT SHARED SECRET";

SecretKey JWTSecretKey = Keys.hmacShaKeyFor(JWTSecret.getBytes());
Date date = new Date();
Date expiryDate = new Date(date.getTime() + 2 * 60 * 60 * 1000);

String signupJWT = Jwts.builder()
    .setHeaderParam("alg", "HS256")
    .setHeaderParam("typ", "JWT")
    .claim("join_team", true)
    .claim("scopes", List.of(3)) // PERMISSION_JOIN_TEAM
    .setId(UUID.randomUUID().toString()) // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
    .setIssuer(JWTSecretId)
    .setIssuedAt(date) // Validité limitée à 10 minutes.
    .setExpiration(expiryDate)
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact();
python
import jwt # Cet exemple utilise la librairie PyJWT : https://pypi.org/project/PyJWT/
from datetime import datetime

# Un secret de JWT qui a `PERMISSION_JOIN_TEAM`
jwt_shared_secret_id = "JWT SHARED SECRET ID"
jwt_shared_secret = "JWT SHARED SECRET"

signup_JWT = jwt.encode(
    {
        "iss": jwt_shared_secret_id,
        "jti": str(uuid.uuid4()), # Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
        "iat": datetime.now(), # Validité limitée à 10 minutes
        "scopes": [3], # PERMISSION_JOIN_TEAM
        "join_team": True,
    },
    jwt_shared_secret,
    algorithm="HS256",
)
go
package main

import (
	"crypto/rand"
	"encoding/base64"
	"github.com/dgrijalva/jwt-go"
	"time"
)

// Un secret de JWT qui a `PERMISSION_JOIN_TEAM`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"

type ConnectorAdd struct {
	Value string `json:"value,omitempty"`
	Type  string `json:"type,omitempty"`
}

type JWTPermissionScopes int

const (
	PermissionAll                    JWTPermissionScopes = -1
	PermissionAnonymousCreateSession JWTPermissionScopes = 0
	PermissionAnonymousFindKeys      JWTPermissionScopes = 1
	PermissionAnonymousFindSigchain  JWTPermissionScopes = 2
	PermissionJoinTeam               JWTPermissionScopes = 3
	PermissionAddConnector           JWTPermissionScopes = 4
	PermissionAnonymousFindSymEncKey JWTPermissionScopes = 5
)

type CustomClaims struct {
	Recipients   []string              `json:"recipients,omitempty"`
	Owner        string                `json:"owner,omitempty"`
	JoinTeam     bool                  `json:"join_team,omitempty"`
	ConnectorAdd ConnectorAdd          `json:"connector_add,omitempty"`
	SymEncKeys   []string              `json:"sym_enc_keys,omitempty"`
	Scopes       []JWTPermissionScopes `json:"scopes"`
	jwt.StandardClaims
}

func randomString() string {
	bytes := make([]byte, 32)
	_, err := rand.Read(bytes)
	if err != nil {
		panic("Failed to generate a random string")
	}
	return base64.StdEncoding.EncodeToString(bytes)
}

func main() {
	claims := CustomClaims{
		Scopes:         []JWTPermissionScopes{PermissionJoinTeam},
		JoinTeam:       true,
		StandardClaims: jwt.StandardClaims{
			Id:       randomString(),
			Issuer:   JWTSecretId,
			IssuedAt: time.Now().Unix(),
		},
	}
	signupJWT, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(JWTSecret))
}
ruby
require 'jose'

# Un secret de JWT qui a `PERMISSION_JOIN_TEAM`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

payload = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
  iat: Time.now.to_i, # Validité limitée à 10 minutes.
  scopes: [3], # PERMISSION_JOIN_TEAM
  join_team: true
}

jwk = JOSE::JWK.from_oct(jwt_secret)
jws = JOSE::JWS.sign(jwk, payload.to_json, { "alg" => "HS256" })
signupJWT = jws.compact
php
// using  web-token/jwt-framework and web-token/jwt-core libraries

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\KeyManagement\JWKFactory;
use Jose\Component\Signature\Algorithm\HS256;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\Signature\Serializer\CompactSerializer;

// Un secret de JWT qui a `PERMISSION_JOIN_TEAM`
$JWTSecretId = "JWT SHARED SECRET ID";
$JWTSecret = "JWT SHARED SECRET";

$random = bin2hex(random_bytes(16));

$algorithmManager = new AlgorithmManager([
    new HS256(),
]);

$jwsBuilder = new JWSBuilder($algorithmManager);

$payload = json_encode([
    'iss' => $JWTSecretId,
    'jti' => $random,// Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter.
    'iat' => time(), // Validité limitée à 10 minutes.
    'scopes' => [3], // PERMISSION_JOIN_TEAM
    'join_team' => true,
]);

$jwk = JWKFactory::createFromSecret(
    $JWTSecret,
    [
        'alg' => 'HS256',
        'use' => 'sig'
    ]
);
$jws = $jwsBuilder
    ->create()
    ->withPayload($payload)
    ->addSignature($jwk, ['alg' => 'HS256'])
    ->build();
$serializer = new CompactSerializer();

$signupJWT = $serializer->serialize($jws, 0);

Exemple d'utilisation du JWT, coté client :

javascript
import SealdSDK from '@seald-io/sdk'

const sdk = SealdSDK({ appId })
await sdk.initiateIdentity({ signupJWT: jwt })
swift
import SealdSdk

let sdk = try! SealdSdk.init(apiUrl: apiURL,
                              appId: appId,
                              dbPath: "PATH TO LOCAL DATABASE",
                              dbb64SymKey: databaseEncryptionKeyB64,
                              instanceName: "instanceName",
                              logLevel: -1,
                              logNoColor: true,
                              encryptionSessionCacheTTL: 0,
                              keySize: 4096)

try! sdk.createAccount(withSignupJwt: signupJWT, deviceName: "Device name", displayName: "User name", expireAfter: 5 * 365 * 24 * 60 * 60)
objc
#import <SealdSdk/SealdSdk.h>

SealdSdk *sdk1 = [[SealdSdk alloc] initWithApiUrl:sealdCredentials->apiURL appId:sealdCredentials->appId dbPath:[NSString stringWithFormat:@"%@/inst1", sealdDir] dbb64SymKey:databaseEncryptionKeyB64 instanceName:@"User1" logLevel:0 logNoColor:true encryptionSessionCacheTTL:0 keySize:4096 error:&error];

[sdk1 createAccountWithSignupJwt:[jwtbuilder signupJWT] deviceName:@"Device name" displayName:@"User name" expireAfter:0 error:&error];
kotlin
import io.seald.seald_sdk.SealdSDK

val sdk1 = SealdSDK(apiURL, appId, "$path/sdk1", databaseEncryptionKeyB64, instanceName = "instanceName", logLevel = -1)
val user1AccountInfo = sdk1.createAccount(signupJWT, "User name", "Device name")
go
import (
  "go-seald-sdk/sdk"
)

initOptions := &sdk.InitializeOptions{
  ApiURL:                    credentials.ApiUrl,
  Database:                  &sdk.MemoryStorage{},
  KeySize:                   1024,
  AppId:                     credentials.AppId,
  EncryptionSessionCacheTTL: 24 * time.Hour,
  LogLevel:                  zerolog.TraceLevel,
}

sdk, err := sdk.Initialize(initOptions)

account.CreateAccount(&sdk.CreateAccountOptions{
  DisplayName: "User name",
  DeviceName:  "Device name",
  SignupJWT:   signupJWT, // Le JWT généré par le backend.
  ExpireAfter: time.Hour * 24 * 365 * 5,
})

JWT d'ajout de connector

Sur votre serveur, exemple de création d'un JWT permettant d'ajouter un connector à une identité :

javascript
import { SignJWT } from 'jose'

// Un secret de JWT qui a `PERMISSION_ADD_CONNECTOR`
const JWTSecretId = 'JWT SHARED SECRET ID'
const JWTSecret = 'JWT SHARED SECRET'

const token = new SignJWT({
  iss: JWTSecretId,
  jti: random(), // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
  iat: Math.floor(Date.now() / 1000), // Validité limitée à 10 minutes. `Date.now()` donne la date en millisecondes, il faut la date en secondes.
  scopes: [4], // PERMISSION_ADD_CONNECTOR
  connector_add: {
    value: customId + '@' + appId,
    type: 'AP'
  }
})
  .setProtectedHeader({ alg: 'HS256' })

const connectorJWT = await token.sign(Buffer.from(JWTSecret, 'ascii'))
swift
import JWT

// Un secret de JWT qui a `PERMISSION_ADD_CONNECTOR`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"

let now = Date()

let headers = ["typ" : "JWT"]
let payload = ["iss" : JWTSharedSecretId,
               "jti" : UUID().uuidString, // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
               "iat" : NSNumber(value: now.timeIntervalSince1970), // Validité limitée à 10 minutes.
               "scopes": [4], // PERMISSION_ADD_CONNECTOR
               "connector_add": ["type": "AP", "value": "\(customId)@\(appId)"],
               ] as [String : Any]
               
let connectorJWT = JWT.encodePayload(payload, withSecret: JWTSharedSecret, withHeaders: headers, algorithm: JWTAlgorithm)
kotlin
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.security.Keys
import java.util.*
import javax.crypto.SecretKey

// Un secret de JWT qui a `PERMISSION_ADD_CONNECTOR`
val JWTSecretId: String = "JWT SHARED SECRET ID"
val JWTSecret: String = "JWT SHARED SECRET"

JWTSecretKey = Keys.hmacShaKeyFor(JWTSecret.toByteArray())
val date = Date()
val expiryDate = Date(date.time + 2 * 60 * 60 * 1000)

val connectorJWT = Jwts.builder()
    .setHeaderParam("alg", "HS256")
    .setHeaderParam("typ", "JWT")
    .claim("join_team", true)
    .claim("scopes", listOf(4)) // PERMISSION_ADD_CONNECTOR
    .claim("connector_add", mapOf("type" to "AP", "value" to "$customId@$appId"))
    .setId(UUID.randomUUID().toString()) // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(date) // Validité limitée à 10 minutes.
    .setExpiration(expiryDate)
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact()
java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import java.util.Date;
import java.util.UUID;

import javax.crypto.SecretKey;

// Un secret de JWT qui a `PERMISSION_ADD_CONNECTOR`
final String JWTSecretId = "JWT SHARED SECRET ID";
final String JWTSecret = "JWT SHARED SECRET";

SecretKey JWTSecretKey = Keys.hmacShaKeyFor(JWTSecret.getBytes());
Date date = new Date();
Date expiryDate = new Date(date.getTime() + 2 * 60 * 60 * 1000);

String connectorJWT = Jwts.builder()
    .setHeaderParam("alg", "HS256")
    .setHeaderParam("typ", "JWT")
    .claim("scopes", List.of(4)) // PERMISSION_ADD_CONNECTOR
    .claim("connector_add", new JSONObject()
        .put("value", customId + "@" + appId)
        .put("type", "AP")
    )
    .setId(UUID.randomUUID().toString()) // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
    .setIssuer(JWTSecretId)
    .setIssuedAt(date) // Validité limitée à 10 minutes.
    .setExpiration(expiryDate)
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact();
python
import jwt # Cet exemple utilise la librairie PyJWT : https://pypi.org/project/PyJWT/
from datetime import datetime

# Un secret de JWT qui a `PERMISSION_ADD_CONNECTOR`
jwt_shared_secret_id = "JWT SHARED SECRET ID"
jwt_shared_secret = "JWT SHARED SECRET"

connector_JWT = jwt.encode(
    {
        "iss": jwt_shared_secret_id,
        "jti": str(uuid.uuid4()), # Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
        "iat": datetime.now(), # Validité limitée à 10 minutes
        "scopes": [4], # PERMISSION_ADD_CONNECTOR
        "connector_add": {
          "value": f"{customId}@{appId}",
          "type": "AP"
        }
    },
    jwt_shared_secret,
    algorithm="HS256",
)
go
package main

import (
	"crypto/rand"
	"encoding/base64"
	"github.com/dgrijalva/jwt-go"
	"time"
)

// Un secret de JWT qui a `PERMISSION_ADD_CONNECTOR`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"

type ConnectorAdd struct {
	Value string `json:"value,omitempty"`
	Type  string `json:"type,omitempty"`
}

type JWTPermissionScopes int

const (
	PermissionAll                    JWTPermissionScopes = -1
	PermissionAnonymousCreateSession JWTPermissionScopes = 0
	PermissionAnonymousFindKeys      JWTPermissionScopes = 1
	PermissionAnonymousFindSigchain  JWTPermissionScopes = 2
	PermissionJoinTeam               JWTPermissionScopes = 3
	PermissionAddConnector           JWTPermissionScopes = 4
	PermissionAnonymousFindSymEncKey JWTPermissionScopes = 5
)

type CustomClaims struct {
	Recipients   []string              `json:"recipients,omitempty"`
	Owner        string                `json:"owner,omitempty"`
	JoinTeam     bool                  `json:"join_team,omitempty"`
	ConnectorAdd ConnectorAdd          `json:"connector_add,omitempty"`
	SymEncKeys   []string              `json:"sym_enc_keys,omitempty"`
	Scopes       []JWTPermissionScopes `json:"scopes"`
	jwt.StandardClaims
}

func randomString() string {
	bytes := make([]byte, 32)
	_, err := rand.Read(bytes)
	if err != nil {
		panic("Failed to generate a random string")
	}
	return base64.StdEncoding.EncodeToString(bytes)
}

func main() {
	claims := CustomClaims{
		Scopes:         []JWTPermissionScopes{PermissionAddConnector},
		ConnectorAdd:   ConnectorAdd{Type: "AP", Value: customId + "@" + appId},
		StandardClaims: jwt.StandardClaims{
			Id:       randomString(),
			Issuer:   JWTSecretId,
			IssuedAt: time.Now().Unix(),
		},
	}
	connectorJWT, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(JWTSecret))
}
ruby
require 'jose'

# Un secret de JWT qui a `PERMISSION_ADD_CONNECTOR`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

payload = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
  iat: Time.now.to_i, # Validité limitée à 10 minutes.
  scopes: [4], # PERMISSION_ADD_CONNECTOR
  connector_add: {
    value: "#{customId}@#{appId}",
    type: 'AP'
  }
}

jwk = JOSE::JWK.from_oct(jwt_secret)
jws = JOSE::JWS.sign(jwk, payload.to_json, { "alg" => "HS256" })
connectorJWT = jws.compact
php
// using  web-token/jwt-framework and web-token/jwt-core libraries

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\KeyManagement\JWKFactory;
use Jose\Component\Signature\Algorithm\HS256;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\Signature\Serializer\CompactSerializer;

// Un secret de JWT qui a `PERMISSION_ADD_CONNECTOR`
$JWTSecretId = "JWT SHARED SECRET ID";
$JWTSecret = "JWT SHARED SECRET";

$random = bin2hex(random_bytes(16));

$algorithmManager = new AlgorithmManager([
    new HS256(),
]);

$jwsBuilder = new JWSBuilder($algorithmManager);

$payload = json_encode([
    'iss' => $JWTSecretId,
    'jti' => $random,// Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter.
    'iat' => time(), // Validité limitée à 10 minutes.
    'scopes' => [4], // PERMISSION_ADD_CONNECTOR
    'connector_add' => [
        'value' => $customId . '@' . $appId,
        'type' => 'AP'
    ],
]);

$jwk = JWKFactory::createFromSecret(
    $JWTSecret,
    [
        'alg' => 'HS256',
        'use' => 'sig'
    ]
);
$jws = $jwsBuilder
    ->create()
    ->withPayload($payload)
    ->addSignature($jwk, ['alg' => 'HS256'])
    ->build();
$serializer = new CompactSerializer();

$connectorJWT = $serializer->serialize($jws, 0);

Exemple d'utilisation du JWT, sur une identité déjà instanciée, coté client :

javascript
await sdk.pushJWT(jwt)
swift
try! sdk.pushJWT(addConnectorJWT)
objc
[sdk pushJWT:addConnectorJWT error:&error];
kotlin
sdk.pushJWT(addConnectorJWT)
go
sdk.PushJWT(joinTeamJWT)

JWTs de chiffrement anonyme

Exemple de création des JWTs de chiffrement anonyme, sur votre backend :

javascript
import { SignJWT } from 'jose'

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_KEYS` et `PERMISSION_ANONYMOUS_CREATE_SESSION`
const JWTSecretId = 'JWT SHARED SECRET ID'
const JWTSecret = 'JWT SHARED SECRET'

const getKeysToken = await new SignJWT({
  iss: JWTSecretId,
  // Pas de 'jti' pour le JWT de récupération des clés: la requête peut être paginée, donc être faite en plusieurs appels API
  iat: Math.floor(Date.now() / 1000), // Validité limitée à 10 minutes. `Date.now()` donne la date en millisecondes, il faut la date en secondes.
  scopes: [1], // PERMISSION_ANONYMOUS_FIND_KEYS
  recipients: [sealdIdUser1, sealdIdUser2]
})
  .setProtectedHeader({ alg: 'HS256' })
  .sign(Buffer.from(JWTSecret, 'ascii'))

const encryptionToken = await new SignJWT({
  iss: JWTSecretId,
  jti: random(), // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
  iat: Math.floor(Date.now() / 1000), // Validité limitée à 10 minutes. `Date.now()` donne la date en millisecondes, il faut la date en secondes.
  scopes: [0], // PERMISSION_ANONYMOUS_CREATE_SESSION
  recipients: [sealdIdUser1, sealdIdUser2],
  owner: sealdIdUser1 // nécessaire pour la création de session
})
  .setProtectedHeader({ alg: 'HS256' })
  .sign(Buffer.from(JWTSecret, 'ascii'))
swift
import JWT

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_KEYS` et `PERMISSION_ANONYMOUS_CREATE_SESSION`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"

let now = Date()

let headers = ["typ" : "JWT"]
let payloadGetKeys = ["iss" : JWTSharedSecretId,
                      // Pas de 'jti' pour le JWT de récupération des clés: la requête peut être paginée, donc être faite en plusieurs appels API
                      "iat" : NSNumber(value: now.timeIntervalSince1970), // Validité limitée à 10 minutes.
                      "scopes": [1], // PERMISSION_ANONYMOUS_FIND_KEYS
                      "recipients": [sealdIdUser1, sealdIdUser2],
                      ] as [String : Any]
let getKeysToken = JWT.encodePayload(payloadGetKeys, withSecret: JWTSharedSecret, withHeaders: headers, algorithm: JWTAlgorithm)

let payloadEncryption = ["iss" : JWTSharedSecretId,
                         "jti" : UUID().uuidString, // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
                         "iat" : NSNumber(value: now.timeIntervalSince1970), // Validité limitée à 10 minutes.
                         "scopes": [0], // PERMISSION_ANONYMOUS_CREATE_SESSION
                         "recipients": [sealdIdUser1, sealdIdUser2],
                         "owner": sealdIdUser1, // nécessaire pour la création de session
                         ] as [String : Any]
let encryptionToken = JWT.encodePayload(payloadEncryption, withSecret: JWTSharedSecret, withHeaders: headers, algorithm: JWTAlgorithm)
kotlin
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.security.Keys
import java.util.*
import javax.crypto.SecretKey

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_KEYS` et `PERMISSION_ANONYMOUS_CREATE_SESSION`
val JWTSecretId: String = "JWT SHARED SECRET ID"
val JWTSecret: String = "JWT SHARED SECRET"

JWTSecretKey = Keys.hmacShaKeyFor(JWTSecret.toByteArray())

val getKeysToken = Jwts.builder()
    .setHeaderParam("alg", "HS256")
    .setHeaderParam("typ", "JWT")
    .claim("scopes", listOf(1)) // PERMISSION_ANONYMOUS_FIND_KEYS
    .claim("recipients", listOf(sealdIdUser1, sealdIdUser2))
    // Pas de 'jti' (`.setId`) pour le JWT de récupération des clés: la requête peut être paginée, donc être faite en plusieurs appels API
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(Date()) // Validité limitée à 10 minutes.
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact()
val encryptionToken = Jwts.builder()
    .setHeaderParam("alg", "HS256")
    .setHeaderParam("typ", "JWT")
    .claim("scopes", listOf(0)) // PERMISSION_ANONYMOUS_CREATE_SESSION
    .claim("recipients", listOf(sealdIdUser1, sealdIdUser2))
    .claim("owner", sealdIdUser1) // nécessaire pour la création de session
    .setId(UUID.randomUUID().toString()) // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(Date()) // Validité limitée à 10 minutes.
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact()
java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import java.util.Date;
import java.util.UUID;

import javax.crypto.SecretKey;

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_KEYS` et `PERMISSION_ANONYMOUS_CREATE_SESSION`
final String JWTSecretId = "JWT SHARED SECRET ID";
final String JWTSecret = "JWT SHARED SECRET";

SecretKey JWTSecretKey = Keys.hmacShaKeyFor(JWTSecret.getBytes());

String getKeysToken = Jwts.builder()
    .setHeaderParam("alg", "HS256")
    .setHeaderParam("typ", "JWT")
    .claim("scopes", List.of(1)) // PERMISSION_ANONYMOUS_FIND_KEYS
    .claim("recipients", List.of(sealdIdUser1, sealdIdUser2))
    // Pas de 'jti' (`.setId`) pour le JWT de récupération des clés: la requête peut être paginée, donc être faite en plusieurs appels API
    .setIssuer(JWTSecretId)
    .setIssuedAt(new Date()) // Validité limitée à 10 minutes.
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact();
String encryptionToken = Jwts.builder()
    .setHeaderParam("alg", "HS256")
    .setHeaderParam("typ", "JWT")
    .claim("scopes", List.of(0)) // PERMISSION_ANONYMOUS_CREATE_SESSION
    .claim("recipients", List.of(sealdIdUser1, sealdIdUser2))
    .claim("owner", sealdIdUser1) // nécessaire pour la création de session
    .setId(UUID.randomUUID().toString()) // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
    .setIssuer(JWTSecretId)
    .setIssuedAt(new Date()) // Validité limitée à 10 minutes.
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact();
python
import jwt # Cet exemple utilise la librairie PyJWT : https://pypi.org/project/PyJWT/
from datetime import datetime

# Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_KEYS` et `PERMISSION_ANONYMOUS_CREATE_SESSION`
jwt_shared_secret_id = "JWT SHARED SECRET ID"
jwt_shared_secret = "JWT SHARED SECRET"

get_keys_token = jwt.encode(
    {
        "iss": jwt_shared_secret_id,
        // Pas de "jti" pour le JWT de récupération des clés: la requête peut être paginée, donc être faite en plusieurs appels API
        "iat": datetime.now(), # Validité limitée à 10 minutes
        "scopes": [1], # PERMISSION_ANONYMOUS_FIND_KEYS
        "recipients": [sealdIdUser1, sealdIdUser2],
    },
    jwt_shared_secret,
    algorithm="HS256",
)
encryption_token = jwt.encode(
    {
        "iss": jwt_shared_secret_id,
        "jti": str(uuid.uuid4()), # Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
        "iat": datetime.now(), # Validité limitée à 10 minutes
        "scopes": [0], # PERMISSION_ANONYMOUS_CREATE_SESSION
        "recipients": [sealdIdUser1, sealdIdUser2],
        "owner": sealdIdUser1, # nécessaire pour la création de session
    },
    jwt_shared_secret,
    algorithm="HS256",
)
go
package main

import (
	"crypto/rand"
	"encoding/base64"
	"github.com/dgrijalva/jwt-go"
	"time"
)

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_KEYS` et `PERMISSION_ANONYMOUS_CREATE_SESSION`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"

type ConnectorAdd struct {
	Value string `json:"value,omitempty"`
	Type  string `json:"type,omitempty"`
}

type JWTPermissionScopes int

const (
	PermissionAll                    JWTPermissionScopes = -1
	PermissionAnonymousCreateSession JWTPermissionScopes = 0
	PermissionAnonymousFindKeys      JWTPermissionScopes = 1
	PermissionAnonymousFindSigchain  JWTPermissionScopes = 2
	PermissionJoinTeam               JWTPermissionScopes = 3
	PermissionAddConnector           JWTPermissionScopes = 4
	PermissionAnonymousFindSymEncKey JWTPermissionScopes = 5
)

type CustomClaims struct {
	Recipients   []string              `json:"recipients,omitempty"`
	Owner        string                `json:"owner,omitempty"`
	JoinTeam     bool                  `json:"join_team,omitempty"`
	ConnectorAdd ConnectorAdd          `json:"connector_add,omitempty"`
	SymEncKeys   []string              `json:"sym_enc_keys,omitempty"`
	Scopes       []JWTPermissionScopes `json:"scopes"`
	jwt.StandardClaims
}

func randomString() string {
	bytes := make([]byte, 32)
	_, err := rand.Read(bytes)
	if err != nil {
		panic("Failed to generate a random string")
	}
	return base64.StdEncoding.EncodeToString(bytes)
}

func main() {
	getKeysClaims := CustomClaims{
		Scopes:     []JWTPermissionScopes{PermissionAnonymousFindKeys},
		Recipients: []string{sealdIdUser1, sealdIdUser2},
		StandardClaims: jwt.StandardClaims{
		  // Pas de "jti" (`Id`) pour le JWT de récupération des clés: la requête peut être paginée, donc être faite en plusieurs appels API
			Issuer:   JWTSecretId,
			IssuedAt: time.Now().Unix(),
		},
	}
	getKeysToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, getKeysClaims).SignedString([]byte(JWTSecret))
	encryptionClaims := CustomClaims{
		Scopes:     []JWTPermissionScopes{PermissionAnonymousCreateSession},
		Recipients: []string{sealdIdUser1, sealdIdUser2},
		Owner:      sealdIdUser1, // nécessaire pour la création de session
		StandardClaims: jwt.StandardClaims{
			Id:       randomString(),
			Issuer:   JWTSecretId,
			IssuedAt: time.Now().Unix(),
		},
	}
	encryptionToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, encryptionClaims).SignedString([]byte(JWTSecret))
}
ruby
require 'jose'

# Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_KEYS` et `PERMISSION_ANONYMOUS_CREATE_SESSION`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

get_keys_claims = {
  iss: jwt_secret_id,
  // Pas de "jti" pour le JWT de récupération des clés: la requête peut être paginée, donc être faite en plusieurs appels API
  iat: Time.now.to_i, # Validité limitée à 10 minutes.
  scopes: [1], # PERMISSION_ANONYMOUS_FIND_KEYS
  recipients: [sealdIdUser1, sealdIdUser2],
}

encryption_claims = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
  iat: Time.now.to_i, # Validité limitée à 10 minutes.
  scopes: [0], # PERMISSION_ANONYMOUS_CREATE_SESSION
  recipients: [sealdIdUser1, sealdIdUser2],
  owner: sealdIdUser1, // nécessaire pour la création de session
}

jwk = JOSE::JWK.from_oct(jwt_secret)
get_keys_token = JOSE::JWS.sign(jwk, get_keys_claims.to_json, { "alg" => "HS256" }).compact
encryption_token = JOSE::JWS.sign(jwk, encryption_claims.to_json, { "alg" => "HS256" }).compact
php
// using  web-token/jwt-framework and web-token/jwt-core libraries

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\KeyManagement\JWKFactory;
use Jose\Component\Signature\Algorithm\HS256;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\Signature\Serializer\CompactSerializer;

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_KEYS` et `PERMISSION_ANONYMOUS_CREATE_SESSION`
$JWTSecretId = "JWT SHARED SECRET ID";
$JWTSecret = "JWT SHARED SECRET";

$random = bin2hex(random_bytes(16));

$algorithmManager = new AlgorithmManager([
    new HS256(),
]);

$jwsBuilder = new JWSBuilder($algorithmManager);

$getKeysClaims = json_encode([
    'iss' => $JWTSecretId,
    // Pas de "jti" pour le JWT de récupération des clés: la requête peut être paginée, donc être faite en plusieurs appels API
    'iat' => time(), // Validité limitée à 10 minutes.
    'scopes' => [1], // PERMISSION_ANONYMOUS_FIND_KEYS
    'recipients' => [sealdIdUser1, sealdIdUser2],
]);

$encryptionClaims = json_encode([
    'iss' => $JWTSecretId,
    'jti' => $random,// Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter.
    'iat' => time(), // Validité limitée à 10 minutes.
    'scopes' => [0], // PERMISSION_ANONYMOUS_CREATE_SESSION
    'recipients' => [sealdIdUser1, sealdIdUser2],
    'owner' => sealdIdUser1, // nécessaire pour la création de session
]);

$jwk = JWKFactory::createFromSecret(
    $JWTSecret,
    [
        'alg' => 'HS256',
        'use' => 'sig'
    ]
);
$getKeysJws = $jwsBuilder
    ->create()
    ->withPayload($getKeysClaims)
    ->addSignature($jwk, ['alg' => 'HS256'])
    ->build();
$getKeysToken = (new CompactSerializer())->serialize($getKeysJws, 0);
$encryptionJws = $jwsBuilder
    ->create()
    ->withPayload($encryptionClaims)
    ->addSignature($jwk, ['alg' => 'HS256'])
    ->build();
$getKeysToken = (new CompactSerializer())->serialize($encryptionJws, 0);

Utilisation des JWTs, coté client :

javascript
const encryptedFile = await anonymousSDK.encrypt({
  getKeysToken,
  encryptionToken,
  sealdIds: [sealdIdUser1, sealdIdUser2],
  clearFile,
  filename: 'test.txt'
})
go
sessionId, encrypted, err := anonymousSDK.encrypt(
  getKeysToken,
  encryptionToken,
  []string{sealdIdUser1, sealdIdUser2},
  clearText,
  "test.txt"
)

JWT de récupération anonyme de session via SymEncKey

Sur votre serveur, exemple de création d'un JWT permettant la récupération anonyme d'une EncryptionSession via une SymEncKey.

javascript
import { SignJWT } from 'jose'

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
const JWTSecretId = 'JWT SHARED SECRET ID'
const JWTSecret = 'JWT SHARED SECRET'

const token = new SignJWT({
  iss: JWTSecretId,
  jti: random(), // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
  iat: Math.floor(Date.now() / 1000), // Validité limitée à 10 minutes. `Date.now()` donne la date en millisecondes, il faut la date en secondes.
  scopes: [5], // PERMISSION_ANONYMOUS_FIND_SYMENCKEY
  sym_enc_keys: [symEncKeyId] // ID de la SymEncKey qui sera utilisée pour récupérer la session.
})
  .setProtectedHeader({ alg: 'HS256' })

const retrieveSessionToken = await token.sign(Buffer.from(JWTSecret, 'ascii'))
swift
import JWT

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"

let now = Date()

let headers = ["typ" : "JWT"]
let payload = ["iss" : JWTSharedSecretId,
               "jti" : UUID().uuidString, // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
               "iat" : NSNumber(value: now.timeIntervalSince1970), // Validité limitée à 10 minutes.
               "scopes": [5], // PERMISSION_ANONYMOUS_FIND_SYMENCKEY
               "sym_enc_keys": [symEncKeyId], // ID de la SymEncKey qui sera utilisée pour récupérer la session.
               ] as [String : Any]
               
let retrieveSessionToken = JWT.encodePayload(payload, withSecret: JWTSharedSecret, withHeaders: headers, algorithm: JWTAlgorithm)
kotlin
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.security.Keys
import java.util.*
import javax.crypto.SecretKey

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
val JWTSecretId: String = "JWT SHARED SECRET ID"
val JWTSecret: String = "JWT SHARED SECRET"

JWTSecretKey = Keys.hmacShaKeyFor(JWTSecret.toByteArray())
val date = Date()
val expiryDate = Date(date.time + 2 * 60 * 60 * 1000)

val retrieveSessionToken = Jwts.builder()
    .setHeaderParam("alg", "HS256")
    .setHeaderParam("typ", "JWT")
    .claim("join_team", true)
    .claim("scopes", listOf(5)) // PERMISSION_ANONYMOUS_FIND_SYMENCKEY
    .claim("sym_enc_keys", listOf(symEncKeyId)) // ID de la SymEncKey qui sera utilisée pour récupérer la session.
    .setId(UUID.randomUUID().toString()) // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(date) // Validité limitée à 10 minutes.
    .setExpiration(expiryDate)
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact()
java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import java.util.Date;
import java.util.UUID;

import javax.crypto.SecretKey;

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
final String JWTSecretId = "JWT SHARED SECRET ID";
final String JWTSecret = "JWT SHARED SECRET";

SecretKey JWTSecretKey = Keys.hmacShaKeyFor(JWTSecret.getBytes());
Date date = new Date();
Date expiryDate = new Date(date.getTime() + 2 * 60 * 60 * 1000);

String retrieveSessionToken = Jwts.builder()
    .setHeaderParam("alg", "HS256")
    .setHeaderParam("typ", "JWT")
    .claim("scopes", List.of(5)) // PERMISSION_ANONYMOUS_FIND_SYMENCKEY
    .claim("sym_enc_keys", List.of(symEncKeyId)) // ID de la SymEncKey qui sera utilisée pour récupérer la session.
    .setId(UUID.randomUUID().toString()) // Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
    .setIssuer(JWTSecretId)
    .setIssuedAt(date) // Validité limitée à 10 minutes.
    .setExpiration(expiryDate)
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact();
python
import jwt # Cet exemple utilise la librairie PyJWT : https://pypi.org/project/PyJWT/
from datetime import datetime

# Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
jwt_shared_secret_id = "JWT SHARED SECRET ID"
jwt_shared_secret = "JWT SHARED SECRET"

retrieve_session_token = jwt.encode(
    {
        "iss": jwt_shared_secret_id,
        "jti": str(uuid.uuid4()), # Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
        "iat": datetime.now(), # Validité limitée à 10 minutes
        "scopes": [5], # PERMISSION_ANONYMOUS_FIND_SYMENCKEY
        "sym_enc_keys": [sym_enc_key_id] # ID de la SymEncKey qui sera utilisée pour récupérer la session.
    },
    jwt_shared_secret,
    algorithm="HS256",
)
go
package main

import (
	"crypto/rand"
	"encoding/base64"
	"github.com/dgrijalva/jwt-go"
	"time"
)

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"

type ConnectorAdd struct {
	Value string `json:"value,omitempty"`
	Type  string `json:"type,omitempty"`
}

type JWTPermissionScopes int

const (
	PermissionAll                    JWTPermissionScopes = -1
	PermissionAnonymousCreateSession JWTPermissionScopes = 0
	PermissionAnonymousFindKeys      JWTPermissionScopes = 1
	PermissionAnonymousFindSigchain  JWTPermissionScopes = 2
	PermissionJoinTeam               JWTPermissionScopes = 3
	PermissionAddConnector           JWTPermissionScopes = 4
	PermissionAnonymousFindSymEncKey JWTPermissionScopes = 5
)

type CustomClaims struct {
	Recipients   []string              `json:"recipients,omitempty"`
	Owner        string                `json:"owner,omitempty"`
	JoinTeam     bool                  `json:"join_team,omitempty"`
	ConnectorAdd ConnectorAdd          `json:"connector_add,omitempty"`
	SymEncKeys   []string              `json:"sym_enc_keys,omitempty"`
	Scopes       []JWTPermissionScopes `json:"scopes"`
	jwt.StandardClaims
}

func randomString() string {
	bytes := make([]byte, 32)
	_, err := rand.Read(bytes)
	if err != nil {
		panic("Failed to generate a random string")
	}
	return base64.StdEncoding.EncodeToString(bytes)
}

func main() {
	claims := CustomClaims{
		Scopes:         []JWTPermissionScopes{PermissionAnonymousFindSymEncKey},
		SymEncKeys:     []string{symEncKeyId},
		StandardClaims: jwt.StandardClaims{
			Id:       randomString(),
			Issuer:   JWTSecretId,
			IssuedAt: time.Now().Unix(),
		},
	}
	retrieveSessionToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(JWTSecret))
}
ruby
require 'jose'

# Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

payload = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter : un UUIDv4 serait un bon choix.
  iat: Time.now.to_i, # Validité limitée à 10 minutes.
  scopes: [5], # PERMISSION_ANONYMOUS_FIND_SYMENCKEY
  sym_enc_keys: [sym_enc_key_id] # ID de la SymEncKey qui sera utilisée pour récupérer la session.
}

jwk = JOSE::JWK.from_oct(jwt_secret)
jws = JOSE::JWS.sign(jwk, payload.to_json, { "alg" => "HS256" })
retrieveSessionToken = jws.compact
php
// using  web-token/jwt-framework and web-token/jwt-core libraries

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\KeyManagement\JWKFactory;
use Jose\Component\Signature\Algorithm\HS256;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\Signature\Serializer\CompactSerializer;

// Un secret de JWT qui a `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
$JWTSecretId = "JWT SHARED SECRET ID";
$JWTSecret = "JWT SHARED SECRET";

$random = bin2hex(random_bytes(16));

$algorithmManager = new AlgorithmManager([
    new HS256(),
]);

$jwsBuilder = new JWSBuilder($algorithmManager);

$payload = json_encode([
    'iss' => $JWTSecretId,
    'jti' => $random,// Pour que le JWT ne soit utilisable qu'une seule fois. Le `random` génère un string aléatoire, avec suffisamment d'entropie pour ne pas répéter.
    'iat' => time(), // Validité limitée à 10 minutes.
    'scopes' => [5], // PERMISSION_ANONYMOUS_FIND_SYMENCKEY
    'sym_enc_keys' => [symEncKeyId], // ID de la SymEncKey qui sera utilisée pour récupérer la session.
]);

$jwk = JWKFactory::createFromSecret(
    $JWTSecret,
    [
        'alg' => 'HS256',
        'use' => 'sig'
    ]
);
$jws = $jwsBuilder
    ->create()
    ->withPayload($payload)
    ->addSignature($jwk, ['alg' => 'HS256'])
    ->build();
$serializer = new CompactSerializer();

$retrieveSessionToken = $serializer->serialize($jws, 0);

Utilisation des JWTs, coté client :

javascript
const encryptedFile = await anonymousSDK.retrieveEncryptionSession({
  appId,
  retrieveSessionToken,
  sessionId,
  symEncKeyId,
  symEncKeyPassword
})