Client Implementation Tips

Here are some helpful tips to keep in mind when implementing a client application for the magiclinksdev project.

Only pick one authentication method

The magiclinksdev project supports both magic links and OTPs. It is recommended that your project pick only one method to keep authentication simple for users.

Magic links are a personal favorite of our team because the authentication experience is as seamless as it gets.

OTPs have an advantage of allowing a user to log in on Device A, when they only read emails on Device B. An example would be a user logging in on a laptop, but they only check their email on their phone.

Verify the JWT aud and iss claims

Every API client needs to validate the aud claim in the JWT. This is essential to prevent a malicious service account from requesting a signed JWT for a different service. Every service account has at least one aud given on creation. The iss claims should be checked too. The iss claim is specific to the configuration of the magiclinksdev service, typically the URL.

Make API calls in a trusted environment

Only make API calls in a trusted environment. Do not expose your API key to a user-facing application, like a web browser and do not pass API responses directly to the frontend. API responses can contain secrets for magic links and OTPs.

Protect access to API calls

Before a request can trigger a magiclinksdev API call in your backend system, ensure the request is rate limited, from an authenticated user, or filtered by a service that helps prevent spam, like reCAPTCHA.

Perform basic checks on emails before sending

At a minimum, ensure requests that trigger emails are rate-limited and filtered for spam. If you are using the SaaS platform, your account may be terminated for sending emails marked as spam or sending to invalid addresses. Read through the SLA for details.

JWT claims

JWT claims must be provided in order to request a magic link. These JWT claims are stored in the service's database and encrypted by default. The claims are used when the magic link is clicked in order to create a JWT.

It is best practice to create JWT claims that do not contain sensitive information such as names, email addresses, etc. Ideally the JWT claims would only contain an opaque identifier unique to the user.

Be mindful that JWTs are signed, but not encrypted. If they are stored on user-owned devices, they can be read by anyone with access to that device. This includes web browser cookies.

JWT lifespan

The lifespan of a JWT is the amount of time the JWT is valid for. Set a JWT lifespan that is reasonably short for your use case.

The recommended JWT lifespan is between 5 and 15 minutes. This is because it is difficult for most applications to revoke access if a JWT is compromised. If a well-behaving user's JWT is about to expire, automatically issue them a new JWT to extend their session.

For web apps, storing JWTs as cookies is a common means of session management. When creating cookies that store JWTs keep these attributes in mind:

AttributeDescriptionRecommendation
DomainDefine which host to which the cookie will be sent.Set to the client application domain
HttpOnlyPrevent web browser JavaScript from accessing the cookieSet to true
Max-AgeCookie expiration dateSet to expire when JWT expires
SameSiteWhen to send the cookie regarding external sitesSet to Lax
SecureOnly sent when using HTTPSSet to true

JWT validation

The server publishes a JWK Set at /api/v2/jwks.json by default. This is a set of public keys that are used to verify JWT signatures. It would be ideal to cache this JWK Set and refresh the cache once an hour. It is essential that the JWK Set is downloaded using HTTPS.

The client application should cryptographically verify the JWT signature, alg header, and registered claims. Note that the server forces the sub claim to be empty.

If there is no suitable JWT/JWK library, or a simpler code base is desired, consider using a jwt validate request. This accomplishes the same thing, but is less network efficient.

Use the open-source Docker images for non-production environments

If you are a SaaS customer, save on credits by using the open-source Docker images in non-production environments. This also prevents sending test emails through the live system. Use the micahparks/magiclinksdevnop image to log requests without ever sending emails.

Limitations

The magiclinksdev project has intentional limitations to prevent abuse and provide a good user experience. Here are a few to keep in mind:

  • There is a server-side limit to how long Magic Links and OTPs can exist before they expire.
  • As soon as a Magic Link or OTP is used successfully, it is invalidated.
  • There is a server-side limit to how large JWT claims can be.