Skip to content

JSON Web Tokens

As a security measure, your server must allow identity creation and anonymous encryptions using JSON Web tokens.

For signup, this feature replaces the now deprecated User License Tokens.

Creating a JWTSecret

Once the JWTSecret is created, you will need to retrieve this JWTSecret and its JWTSecretId to enable your server to create JSON Web Tokens.

Each JWTSecret has permissions. For more details, see permissions.

TIP

JWTSecret is, as the name implies, secret. Please take appropriate measures to store it securely.

However, JWTSecretId is not secret.

Via the dashboard

When creating your dashboard account, a JWT secret including all permissions (permission: -1) is automatically created. This is displayed on the homepage.

To generate another JWT Secret, login to your admin dashboard and follow these steps:

  • Go to the settings, JWT secrets tab.
  • Click on create a secret.
  • Select the desired permissions.
  • Confirm the creation of the secret.

Once the secret is created, retrieve its ID, JWTSecretId, and its value: JWTSecret.

Programmatically

To create a JSON Web Token, you first need a JWTSecret.

You can create, list, and delete JWTSecret, which allow to create JSON Web Tokens for your team, on the DashboardAPI.

In order to create a JWTSecret, you can make a POST /dashboardapi/v2/jwtsharedsecret/, with the request body:

json
{
  "permissions": Array<Permission>
}
{
  "permissions": Array<Permission>
}

Code example

Example request to create a JWTSecret:

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

Example response:

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

Adding a connector

You can add a connector to a SDK identity. This connector is a string that specifies a user uniquely for your application. You can choose any unique identifier. Depending on the user model in your application, you can for example choose: the username, their email address, their internal ID in your application, ...

The connector must be in the format ${IDENTIFIER}@${APP_ID}. The connector type must always be 'AP'.

An identity can have multiple identifiers.

TIP

The old name of this feature, now deprecated, used to be userId. The userId used to correspond to the IDENTIFIER part of the connector format described above, without the @${APP_ID}.

For this, the JWT Secret must have the PERMISSION_ADD_CONNECTOR permission. This connector is specified in the connector_add key of the JWT.

WARNING

The connector is stored in clear text in our database. In order to minimize the data that Seald stores about your users, it is not recommended to use an email address or any other personal identifying information. The optimal is to use a UUID.

JSON Web Token permissions

Each JWTSecret has permissions, and it can assign any or all of the permissions it has to each JSON Web Token it creates.

Permission are integers. Possible values are as follows:

  • PERMISSION_ALL = -1: all permissions.
  • PERMISSION_ANONYMOUS_CREATE_SESSION = 0: allows creating JSON Web Tokens which can anonymously create an Encryption Session.
  • PERMISSION_ANONYMOUS_FIND_KEYS = 1: allows creating JSON Web Tokens which can retrieve the recipients' encryption keys.
  • PERMISSION_ANONYMOUS_FIND_SIGCHAIN = 2: allows creating JSON Web Tokens which can retrieve the recipients' SigChain. Unused.
  • PERMISSION_JOIN_TEAM = 3 : allows creating JSON Web Tokens which can add an SDK identity to your team.
  • PERMISSION_ADD_CONNECTOR = 4 : allows creating JSON Web Tokens which can add a custom userId to an SDK identity.
  • PERMISSION_ANONYMOUS_FIND_SYMENCKEY = 5: allows creating JSON Web Tokens which can retrieve an Encryption Session anonymously via a SymEncKey.

Creating a JSON Web Token

The JSON Web Token, or JWT, is created with the JWTSecret and its JWTSecretId: it has the JWTSecretId as iss, and is signed with the JWTSecret with the HS256 algorithm.

Its payload is:

NameTypeUseDescription
issstringAlways"Issuer" : the JWTSecretId
iatnumberAlways"Issued at" : timestamp (in seconds) of the JWT creation. If exp is not set, the JWT expires 10 min after creation.
exp?numberAlways"Expire" : timestamp (in seconds) of when the JWT will expire. If not set, the JWT expires 10 min after creation.
jti?numberAlways"JWT ID" : unique nonce. Must never be re-used. Optional. If defined, the JWT is usable only once. If not, it is usable until its potential expiration.
scopes?Array<Permission>AlwaysList of this JWT's permissions. Optional. Must be a subset of the JWTSecret's permissions. If defined, the JWT is limited to these permissions only. If not, it has all the permissions assigned to the creating JWTSecret.
recipients?Array<string>Anonymous encryptionList of sealdIds of recipients for whom this JWT is authorized to perform operations.
owner?stringAnonymous encryptionOptional for retrieving recipient keys. Necessary for session creation. sealdId of the user who will own the created session.
join_team?booleanIdentitiesAllow the identity to join your team. Each identity can only be in one team at a time.
connector_add?{ value: connectorValue, type: 'AP' }IdentitiesAllows the addition of a custom identifier connector to an identity. The value must be of the form ${IDENTIFIER}@${APP_ID}.
sym_enc_keys?Array<string>Anonymous session retrievalRequired for anonymous session retrieval. Array of IDs of the SymEncKeys that this JWT will be allowed to use to retrieve a session.

Code examples

Signup JWT

Example of creating a JWT for signup, on your backend:

javascript
import { SignJWT } from 'jose'

// A JWT secret that has `PERMISSION_JOIN_TEAM`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"

const token = new SignJWT({
  iss: JWTSecretId,
  jti: random(), // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Math.floor(Date.now() / 1000), // JWT valid only for 10 minutes. `Date.now()` returns the timestamp in milliseconds, this needs it in seconds.
  scopes: [3], // PERMISSION_JOIN_TEAM
  join_team: true
})
  .setProtectedHeader({ alg: 'HS256' })

const signupJWT = await token.sign(Buffer.from(JWTSecret, 'ascii'))
import { SignJWT } from 'jose'

// A JWT secret that has `PERMISSION_JOIN_TEAM`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"

const token = new SignJWT({
  iss: JWTSecretId,
  jti: random(), // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Math.floor(Date.now() / 1000), // JWT valid only for 10 minutes. `Date.now()` returns the timestamp in milliseconds, this needs it in seconds.
  scopes: [3], // PERMISSION_JOIN_TEAM
  join_team: true
})
  .setProtectedHeader({ alg: 'HS256' })

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

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
               "iat" : NSNumber(value: now.timeIntervalSince1970), // JWT valid only for 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 JWT

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
               "iat" : NSNumber(value: now.timeIntervalSince1970), // JWT valid only for 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

// A JWT secret that has `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()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(date) // JWT valid only for 10 minutes.
    .setExpiration(expiryDate) 
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact()
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.security.Keys
import java.util.*
import javax.crypto.SecretKey

// A JWT secret that has `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()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(date) // JWT valid only for 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;

// A JWT secret that has `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()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSecretId)
    .setIssuedAt(date) // JWT valid only for 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;

// A JWT secret that has `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()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSecretId)
    .setIssuedAt(date) // JWT valid only for 10 minutes.
    .setExpiration(expiryDate)
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact();
python
import jwt # This example uses the PyJWT library: https://pypi.org/project/PyJWT/
from datetime import datetime

# A JWT secret that has `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()), # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
        "iat": datetime.now(), # JWT valid only for 10 minutes.
        "scopes": [3], # PERMISSION_JOIN_TEAM
        "join_team": True,
    },
    jwt_shared_secret,
    algorithm="HS256",
)
import jwt # This example uses the PyJWT library: https://pypi.org/project/PyJWT/
from datetime import datetime

# A JWT secret that has `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()), # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
        "iat": datetime.now(), # JWT valid only for 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"
)

// A JWT secret that has `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))
}
package main

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

// A JWT secret that has `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'

# A JWT secret that has `PERMISSION_JOIN_TEAM`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

payload = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Time.now.to_i, # JWT valid only for 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
require 'jose'

# A JWT secret that has `PERMISSION_JOIN_TEAM`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

payload = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Time.now.to_i, # JWT valid only for 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;

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    'iat' => time(),  // JWT valid only for 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);
// 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;

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    'iat' => time(),  // JWT valid only for 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);

JWT usage, client-side:

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

const sdk = SealdSDK({ appId })
await sdk.initiateIdentity({ signupJWT: jwt })
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)
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];
#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")
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)

_, err = account.CreateAccount(&sdk.CreateAccountOptions{
  DisplayName: "User name",
  DeviceName:  "Device name",
  SignupJWT:   signupJWT,
  ExpireAfter: time.Hour * 24 * 365 * 5,
})
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)

_, err = account.CreateAccount(&sdk.CreateAccountOptions{
  DisplayName: "User name",
  DeviceName:  "Device name",
  SignupJWT:   signupJWT,
  ExpireAfter: time.Hour * 24 * 365 * 5,
})

JWT to add a connector

On your backend, example of creating a JWT to add a connector:

javascript
import { SignJWT } from 'jose'

// A JWT secret that has `PERMISSION_ADD_CONNECTOR`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"


const token = new SignJWT({
  iss: JWTSecretId,
  jti: random(), // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Math.floor(Date.now() / 1000), // JWT valid only for 10 minutes. `Date.now()` returns the timestamp in milliseconds, this needs it in seconds.
  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 { SignJWT } from 'jose'

// A JWT secret that has `PERMISSION_ADD_CONNECTOR`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"


const token = new SignJWT({
  iss: JWTSecretId,
  jti: random(), // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Math.floor(Date.now() / 1000), // JWT valid only for 10 minutes. `Date.now()` returns the timestamp in milliseconds, this needs it in seconds.
  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

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
               "iat" : NSNumber(value: now.timeIntervalSince1970), // JWT valid only for 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 JWT

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
               "iat" : NSNumber(value: now.timeIntervalSince1970), // JWT valid only for 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

// A JWT secret that has `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()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(date) // JWT valid only for 10 minutes.
    .setExpiration(expiryDate)
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact()
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.security.Keys
import java.util.*
import javax.crypto.SecretKey

// A JWT secret that has `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()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(date) // JWT valid only for 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;

// A JWT secret that has `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()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSecretId)
    .setIssuedAt(date) // JWT valid only for 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;

// A JWT secret that has `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()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSecretId)
    .setIssuedAt(date) // JWT valid only for 10 minutes.
    .setExpiration(expiryDate)
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact();
python
import jwt # This example uses the PyJWT library: https://pypi.org/project/PyJWT/
from datetime import datetime

# A JWT secret that has `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()), # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
        "iat": datetime.now(), # JWT valid only for 10 minutes.
        "scopes": [4], # PERMISSION_ADD_CONNECTOR
        "connector_add": {
          "value": f"{customId}@{appId}",
          "type": "AP"
        }
    },
    jwt_shared_secret,
    algorithm="HS256",
)
import jwt # This example uses the PyJWT library: https://pypi.org/project/PyJWT/
from datetime import datetime

# A JWT secret that has `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()), # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
        "iat": datetime.now(), # JWT valid only for 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"
)

// A JWT secret that has `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))
}
package main

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

// A JWT secret that has `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'

# A JWT secret that has `PERMISSION_ADD_CONNECTOR`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

payload = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Time.now.to_i, # JWT valid only for 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
require 'jose'

# A JWT secret that has `PERMISSION_ADD_CONNECTOR`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

payload = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Time.now.to_i, # JWT valid only for 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;

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    'iat' => time(),  // JWT valid only for 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);
// 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;

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    'iat' => time(),  // JWT valid only for 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);

client-side: JWT usage, on an already instanciated SDK identity.

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

JWTs for anonymous encryption

On your backend, example of creating JWTs for anonymous encryption:

javascript
import { SignJWT } from 'jose'

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `PERMISSION_ANONYMOUS_CREATE_SESSION`
const JWTSecretId = 'JWT SHARED SECRET ID'
const JWTSecret = 'JWT SHARED SECRET'

const getKeysToken =  await new SignJWT({
  iss: JWTSecretId,
  // No 'jti' for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
  iat: Math.floor(Date.now() / 1000), // JWT valid only for 10 minutes. `Date.now()` returns the timestamp in milliseconds, this needs it in seconds.
  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(), // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Math.floor(Date.now() / 1000), // JWT valid only for 10 minutes. `Date.now()` returns the timestamp in milliseconds, this needs it in seconds.
  scopes: [0], // PERMISSION_ANONYMOUS_CREATE_SESSION
  recipients: [sealdIdUser1, sealdIdUser2],
  owner: sealdIdUser1, // necessary for session creation
})
  .setProtectedHeader({ alg: 'HS256' })
  .sign(Buffer.from(JWTSecret, 'ascii'))
import { SignJWT } from 'jose'

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `PERMISSION_ANONYMOUS_CREATE_SESSION`
const JWTSecretId = 'JWT SHARED SECRET ID'
const JWTSecret = 'JWT SHARED SECRET'

const getKeysToken =  await new SignJWT({
  iss: JWTSecretId,
  // No 'jti' for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
  iat: Math.floor(Date.now() / 1000), // JWT valid only for 10 minutes. `Date.now()` returns the timestamp in milliseconds, this needs it in seconds.
  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(), // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Math.floor(Date.now() / 1000), // JWT valid only for 10 minutes. `Date.now()` returns the timestamp in milliseconds, this needs it in seconds.
  scopes: [0], // PERMISSION_ANONYMOUS_CREATE_SESSION
  recipients: [sealdIdUser1, sealdIdUser2],
  owner: sealdIdUser1, // necessary for session creation
})
  .setProtectedHeader({ alg: 'HS256' })
  .sign(Buffer.from(JWTSecret, 'ascii'))
swift
import JWT

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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,
                      // No 'jti' for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
                      "iat" : NSNumber(value: now.timeIntervalSince1970), // JWT valid only for 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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
                         "iat" : NSNumber(value: now.timeIntervalSince1970), // JWT valid only for 10 minutes.
                         "scopes": [0], // PERMISSION_ANONYMOUS_CREATE_SESSION
                         "recipients": [sealdIdUser1, sealdIdUser2],
                         "owner": sealdIdUser1, // necessary for session creation
                         ] as [String : Any]
let encryptionToken = JWT.encodePayload(payloadEncryption, withSecret: JWTSharedSecret, withHeaders: headers, algorithm: JWTAlgorithm)
import JWT

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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,
                      // No 'jti' for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
                      "iat" : NSNumber(value: now.timeIntervalSince1970), // JWT valid only for 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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
                         "iat" : NSNumber(value: now.timeIntervalSince1970), // JWT valid only for 10 minutes.
                         "scopes": [0], // PERMISSION_ANONYMOUS_CREATE_SESSION
                         "recipients": [sealdIdUser1, sealdIdUser2],
                         "owner": sealdIdUser1, // necessary for session creation
                         ] 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

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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))
    // No 'jti' (`.setId`) for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(Date()) // JWT valid only for 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) // necessary for session creation
    .setId(UUID.randomUUID().toString()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(Date()) // JWT valid only for 10 minutes.
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact()
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.security.Keys
import java.util.*
import javax.crypto.SecretKey

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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))
    // No 'jti' (`.setId`) for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(Date()) // JWT valid only for 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) // necessary for session creation
    .setId(UUID.randomUUID().toString()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(Date()) // JWT valid only for 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;

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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))
    // No 'jti' (`.setId`) for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
    .setIssuer(JWTSecretId)
    .setIssuedAt(new Date()) // JWT valid only for 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) // necessary for session creation
    .setId(UUID.randomUUID().toString()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSecretId)
    .setIssuedAt(new Date()) // JWT valid only for 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;

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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))
    // No 'jti' (`.setId`) for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
    .setIssuer(JWTSecretId)
    .setIssuedAt(new Date()) // JWT valid only for 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) // necessary for session creation
    .setId(UUID.randomUUID().toString()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSecretId)
    .setIssuedAt(new Date()) // JWT valid only for 10 minutes.
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact();
python
import jwt # This example uses the PyJWT library: https://pypi.org/project/PyJWT/
from datetime import datetime

# A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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,
        // No 'jti' for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
        "iat": datetime.now(), # JWT valid only for 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()), # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
        "iat": datetime.now(), # JWT valid only for 10 minutes.
        "scopes": [0], # PERMISSION_ANONYMOUS_CREATE_SESSION
        "recipients": [sealdIdUser1, sealdIdUser2],
        "owner": sealdIdUser1, # necessary for session creation
    },
    jwt_shared_secret,
    algorithm="HS256",
)
import jwt # This example uses the PyJWT library: https://pypi.org/project/PyJWT/
from datetime import datetime

# A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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,
        // No 'jti' for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
        "iat": datetime.now(), # JWT valid only for 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()), # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
        "iat": datetime.now(), # JWT valid only for 10 minutes.
        "scopes": [0], # PERMISSION_ANONYMOUS_CREATE_SESSION
        "recipients": [sealdIdUser1, sealdIdUser2],
        "owner": sealdIdUser1, # necessary for session creation
    },
    jwt_shared_secret,
    algorithm="HS256",
)
go
package main

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

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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{
		  // No 'jti' (`Id`) for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
			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, // necessary for session creation
		StandardClaims: jwt.StandardClaims{
			Id:       randomString(),
			Issuer:   JWTSecretId,
			IssuedAt: time.Now().Unix(),
		},
	}
	encryptionToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, encryptionClaims).SignedString([]byte(JWTSecret))
}
package main

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

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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{
		  // No 'jti' (`Id`) for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
			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, // necessary for session creation
		StandardClaims: jwt.StandardClaims{
			Id:       randomString(),
			Issuer:   JWTSecretId,
			IssuedAt: time.Now().Unix(),
		},
	}
	encryptionToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, encryptionClaims).SignedString([]byte(JWTSecret))
}
ruby
require 'jose'

# A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `PERMISSION_ANONYMOUS_CREATE_SESSION`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

get_keys_claims = {
  iss: jwt_secret_id,
  // No 'jti' for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
  iat: Time.now.to_i, # JWT valid only for 10 minutes.
  scopes: [1], # PERMISSION_ANONYMOUS_FIND_KEYS
  recipients: [sealdIdUser1, sealdIdUser2],
}

encryption_claims = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Time.now.to_i, # JWT valid only for 10 minutes.
  scopes: [0], # PERMISSION_ANONYMOUS_CREATE_SESSION
  recipients: [sealdIdUser1, sealdIdUser2],
  owner: sealdIdUser1, // necessary for session creation
}

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
require 'jose'

# A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `PERMISSION_ANONYMOUS_CREATE_SESSION`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

get_keys_claims = {
  iss: jwt_secret_id,
  // No 'jti' for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
  iat: Time.now.to_i, # JWT valid only for 10 minutes.
  scopes: [1], # PERMISSION_ANONYMOUS_FIND_KEYS
  recipients: [sealdIdUser1, sealdIdUser2],
}

encryption_claims = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Time.now.to_i, # JWT valid only for 10 minutes.
  scopes: [0], # PERMISSION_ANONYMOUS_CREATE_SESSION
  recipients: [sealdIdUser1, sealdIdUser2],
  owner: sealdIdUser1, // necessary for session creation
}

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;

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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,
    // No 'jti' for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
    'iat' => time(), // JWT valid only for 10 minutes.
    'scopes' => [1], // PERMISSION_ANONYMOUS_FIND_KEYS
    'recipients' => [sealdIdUser1, sealdIdUser2],
]);

$encryptionClaims = json_encode([
    'iss' => $JWTSecretId,
    'jti' => $random, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    'iat' => time(),  // JWT valid only for 10 minutes.
    'scopes' => [0],  // PERMISSION_ANONYMOUS_CREATE_SESSION
    'recipients' => [sealdIdUser1, sealdIdUser2],
    'owner' => sealdIdUser1, // necessary for session creation
]);

$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);
// 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;

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_KEYS` & `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,
    // No 'jti' for the 'get keys' JWT: the request may be paginated, so done in multiple API calls
    'iat' => time(), // JWT valid only for 10 minutes.
    'scopes' => [1], // PERMISSION_ANONYMOUS_FIND_KEYS
    'recipients' => [sealdIdUser1, sealdIdUser2],
]);

$encryptionClaims = json_encode([
    'iss' => $JWTSecretId,
    'jti' => $random, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    'iat' => time(),  // JWT valid only for 10 minutes.
    'scopes' => [0],  // PERMISSION_ANONYMOUS_CREATE_SESSION
    'recipients' => [sealdIdUser1, sealdIdUser2],
    'owner' => sealdIdUser1, // necessary for session creation
]);

$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);

JWT usage, client-side:

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

JWT to retrieve a session anonymously via a SymEncKey

On your backend, example of creating a JWTs to retrieve an EncryptionSession anonymously via a SymEncKey:

javascript
import { SignJWT } from 'jose'

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"


const token = new SignJWT({
  iss: JWTSecretId,
  jti: random(), // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Math.floor(Date.now() / 1000), // JWT valid only for 10 minutes. `Date.now()` returns the timestamp in milliseconds, this needs it in seconds.
  scopes: [5], // PERMISSION_ANONYMOUS_FIND_SYMENCKEY
  sym_enc_keys: [symEncKeyId] // ID of the SymEncKey that will be used to retrieve the session.
})
  .setProtectedHeader({ alg: 'HS256' })

const retrieveSessionToken = await token.sign(Buffer.from(JWTSecret, 'ascii'))
import { SignJWT } from 'jose'

// A JWT secret that has `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
const JWTSecretId = "JWT SHARED SECRET ID"
const JWTSecret = "JWT SHARED SECRET"


const token = new SignJWT({
  iss: JWTSecretId,
  jti: random(), // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Math.floor(Date.now() / 1000), // JWT valid only for 10 minutes. `Date.now()` returns the timestamp in milliseconds, this needs it in seconds.
  scopes: [5], // PERMISSION_ANONYMOUS_FIND_SYMENCKEY
  sym_enc_keys: [symEncKeyId] // ID of the SymEncKey that will be used to retrieve the session.
})
  .setProtectedHeader({ alg: 'HS256' })

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

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
               "iat" : NSNumber(value: now.timeIntervalSince1970), // JWT valid only for 10 minutes.
               "scopes": [5], // PERMISSION_ANONYMOUS_FIND_SYMENCKEY
               "sym_enc_keys": [symEncKeyId], // ID of the SymEncKey that will be used to retrieve the session.
               ] as [String : Any]
               
let retrieveSessionToken = JWT.encodePayload(payload, withSecret: JWTSharedSecret, withHeaders: headers, algorithm: JWTAlgorithm)
import JWT

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
               "iat" : NSNumber(value: now.timeIntervalSince1970), // JWT valid only for 10 minutes.
               "scopes": [5], // PERMISSION_ANONYMOUS_FIND_SYMENCKEY
               "sym_enc_keys": [symEncKeyId], // ID of the SymEncKey that will be used to retrieve the 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

// A JWT secret that has `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 of the SymEncKey that will be used to retrieve the session.
    .setId(UUID.randomUUID().toString()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(date) // JWT valid only for 10 minutes.
    .setExpiration(expiryDate)
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact()
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.security.Keys
import java.util.*
import javax.crypto.SecretKey

// A JWT secret that has `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 of the SymEncKey that will be used to retrieve the session.
    .setId(UUID.randomUUID().toString()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSharedSecretId)
    .setIssuedAt(date) // JWT valid only for 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;

// A JWT secret that has `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 of the SymEncKey that will be used to retrieve the session.
    .setId(UUID.randomUUID().toString()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSecretId)
    .setIssuedAt(date) // JWT valid only for 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;

// A JWT secret that has `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 of the SymEncKey that will be used to retrieve the session.
    .setId(UUID.randomUUID().toString()) // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    .setIssuer(JWTSecretId)
    .setIssuedAt(date) // JWT valid only for 10 minutes.
    .setExpiration(expiryDate)
    .signWith(JWTSecretKey, SignatureAlgorithm.HS256)
    .compact();
python
import jwt # This example uses the PyJWT library: https://pypi.org/project/PyJWT/
from datetime import datetime

# A JWT secret that has `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()), # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
        "iat": datetime.now(), # JWT valid only for 10 minutes
        "scopes": [5], # PERMISSION_ANONYMOUS_FIND_SYMENCKEY
        "sym_enc_keys": [sym_enc_key_id] # ID of the SymEncKey that will be used to retrieve the session.
    },
    jwt_shared_secret,
    algorithm="HS256",
)
import jwt # This example uses the PyJWT library: https://pypi.org/project/PyJWT/
from datetime import datetime

# A JWT secret that has `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()), # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
        "iat": datetime.now(), # JWT valid only for 10 minutes
        "scopes": [5], # PERMISSION_ANONYMOUS_FIND_SYMENCKEY
        "sym_enc_keys": [sym_enc_key_id] # ID of the SymEncKey that will be used to retrieve the session.
    },
    jwt_shared_secret,
    algorithm="HS256",
)
go
package main

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

// A JWT secret that has `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))
}
package main

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

// A JWT secret that has `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'

# A JWT secret that has `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

payload = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Time.now.to_i, # JWT valid only for 10 minutes.
  scopes: [5], # PERMISSION_ANONYMOUS_FIND_SYMENCKEY
  sym_enc_keys: [sym_enc_key_id] # ID of the SymEncKey that will be used to retrieve the session.
}

jwk = JOSE::JWK.from_oct(jwt_secret)
jws = JOSE::JWS.sign(jwk, payload.to_json, { "alg" => "HS256" })
retrieveSessionToken = jws.compact
require 'jose'

# A JWT secret that has `PERMISSION_ANONYMOUS_FIND_SYMENCKEY`
jwt_secret_id = "JWT SHARED SECRET ID"
jwt_secret = "JWT SHARED SECRET"

payload = {
  iss: jwt_secret_id,
  jti: SecureRandom.uuid, # So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
  iat: Time.now.to_i, # JWT valid only for 10 minutes.
  scopes: [5], # PERMISSION_ANONYMOUS_FIND_SYMENCKEY
  sym_enc_keys: [sym_enc_key_id] # ID of the SymEncKey that will be used to retrieve the 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;

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    'iat' => time(),  // JWT valid only for 10 minutes.
    'scopes' => [5],  // PERMISSION_ANONYMOUS_FIND_SYMENCKEY
    'sym_enc_keys' => [symEncKeyId], // ID of the SymEncKey that will be used to retrieve the 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);
// 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;

// A JWT secret that has `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, // So the JWT is only usable once. The `random` generates a random string, with enough entropy to never repeat : a UUIDv4 would be a good choice.
    'iat' => time(),  // JWT valid only for 10 minutes.
    'scopes' => [5],  // PERMISSION_ANONYMOUS_FIND_SYMENCKEY
    'sym_enc_keys' => [symEncKeyId], // ID of the SymEncKey that will be used to retrieve the 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);

JWT usage, client-side:

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