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 :
{
"permissions": Array<Permission>
}
Exemple de code
Exemple de requête pour créer un JWTSecret :
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 :
{
"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 :
Nom | Type | Domaine d'utilisation | Description |
---|---|---|---|
iss | string | Général | "Issuer" : le JWTSecretId |
iat | number | Gé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? | number | Gé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? | number | Gé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éral | Liste 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 anonyme | Liste des sealdIds des destinataires pour qui ce JWT est autorisé à effectuer des opérations. |
owner? | string | Chiffrement anonyme | Optionnel 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? | boolean | Gestion des identités | Permet à 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és | Permet 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 anonyme | Né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 :
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'))
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)
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()
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();
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",
)
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))
}
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
// 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 :
import SealdSDK from '@seald-io/sdk'
const sdk = SealdSDK({ appId })
await sdk.initiateIdentity({ signupJWT: jwt })
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)
#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];
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")
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é :
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'))
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)
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()
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();
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",
)
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))
}
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
// 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 :
await sdk.pushJWT(jwt)
try! sdk.pushJWT(addConnectorJWT)
[sdk pushJWT:addConnectorJWT error:&error];
sdk.pushJWT(addConnectorJWT)
sdk.PushJWT(joinTeamJWT)
JWTs de chiffrement anonyme
Exemple de création des JWTs de chiffrement anonyme, sur votre backend :
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'))
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)
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()
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();
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",
)
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))
}
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
// 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 :
const encryptedFile = await anonymousSDK.encrypt({
getKeysToken,
encryptionToken,
sealdIds: [sealdIdUser1, sealdIdUser2],
clearFile,
filename: 'test.txt'
})
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.
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'))
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)
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()
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();
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",
)
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))
}
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
// 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 :
const encryptedFile = await anonymousSDK.retrieveEncryptionSession({
appId,
retrieveSessionToken,
sessionId,
symEncKeyId,
symEncKeyPassword
})