Security Model

We believe transparency is a key part of security. Here we lay out all the important technical security details for JustAuthenticateMe.

Public-Private Key Pairs per App

When you create an app via the JustAuthenticateMe console, we automatically generate a new public-private key pair specific to your app. No two apps share the same key pair, and no other app has access to your app's private key.

The key pairs are generated with the Nodejs crypto.generateKeyPair function. They are elliptic curve keys using the P-521 curve.

Each key pair also gets a key ID, or kid which is just a UUID to identify the key pair.

The public key and kid are truly public, and available via the JWKS endpoint as documented in the REST API docs.

You cannot access the private key for your app. This reduces the burden on you, because we handle keeping your private key a secret. Private keys are stored in an encrypted DynamoDB table with strict access control.

App IDs are public

Your app ID is safe to put in the front end of your app, as it is intended to be public. There is no risk that malicious others could reuse your app ID for their own app, because JustAuthenticateMe only sends successfully generated tokens to the redirect URL that you configure via the JustAuthenticateMe console.

Random Codes

JustAuthenticateMe uses random codes to ensure that attackers cannot guess magic links or refresh tokens. JustAuthenticateMe's random codes consist of 1024 random bits concatenated with the user's email address, all base 64 encoded. These codes are also tied with your app ID on the backend. The random bits are generated with Nodejs crypto.randomBytes.

Because each code is tied to an email and app, untargeted "spray" type attacks are not possible. 1024 bits of randomness are enough to be unguessable for many years into the future.

The core authentication strategy of JustAuthenticateMe is to send "magic links" to the user's email address. These magic links look like this:

https://api.justauthenticate.me/b19a21b4-ad9d-4fd7-88c1-1b68b825c671/authenticate?code=c3VwcG9ydEBqdXN0YXV0aGVudGljYXRlLm1l.AG0sPx7KtzNCz%2FtuRgHwbgcWHX%2FsG91pMKTQa03BLpc%2BJ2lnweg%2FvFmk6Ey7YfSWsRqyZjbOEkMoWcxq7CYEZTC0w3EnwAP%2FhaBVb2sT2VC1UJoyFfcgeJhvCuVJxvcjjZgLQZo6A83HKnuAUvlCd8Bd1KGtV8deAlvSp68uH8U%3D

Where the code query string parameter is a random code as described above. The random code makes this link unguessable. The code is only valid for 15 minutes.

Because this link is emailed to the user's email address, it means that anyone with access to that email address's inbox can access the link. This means that the security of this scheme is as strong as the access control of the user's email account. It also means that we cannot guarantee that whoever clicks the link (thereby logging in to your app) is the same person that requested the magic link. Rather, a signed ID token from JustAuthenticateMe solely means that whoever owns that token has access to the email address therein.

ID Tokens

ID Tokens issued by JustAuthenticateMe are signed with the private key generated for your app as described above, using the ES512 algorithm. Additionally, the kid of the public-private key pair is stored in the token header. When decoded the token header looks like:

{
  "alg": "ES512",
  "typ": "JWT",
  "kid": "2eb40104-dc45-4f37-b69c-937b6386b9f6"
}

The payload of the token contains the following.

{
  "email": "support@justauthenticate.me",
  "sub": "support@justauthenticate.me",
  "aud": "b19a21b4-ad9d-4fd7-88c1-1b68b825c671",
  "iss": "https://api.justauthenticate.me/b19a21b4-ad9d-4fd7-88c1-1b68b825c671",
  "jti": "0110e017-6a33-45cc-ad4b-529750b3b865",
  "token_use": "id",
  "iat": 1583001907,
  "nbf": 1583001907,
  "exp": 1583003707
}
  • The email and sub (Subject) claims are set to the user's email address.
  • The aud (Audience) claim is set to your app ID to indicate that only your app should be using this token.
  • The iss (Issuer) claim is set to https://api.justauthenticate.me/${appId} to indicate that the key was issued by JustAuthenticateMe using the private key for your app.
  • The jti (JWT ID) claim is set to a UUID.
  • The token_use claim will always be id
  • The iat (Issued At) and nbf (Not Before) claims are set to the time of token generation, to indicate that the token is not valid before that time.
  • The exp (Epiration Time) is set to the time of generation + the ID Token TTL as defined for your app in the JustAuthenticateMe console.

Refresh Tokens

A refresh token generated by JustAuthenticateMe is simply a random code as described above. A targeted online brute force attack would be necessary to guess this token, but 1024 random bits is more than enough to make even an offline attack infeasible.

Currently, refresh tokens remain valid as long as they are used more frequently than every 7 days. We recognize this may not be acceptable to particularly sensitive apps. This frequency requirement, as well as the total lifetime of refresh tokens will be configurable in an upcoming update to JustAuthenticateMe. Until that time, if this is unacceptable, we recommend not using refresh tokens.

Deleting Refresh Tokens

Deleting a refresh token means that it can no longer be used to fetch a new ID Token.

JustAuthenticateMe offers endpoints for a user to delete an existing refresh token, as well as for a user to delete all their active refresh tokens. We recommend that your app code calls the former whenever a user logs out of your app. The latter is effectively a "sign out everywhere" option, which we also recommend exposing as an action to your users.

Storing Tokens

While JustAuthenticateMe merely provides tokens to your app and doesn't mandate any particular method of storing or using them, we offer some brief guidance here.

Secure HTTP Only Cookies

Probably the most secure way for you to handle the tokens we provide is via Secure HTTP Only cookies. "Secure" cookies are only sent over HTTPS. "HTTP Only" cookies are not accessible by client side javascript and so won't be exposed even in the event of a XSS (Cross-Site Scripting) attack.

In this scheme, you will need an API endpoint listening at the Redirect URL you configure in the JustAuthenticateMe console. This endpoint will grab the query parameters, and return them to the user's browser in an appropriate Set-Cookie header with the Secure and HttpOnly flags.

Note that if you are using this method and using fetch on the frontend, you'll need to set the credentials parameter to true to ensure the cookie is sent along with your fetch requests.

Local Storage

In some cases, it may be more convenient to use localStorage to store the tokens that JustAuthenticateMe provides. This is sometimes frowned upon because localStorage is accessible by javascript and its contents would be exposed in the event of an XSS attack. While true, this won't be a significant concern for many websites, or the extra effort to mitigate this won't be worth the cost, in which case localStorage is a safe-enough place to store tokens. JustAuthenticateMe cannot tell you if your app is in this category or not. It is up to you to decide if localStorage is a reasonable location to store tokens.

Feedback

If this article is missing any important security information or if you have a concern about the security of JustAuthenticateMe, please immediately reach out to support@justauthenticate.me.