Introduction

Aspera Files offers a JWT-based OAuth 2.0 grant type to enable client applications to use the Files API without a user having to log in from a web browser.

This flow utilizes an RSA keypair and the client’s public key is given to the server to verify a JWT that the client generates. These keypairs can be created on a per-client or per-user basis.

A public key defined on the client itself can be used to authenticate any user in the organization. Since no login prompt is presented, and the client will be able to act on behalf of any user, this should be used carefully only by highly trusted clients. An alternate authentication scheme may be added in the client as desired.

A public key defined on a particular user can only be used to authenticate that user. This is the preferred mode of operation, as a special user should be created for automated scripts to run as. If public keys are defined both on the client and user level, both will be tried when attempting to obtain a token.

Once a valid JWT token is presented to the server and verified, the server returns an OAuth 2.0 bearer token that can then be used by the client to make API calls.

JWT Format

A JWT consists of a JSON header, payload, and signature. When generating a JWT assertion for Files, the header should always be:

{
  "typ": "JWT",
  "alg": "RS256"
}

The payload should be in the following format:

{
  "iss": "test",
  "sub": "admin@asperasoft.com",
  "aud": "https://api.asperafiles.com/api/v1/oauth2/token",
  "nbf": 1483984874,
  "exp": 1516318468
}

Files expects these claims:

  • iss (issuer) - this should be the id of the client making the JWT. Note that this must match the client id for the OAuth client authentication.
  • sub (subject) - the username (email address) of the user to authenticate
  • aud (audience) - always the exact URL shown in the example above
  • nbf (not before) - the timestamp of the earliest validity of the JWT in Unix time format
  • exp (expiration time) - the timestamp of the latest validity of the JWT in Unix time format

The header and payload are then separately base64 encoded and separated by a “.” The header and payload are then signed using SHA256 with the RSA private key.

For more details, see https://jwt.io/introduction/ or https://tools.ietf.org/html/rfc7519.

A tool for encoding and decoding JWTs for testing purposes can be found at https://jwt.io.

Configuration

client resource

  • jwt_grant_enabled (default: false) - whether or not a particular client is allowed to use the JWT grant type
  • public_key (optional) - RSA public key in PEM format. The corresponding private key can be used to generate a JWT assertion for any user in the organization.
  • explicit_authorization_required (default: true) - whether the user needs to explicitly grant the client access via a browser dialog. Because a client using JWT grants will not utilize a web browser, this setting is ignored for the JWT grant type.

user resource

  • public_key
    (optional) - RSA public key in PEM format. The corresponding private key can be used to generate a JWT assertion for this particular user only.

Configuration via API

Here are some examples of how to configure an existing client or user via curl:

curl -i -H "Authorization: Bearer ..." -H "Content-Type: application/json" -X
PUT "https://api.asperafiles.com/api/v1/clients/test" -d '{
  "jwt_grant_enabled": true,
  "explicit_authorization_required": false,
  "public_key": "..."
}'

HTTP/1.1 204 No Content

An admin-scoped bearer token is used and ‘test’ in the URL is the client id. The bearer token needs to be obtained using another configured client, or if none exists yet, can be taken from the local storage in the web inspector of a browser logged into the Files admin view. public_key can also be passed when creating a client with a POST request.

Also note that JSON strings cannot contain literal newlines, so a multi-line public key must use “\n” in the public_key value.

Configuring a user’s public key is similar:

curl -i -H "Authorization: Bearer ..." -H "Content-Type: application/json" -X PUT "https://api.asperafiles.com/api/v1/users/479" -d '{"public_key":"..."}'

HTTP/1.1 204 No Content

Token Flow

Construct the JWT

Construct a JWT as described previously. Below is an example on how to do so from the command line.

First, encode the header using url-safe characters and omitting trailing padding (“=”) in the output:

echo -n '{"typ":"JWT","alg":"RS256"}' | base64 | tr '+' '-' | tr '/' '_' | tr -d '='
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9

Encode the payload the same way:

echo -n '{"iss":"test","sub":"admin@asperasoft.com","aud":"https://api.asperafiles.com/api/v1/oauth2/token","nbf":1483984874,"exp":1516318468}' | base64 | tr '+' '-' | tr '/' '_' | tr -d '='
eyJpc3MiOiJ0ZXN0Iiwic3ViIjoiYWRtaW5AYXNwZXJhc29mdC5jb20iLCJhdWQiOiJodHRwczovL2FwaS5hc3BlcmFmaWxlcy5jb20vYXBpL3YxL29hdXRoMi90b2tlbiIsIm5iZiI6MTQ4Mzk4NDg3NCwiZXhwIjoxNTE2MzE4NDY4fQ

Join the encoded header and encoded payload with a “.”:

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJ0ZXN0Iiwic3ViIjoiYWRtaW5AYXNwZXJhc29mdC5jb20iLCJhdWQiOiJodHRwczovL2FwaS5hc3BlcmFmaWxlcy5jb20vYXBpL3YxL29hdXRoMi90b2tlbiIsIm5iZiI6MTQ4Mzk4NDg3NCwiZXhwIjoxNTE2MzE4NDY4fQ

Using the private key (assumed to be in private_key.pem in this example), sign the combined header and payload and encode the signature:

echo -n eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJ0ZXN0Iiwic3ViIjoiYWRtaW5AYXNwZXJhc29mdC5jb20iLCJhdWQiOiJodHRwczovL2FwaS5hc3BlcmFmaWxlcy5jb20vYXBpL3YxL29hdXRoMi90b2tlbiIsIm5iZiI6MTQ4Mzk4NDg3NCwiZXhwIjoxNTE2MzE4NDY4fQ | openssl dgst -sha256 -sign private_key.pem | base64 | tr '+' '-' | tr '/' '_' | tr -d '='
io-t_tVHwA_4qGRC6uBXtjAsyxfJsBwSdAxTsocyX6oERUdRKaXFammUCdb6PmSxtaB88I912lpgIK4Wwrb55p7AFrZjKmcmBN5AZJrhPAB-J3d1M_7h8chLZUoFGS6ny5NgInRsk1I_qdTCCe9pUTNpcv7MRErvmgcNTcJqZ21nrFYlQM8DX8rezs1rwH7nDHDzA-9U5E_J9e19v-tRmTJHAxhpeeDrlvgRiS84bW3_2msF9tNSMTjcckGq4XvgLtgc4eMLN4BSLjp7Q9FKZwSxx_Ry-N_U972fo_aHXakstxGxtim8CCGWAkpu1AnoW-zrv0Kk1IXNU4qY-bwX6w

Join all the pieces together:

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJ0ZXN0Iiwic3ViIjoiYWRtaW5AYXNwZXJhc29mdC5jb20iLCJhdWQiOiJodHRwczovL2FwaS5hc3BlcmFmaWxlcy5jb20vYXBpL3YxL29hdXRoMi90b2tlbiIsIm5iZiI6MTQ4Mzk4NDg3NCwiZXhwIjoxNTE2MzE4NDY4fQ.io-t_tVHwA_4qGRC6uBXtjAsyxfJsBwSdAxTsocyX6oERUdRKaXFammUCdb6PmSxtaB88I912lpgIK4Wwrb55p7AFrZjKmcmBN5AZJrhPAB-J3d1M_7h8chLZUoFGS6ny5NgInRsk1I_qdTCCe9pUTNpcv7MRErvmgcNTcJqZ21nrFYlQM8DX8rezs1rwH7nDHDzA-9U5E_J9e19v-tRmTJHAxhpeeDrlvgRiS84bW3_2msF9tNSMTjcckGq4XvgLtgc4eMLN4BSLjp7Q9FKZwSxx_Ry-N_U972fo_aHXakstxGxtim8CCGWAkpu1AnoW-zrv0Kk1IXNU4qY-bwX6w

Call the Files token endpoint with the JWT

Request:

POST https://api.asperafiles.com/api/v1/oauth2/{org_name}/token
Authorization: Basic
dGVzdDoySFVTbnAxSTIzejhCRTM1RUZrTWlLc3RxTDhZRXhhdFZTcThiOWhDUkpVODNKUmRYTHVvdkdFay1WQllzRHJEeGtKQWxxVFBQVjNxRmdkb0tEVFFYcTVLXzJEeUFsTF8=

assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJ0ZXN0Iiwic3ViIjoiYWRtaW5AYXNwZXJhc29mdC5jb20iLCJhdWQiOiJodHRwczovL2FwaS5hc3BlcmFmaWxlcy5jb20vYXBpL3YxL29hdXRoMi90b2tlbiIsIm5iZiI6MTQ4Mzk4NDg3NCwiZXhwIjoxNTE2MzE4NDY4fQ.io-t_tVHwA_4qGRC6uBXtjAsyxfJsBwSdAxTsocyX6oERUdRKaXFammUCdb6PmSxtaB88I912lpgIK4Wwrb55p7AFrZjKmcmBN5AZJrhPAB-J3d1M_7h8chLZUoFGS6ny5NgInRsk1I_qdTCCe9pUTNpcv7MRErvmgcNTcJqZ21nrFYlQM8DX8rezs1rwH7nDHDzA-9U5E_J9e19v-tRmTJHAxhpeeDrlvgRiS84bW3_2msF9tNSMTjcckGq4XvgLtgc4eMLN4BSLjp7Q9FKZwSxx_Ry-N_U972fo_aHXakstxGxtim8CCGWAkpu1AnoW-zrv0Kk1IXNU4qY-bwX6w&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&scope=user%3Aall

Note that the basic auth is using the client id and secret. The requested scope can be user:all, admin:all, or a node scope of the format node. <access_key>:user:all.

Response:

HTTP/1.1 201 Created
{"access_token":"eJwdkNuOokAURd/5DF4dQgElFxMflIuIAtqAFzqdDpcSUcSyoFTozL8PPQ8n2VlZ2Ts5PyxtEPkuc3bCiuwftsnuGA35l06SqhoQeuOSoOY7aX8dICgcEDhBC0UwEZQJAPHgUPq/QREUkAqJzMkwyzh4gimX5ILGpZmWKTmEQMpPg12QO8XDZsNOPllhAOPhJPbrLzOdBsuFNwujD3M6ZXB109dCHirLo296RjAmqxFd1LkR39uVG5OHrZ6XzsOOjlgsna19t6o0HgeF61U94yu+BC31fUqKdiOVNJBI5p59Y7spu6KuK/jRi2htBy4Jm9IKZY1/JOpxDbGSvph3V89Sq6eWi1Rt1+4lpGnt6CiPlsg6FJ7m392NuZnLHtEF7Iid7CWxfeGNuo1C5rz33eIBQEgJwn2a2wdI67C9oWB3RbzlXC295AGYz66pEu9pYnvzC32BGswvL4b0wPRETBIM1O6lNry2Q/tqcT0fbqbTdcdTaXQ38KaR7VFYBhtdvUHB4hdPBzXMOyHRFrqJWvHSc/H01wcU87oum3KP9Og6Gq+NEGOyWs2G7/4DxMqeUg==","token_type":"bearer","expires_in":3599,"scope":"user:all"}

Use the bearer token for API calls

The bearer token obtained using this flow can be used like any other token. Note that it cannot be refreshed and a new token will need to be obtained using the same mechanism when it expires.

Appendices

Generating an RSA key pair

To generate a private key, run:

openssl genrsa -out private_key.pem 2048

To generate the public key corresponding to the private key, run:

openssl rsa -in private_key.pem -pubout -out public_key.pem

Ruby Example

Here is a code example of how to obtain a bearer token using the jwt gem:

private_key = OpenSSL::PKey::RSA.new '-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAvLhSNC6e7TqzNEoJ//CFWU/3AhmacM60yON2ykN97cpBhxs4
4W+BREuw79qPmvXttbDunO0j2aPR+PqnZAsNypu2pBN4CehAJ8UtIKXLcehd7v5l
s23S3FHnTtdOJPljCaoQOvX4/9cH85sMzbhOMpxNjQ7fnuk+Yxxwf1avunNi8HE6
i5d9ORzBs6ZS5n/jgjfe+90PiaHk5LC1FjLaZZlwp8Sx+gXCAFPh67SfQYMU5+So
26F7c1Q5geiGlmCpuDvl5xGgGnHCAqIWArqFzW8PlbjDojAMb2k0Ukpnsm92sdNX
QTFpaCqiYDHMxgzJWYVCvzKM6DpU9U2a8AoYMwIDAQABAoIBACRlK71SjGXV3z+j
U8BfQ2hRCqMGCwncY3Mmov8o+v1D91U2A8c+OODR9CKdtPo+mYRjxfDPrw1HEbSB
qoyZsSQ02YlVE95dQxooZaaZMt4Z4Ru5RiXPdU32IbTLww9QZTA08wJb85RrhOYP
Le3ysGoESNR+XNQJyXVVw8BbGHLNfx9h9tKY6cVzcVioEuD7ibolo9rj4GLFPTO8
yUV+MLUyhaC6lOfCnGfNe278wv9UNuIieZGGP8wUrZYflxFhmF+SyfHD34csOJLk
+178/G3UPNHPqBmuOoCD2ui/RfTsFM4GU7/svGNfqGDXrzwGJzpIRzw0fV6u8F0G
zReVugECgYEA457IYek0czWqMErSf8Z0g/2s5J2qrMeGqeS/7lYh4Gz2XM7R/8lz
S5aLQwBi9lH5XOSElmADs+5BHuZ/aUQLm8+omrLR7iHboD768dx5toEvEMJ49Dy+
uGu+Gp4HFgm/OwDG9Thb6SExyG6yVerygZvDNOCT62aqysMVQeJ4uwECgYEA1D/q
JLhZMBXGFR/M7V/MSQ/ER1h4XEN7LYfkCTNTq9xq38RyKq2s5ERGLOAYCRAfo+nT
puO6wecOvFM9QlPg4MHYT8ANCDcESE6HUMqiNNvnTpE1hcNrC6Y0pFX5LBpGwHrN
rcMRFZKpyCEYaA43xB3wCtJUoOpKtpM3g9fv1zMCgYEAgW8iPWMSO01R6MIzbwtX
6FKRRTNPAY84SVXVul7qRqdPdLj6+3v/fwUtTb1jiPRWW3dq2ePhVyjtiPfDjgVu
oHx2QPncnJBORpJpuq0H3Tsam2oqw5/jSBWkdZhVKTgDsTag7ILO6QPCPlqMOIUw
v3jmiSQHmT25czF53gw9YQECgYBvvgKAOInD9gKmBmr2IbREVseoJByhvmkwLFXS
vjHzzgR6MEtGys62Ene7knM0+kceHbFvMH+XLBUm8s/ShqRiz8sZGWwDAiwAAU/e
KgBBwfBB/dLmzF4j8OnQBu86186d8UktjQAGkoNOOJZH2gxj9Lr2h4tEeHTDMDhO
8jVz3wKBgEfZXXvdhihWvqqhBLxUDbfm03v3s8JpkCicFdeYiH7XyWkJgesevD/i
89G+lq8+6fYdvN8L/NrtwF1mZkjq5m4ks8WcczmKSasuAG7hCigO8knaPyZThj3O
Mg8nQ0HUP219KO3xPspPfiRfYR8ii/BbT1EQMMmYiNpL57sTOPw9
-----END RSA PRIVATE KEY-----'

Note that passing 2048 to OpenSSL::PKey::RSA.new instead of an existing key as a string will generate a new private key.

public_key = private_key.public_key

puts public_key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvLhSNC6e7TqzNEoJ//CF
WU/3AhmacM60yON2ykN97cpBhxs44W+BREuw79qPmvXttbDunO0j2aPR+PqnZAsN
ypu2pBN4CehAJ8UtIKXLcehd7v5ls23S3FHnTtdOJPljCaoQOvX4/9cH85sMzbhO
MpxNjQ7fnuk+Yxxwf1avunNi8HE6i5d9ORzBs6ZS5n/jgjfe+90PiaHk5LC1FjLa
ZZlwp8Sx+gXCAFPh67SfQYMU5+So26F7c1Q5geiGlmCpuDvl5xGgGnHCAqIWArqF
zW8PlbjDojAMb2k0Ukpnsm92sdNXQTFpaCqiYDHMxgzJWYVCvzKM6DpU9U2a8AoY
MwIDAQAB
-----END PUBLIC KEY-----

payload = {
  "iss": "test",
  "sub": "jimmy@asperasoft.com",
  "aud": "https://api.asperafiles.com/api/v1/oauth2/token",
  "nbf": 1483984874,
  "exp": 1516318468
}

jwt = JWT.encode payload, private_key, 'RS256'

puts jwt
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJ0ZXN0Iiwic3ViIjoiamltbXlAYXNwZXJhc29mdC5jb20iLCJhdWQiOiJodHRwczovL2FwaS5hc3BlcmFmaWxlcy5jb20vYXBpL3YxL29hdXRoMi90b2tlbiIsIm5iZiI6MTQ4Mzk4NDg3NCwiZXhwIjoxNTE2MzE4NDY4fQ.GAiDzqok56ffKa4MtUEO1YFartooNHu5u-oi8t-PffnPpqZjI6ySKNWJIKv93X_IeT1ADJ_lQ8j4oUcIZJGXVuYnT23SmngLPeyHoGBbJVE_cj4-3sK8Ukfo-Rb1Gij7B2p8JwY5CbL4yq9d4pE7pQgFPLtOZPf0M-ZH9PUlbYEp21lLRAy4s47Lf_2KAZRBRitHGQc7AisTRH6N768xgGs36TaaVboHjkZH2ccUqtNc1x7lm4zoeAvhln2lHWRaFpVixWE1Zi8KwK8ig1zd1RuJaS0eyZN1Ezcb-d15rpNfKb7QHJ_a7TaYx2VR1Pl8Qdc1w7-mxHwfdxLWIJtDRA

Assuming you have a client with id ‘test’ and secret ‘secret’ in the testeng organization, and a user ‘jimmy@asperasoft.com’ whose public key is set to the above public key, you can run the following curl command, where basic auth is used for the client id and secret:

curl -i -u test:secret -X POST https://api.asperafiles.com/api/v1/oauth2/testeng/token -d "assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJ0ZXN0Iiwic3ViIjoiamltbXlAYXNwZXJhc29mdC5jb20iLCJhdWQiOiJodHRwczovL2FwaS5hc3BlcmFmaWxlcy5jb20vYXBpL3YxL29hdXRoMi90b2tlbiIsIm5iZiI6MTQ4Mzk4NDg3NCwiZXhwIjoxNTE2MzE4NDY4fQ.GAiDzqok56ffKa4MtUEO1YFartooNHu5u-oi8t-PffnPpqZjI6ySKNWJIKv93X_IeT1ADJ_lQ8j4oUcIZJGXVuYnT23SmngLPeyHoGBbJVE_cj4-3sK8Ukfo-Rb1Gij7B2p8JwY5CbL4yq9d4pE7pQgFPLtOZPf0M-ZH9PUlbYEp21lLRAy4s47Lf_2KAZRBRitHGQc7AisTRH6N768xgGs36TaaVboHjkZH2ccUqtNc1x7lm4zoeAvhln2lHWRaFpVixWE1Zi8KwK8ig1zd1RuJaS0eyZN1Ezcb-d15rpNfKb7QHJ_a7TaYx2VR1Pl8Qdc1w7-mxHwfdxLWIJtDRA&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&scope=user%3Aall"

HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
{"access_token":"eJwlkFuPokAQhd/5GbzOEugLNJj4IOAFcZy0A6hsNhNoAXEQHaAB3ex/X9SnL3Xq1DlJ/RV5nVRf+UEciQDoQPwl1uxyTYbxsRhFRTFISX/Nq6T+ippBhwogkgIkpHhAGyn6CMBw8HD+DCEYxDFhkZQypEqYMV0yFJ1IUFE1kmqpliiPkqy68OtQW4uj3yLSABw0RDAagDWovqC9QAYQCIwnoPLCI0TFxuMA4qcTYYxfUF94iFDFQPzzTxiPP535euL5m+l4LJRM9eNuvUWlUUWyW2ZB3LELvwEL3VC6tRvZ3VmrW29FtUGzuTrVz4VcxzZFKBOaSC7pJLiddEzdCsyXJs30FibN/CqD7JRNXTML/LcV2gT5OQmp+R5uCx6TCq1mghded59hbeeBt9ocQt9ZFjlOF+QCWsasnW+bO9oa532xBxVJXCc2MzPTcl50CyyU2HW3fby4m01lnaJ+Cp1F2ubfzgztVSZb9kaBIS7f7w7myLzbnHag133gHyJfOB2UJDKV4/lI30u2r86mZtnzeqg4dmlR99F9syZv00mc8QombYD3mLQ/aJ2XqBK8hgYrW1ZLvGW9/M3v5WKW7/Kj638UPPIIXbY/9QQjuxu++x82Sq6e","token_type":"bearer","expires_in":899,"scope":"user:all"}

References

The OAuth 2.0 Authorization Framework
https://tools.ietf.org/html/rfc6749

Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants
https://tools.ietf.org/html/rfc7521

JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants
https://tools.ietf.org/html/rfc7523

JWT.IO
https://jwt.io

Video player

Video

×

Reset your Password Password resets are handled on the Support Site

×