Authentication#
Self-signed JWT#
Before getting started, ensure a developer account has been created for you. You will receive an email containing your private key and key ID.
Please speak with your GroupVAN integrator to receive your client ID and user IDs.
API requests are authenticated with a short-lived JWT that you sign yourself using your private key. On each request, GroupVAN looks up the public key registered to your key ID and verifies the token — there is no login or token-exchange call.
You must implement a solution for minting a JWT using the key we created during the steps above — we have included some examples below on how to do this in a few different languages. If additional help or examples are needed, please reach out to your GroupVAN integrator.
First, install a JWT library:
Open a terminal and navigate to your project directory, then run:
pip3 install PyJWT
Open a terminal and navigate to your project directory, then run:
npm install jsonwebtoken
<!-- Maven (pom.xml) -->
<dependencies>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
// Gradle (build.gradle)
dependencies {
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}
Then generate a token:
import jwt
from uuid import uuid4
from datetime import datetime, timedelta, timezone
private_key = YOUR_PRIVATE_KEY
key_id = YOUR_KEY_ID
client_id = YOUR_CLIENT_ID
user_id = YOUR_USER_ID
now = datetime.now(timezone.utc)
expires_at = now + timedelta(seconds=60)
token = jwt.encode(
{
'aud': 'groupvan',
'iss': client_id,
'kid': key_id,
'sub': user_id,
'iat': now,
'exp': expires_at,
'type': 'access',
'jti': str(uuid4())
},
private_key,
algorithm='RS256',
headers={'gv-ver': 'GV-JWT-V1'}
)
print(token)
const jwt = require('jsonwebtoken');
const { randomUUID } = require('crypto');
const privateKey = YOUR_PRIVATE_KEY; // PEM string
const keyId = YOUR_KEY_ID;
const clientId = YOUR_CLIENT_ID;
const userId = YOUR_USER_ID;
const nowSeconds = Math.floor(Date.now() / 1000);
const expiresAtSeconds = nowSeconds + 60; // 60 seconds
const payload = {
aud: 'groupvan',
iss: clientId,
kid: keyId,
sub: userId,
iat: nowSeconds,
exp: expiresAtSeconds,
type: 'access',
jti: randomUUID()
};
const token = jwt.sign(payload, privateKey, {
algorithm: 'RS256',
header: { 'gv-ver': 'GV-JWT-V1' }
});
console.log(token);
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Instant;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
public class JwtExample {
public static void main(String[] args) throws Exception {
String privateKeyPem = YOUR_PRIVATE_KEY; // PKCS#8 PEM string
String keyId = YOUR_KEY_ID;
String clientId = YOUR_CLIENT_ID;
String userId = YOUR_USER_ID;
PrivateKey privateKey = loadPrivateKeyFromPem(privateKeyPem);
Instant now = Instant.now();
Date iat = Date.from(now);
Date exp = Date.from(now.plusSeconds(60));
String token = Jwts.builder()
.setAudience("groupvan")
.setIssuer(clientId)
.setSubject(userId)
.setIssuedAt(iat)
.setExpiration(exp)
.claim("kid", keyId)
.claim("type", "access")
.setId(UUID.randomUUID().toString())
.setHeaderParam("gv-ver", "GV-JWT-V1")
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
System.out.println(token);
}
private static PrivateKey loadPrivateKeyFromPem(String pem) throws Exception {
String sanitized = pem
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s", "");
byte[] keyBytes = Base64.getDecoder().decode(sanitized.getBytes(StandardCharsets.UTF_8));
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
}
How it works#
The code builds a JWT and signs it with your private key using the RS256 algorithm. GroupVAN
verifies the signature with the public key registered to your key ID, so your private key never
leaves your system.
The token header must include gv-ver: GV-JWT-V1, which identifies the token format. Tokens
without it are rejected.
The payload claims:
Claim |
Purpose |
|---|---|
|
Audience. Always |
|
Issuer. Your client ID, identifying the application making the request. |
|
Key ID. Tells GroupVAN which public key to verify the signature with. |
|
Subject. The user ID the request acts on behalf of; must belong to your client. |
|
Issued-at and expiration times. |
|
Token type. Always |
|
A unique ID for this token (any UUID). Allows individual tokens to be revoked. |
Using the token#
Send the token in the Authorization header of each API request:
curl -H "Authorization: Bearer <token>" https://gateway.groupvan.com/json/federated/v3_2/...
Since tokens expire after 60 seconds, sign a fresh one for each request rather than caching — signing is fast and requires no network call.