Client Magic Link Workflow
magiclinksdev is an authentication project. It is essential to perform the correct steps in the correct order for authentication to be secure. This page describes the suggested authentication workflow for magic links.
Definitions
- Server - The instance of the magiclinksdev being used.
- Service account - An account on the server with programmatic access to the server's API.
- Client application - The application using magiclinksdev for authentication. It has a service account.
- User - A person using the client application.
Workflow
- Client application requests a magic link
- User clicks the magic link
- Client application accepts redirect
- Client application validates the JWT
- Client application treats the user as authenticated
Suggested Magic Link Workflow
Step 1: Client application requests a magic link
A user initiated action causes the client application to request a magic link from the server. Currently, there are two types of magic link requests. See 1A and 1B.
1A: Email directly to the user
The client application makes a magic link email create request. This involves specifying the values for creating the magic link, JWT, and email template.
Upon processing the magic link email create request, the server will send the magic link directly to the user via email.
It is recommended that the client application add UI text to remind the user to check their email for the magic link.
The magic link and magic link secret will be sent to the client application in the response. These values can be safely ignored. Only use them if the user should be sent the same magic link via a custom alternative secure channel. Never present the magic link or its secret to an unauthenticated user.
If a request is going to trigger an email, perform basic validation, rate limit, and use a service like reCAPTCHA to filter spam and bots.
1B: Custom delivery
The client application makes a magic link create request. This involves specifying the values for creating the magic link and JWT.
After the request is processed and the response is received, the client application is responsible for sending the magic link to the user through a custom secure channel. An example would be a mobile push notification.
To prevent confusion, it may be best to add UI text to inform the user of the following:
- The user should check the platform where the magic link was sent.
- Magic links can only be used once.
- Magic links expire after a specified time.
- Magic links should not be shared with anyone.
If the server is the magiclinksdev SaaS platform or otherwise configured to prevent robots using reCAPTCHA, add the reCAPTCHA branding visibly in the user flow. See this reCAPTCHA FAQ.
Step 2: User clicks the magic link
When a user clicks a magic link, they are directed to the server. The server will check the secret in the magic link, then craft and sign the JWT the magic link was configured for. The server will automatically redirect the user to the client application with the signed JWT in a URL query parameter.
Step 3: Client application accepts redirect
The client application must be listening via HTTP(S) at the redirect URL provided when the user clicks the magic link.
When a request is received, the user's JWT should be extracted from a URL query parameter. The default URL query
parameter is jwt
.
Step 4: Client application validates the JWT
The client application must validate the JWT properly. This is a crucial step where special attention is required. There are two documented methods to validate the user's JWT. See 4A and 4B.
4A: Validate the JWT using the JWT validation endpoint
The client application makes a jwt validate request. This involves passing the JWT from the URL query parameter to the server.
The server will verify the JWT signature, alg
header, and
registered claims. It will extract the claims as a JSON object and
return them in the response back to the client application.
If the HTTP status code from the server is 200 OK, then the client application should treat the user as authenticated. Any other HTTP status code should fail authentication.
Method 4A is not network efficient. However, it may be best for use cases where the client application does not have an adequate JWK/JWT library. It also helps shorten development time.
4B: Validate the JWT locally using the JWK Set
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.
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.
Step 5: Client application treats the user as authenticated
After the client application has validated the user's JWT, it should treat the user as authenticated. This means the user should have access to the resources they are authorized to.
For web apps, see JWT browser cookie recommendations on the implementation tips page.
Want to say thanks for writing up the docs? Support the project financially? Consider becoming a GitHub Sponsor.