Skynet uses a JWT signed with an asymmetric key, just like Jitsi as a Service does.
The authorization is performed by downloading the public key advertised in the JWT's kid header from a well-known location and checking if the JWT signature and the audiences match.
Authorization can be disabled altogether by setting the
BYPASS_AUTHORIZATIONenv var totrue.
Generate an RS256 keypair with the command below and upload the public key to a web server.
ssh-keygen -m PKCS8 -b 2048 -t rsaPick a key id (kid), e.g. my-awesome-service, and rename the public key to its SHA256 sum followed by the .pem extension.
echo -n "my-awesome-service" | shasum -a 256
# cf83fb2ffe64d959f93c3ade60a1c45421f016be3dcbbeda9ea7f1b78afdb698 -So you would rename id_rsa.pub, or whatever your public key's name is, to cf83fb2ffe64d959f93c3ade60a1c45421f016be3dcbbeda9ea7f1b78afdb698.pem and upload
it to a http server of your liking.
N.B. The web service url and root path should be specified as environment variables, check
ASAP_PUB_KEYS_REPO_URLandASAP_PUB_KEYS_FOLDERin Environment Variables.
The JWT unencrypted header should look like this, note the mandatory kid with the name we picked earlier.
{
"alg": "RS256",
"kid": "my-awesome-service",
"typ": "JWT"
}The only mandatory field in the body is the aud claim, which must match at least one of the audiences defined in the ASAP_PUB_KEYS_AUDS environment variable, check Environment Variables.
Once you have your JWT, sign it with the private key and use it to access any of the services provided by Skynet.
- Skynet receives the JWT and reads the unencrypted header
- Extracts the
kidand performs aSHA256sum on the string - Attempts to download the public key from
ASAP_PUB_KEYS_REPO_URL/ASAP_PUB_KEYS_FOLDER/{sha-sum-of-kid}.pemif it cannot find it in the local cache - Verifies the signature and the audiences
Use this bash script to quickly generate JWTs. Please note that you will still need to upload the public key somewhere and generate the key pair.
./docs/jaas-jwt.sh PrivateKey.pk my-awesome-service
# The generated token has a validity of 7200 secondsprivate String getJWT() throws NoSuchAlgorithmException, InvalidKeySpecException
{
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
PrivateKey finalPrivateKey = kf.generatePrivate(keySpecPKCS8);
JwtBuilder builder = Jwts.builder()
.setHeaderParam("kid", "my-awesome-service")
.setIssuedAt(now)
.setIssuer("myCoolApp")
.setAudience("my-audience")
.signWith(SignatureAlgorithm.RS256, finalPrivateKey);
long expires = nowMillis + (60 * 60 * 1000);
Date expiry = new Date(expires);
builder.setExpiration(expiry);
return builder.compact();
}import time
import os
import sys
import jwt
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(parent_dir)
dir_path = os.path.dirname(os.path.realpath(__file__))
header = {
'alg': 'RS256',
'kid': 'my-awesome-service',
'typ': 'JWT'
}
claims = {
'iss': 'myAwesomeApp',
'exp': time.time() + 300,
'user': 'me',
'aud': 'my-audience'
}
def create_jwt(claims: dict, headers: dict, secret_key: str) -> str:
return jwt.encode(claims, secret_key, headers=headers)
with open(f'{dir_path}/keys/private.key', 'r') as f:
private_key = f.read()
jwt = create_jwt(claims, header, private_key)
print(jwt)