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.
JWT as cookie
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:
Attribute | Description | Recommendation |
---|---|---|
Domain | Define which host to which the cookie will be sent. | Set to the client application domain |
HttpOnly | Prevent web browser JavaScript from accessing the cookie | Set to true |
Max-Age | Cookie expiration date | Set to expire when JWT expires |
SameSite | When to send the cookie regarding external sites | Set to Lax |
Secure | Only sent when using HTTPS | Set 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.
Want to say thanks for writing up the docs? Support the project financially? Consider becoming a GitHub Sponsor.