Guide d'implémentation d'un login Magic Link
Les Magic Links fonctionnent en intégrant un Token, à usage unique et limité dans le temps, dans l’URL d’un lien d’accès envoyé par email. Ce Token est associé au compte de l’utilisateur et est vérifié par Cryptr lors de l’accès au lien, avant de rediriger l’utilisateur autorisé vers votre application. Avec cette intégration, vous embarquez dans votre code une authentification Magic Link en seulement deux requêtes API.
- Quickstart
- 15 min
Dans ce guide, nous vous accompagnerons sur l’implémentation de l’authentification par Magic Link dans votre application.
Avant de commencer
Créez votre compte Cryptr gratuitement maintenant, vous aurez ainsi les 3 éléments requis pour suivre ce guide :
- Clé d’API : Vous obtiendrez un
client_id
et unclient_secret
. Vous pouvez lire notre guide pour apprendre comment vous authentifier avec ces éléments pour utiliser l’API Cryptr. - Organization : Vous créerez votre première organisation, qui représente par exemple votre client et qui peut même être vous même pour un 1er test. En savoir plus sur l’organisation.
- Redirection : Également appelée
redirect_uri
, c’est l’URL vers laquelle votre utilisateur sera redirigé après la réussite de son authentification.
La méthode d’authentification par Magic Link est activée par défaut pour toute nouvelle Organization, vous n’avez donc rien à faire. Sachez néanmoins qu’il est possible de gérer son activation depuis votre Dashboard Cryptr.
Activez les Magic Links pour une Organization
Vous pouvez activer les Magic Links directement depuis la page d'une de vos Organization sur votre tableau de bord Cryptr.
Ce que nous allons construire ensemble
Il y a 3 étapes pour compléter un processus de Challenge Magic Link pour un utilisateur final d’une Organization
(tel qu’un employé de votre client).
- Depuis votre BackEnd, vous devez demander un MagicLinkChallenge avec le
redirect_uri
souhaité. C’est l’endroit sur votre applicatif où vous souhaitez que Cryptr redirige l’utilisateur après son authentification. Cet appel API est protégé par votre clé d’API Cryptr, vous ne devez jamais la réaliser depuis votre FrontEnd. - Le MagicLinkChallenge comprend une URL contenant un Token qui va permettre à Cryptr d’authentifier l’utilisateur final. Cette URL est à consommation unique et à durée limitée.
- Une fois l’authentification par Magic Link réussie, l’utilisateur est redirigé vers le
redirect_uri
, avec un code d’autorisation en query params nommécode
. Celui-ci vous permet de récupérer les Json Web Tokens finaux (JWT).
1. Demander un challenge Magic Link
Pour demander un challenge Magic Link, vous aurez besoin :
- De l’email de l’utilisateur
- Du
redirect_uri
: C’est l’endroit dans votre application (URL) où vous souhaitez que Cryptr redirige l’utilisateur après son authentification.
Pour utiliser les stratégies d’authentification de Cryptr, vous devez préalablement préciser les redirections à autoriser (redirect_uri
) depuis votre tableau de bord Cryptr. Si vous n’avez pas fourni de redirection dans votre demande de Challenge, la redirection par défaut sera utilisée. Cryptr vous incite lors de la création de votre compte à créer votre première redirection pour votre environnement de développement (sandbox).
Méthode 1 : Emails envoyés par Cryptr
- cURL
- Java
- JavaScript
- PHP - Guzzle
- Python
- Ruby
- C#
curl -X POST '${cryptr_service_url}/api/v2/magic-link-challenge' \
-d user_email="john@misapret.com" \
-d redirect_uri="https//example-of_url.com/welcome-back-user"
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "user_email=john@misapret.com&redirect_uri=https//example-of_url.com/welcome-back-user");
Request request = new Request.Builder()
.url("${cryptr_service_url}/api/v2/magic-link-challenge")
.method("POST", body)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("Authorization", "Bearer ...")
.build();
Response response = client.newCall(request).execute();
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
myHeaders.append("Authorization", "Bearer ...");
const urlencoded = new URLSearchParams();
urlencoded.append("user_email", "john@misapret.com");
urlencoded.append("redirect_uri", "https//example-of_url.com/welcome-back-user");
const requestOptions = {
method: "POST",
headers: myHeaders,
body: urlencoded,
redirect: "follow"
};
fetch("${cryptr_service_url}/api/v2/magic-link-challenge", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
<?php
$client = new Client();
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => 'Bearer ...'
];
$options = [
'form_params' => [
'user_email' => 'john@misapret.com',
'redirect_uri' => 'https//example-of_url.com/welcome-back-user'
]];
$request = new Request('POST', '${cryptr_service_url}/api/v2/magic-link-challenge', $headers);
$res = $client->sendAsync($request, $options)->wait();
echo $res->getBody();
import requests
url = "${cryptr_service_url}/api/v2/magic-link-challenge"
payload = 'user_email=john@misapret.com&redirect_uri=https%2F%2Fexample-of_url.com%2Fwelcome-back-user'
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ...'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
require "uri"
require "net/http"
url = URI("${cryptr_service_url}/api/v2/magic-link-challenge")
http = Net::HTTP.new(url.host, url.port);
request = Net::HTTP::Post.new(url)
request["Content-Type"] = "application/x-www-form-urlencoded"
request["Authorization"] = "Bearer ..."
request.body = "user_email=john@misapret.com&redirect_uri=https%2F%2Fexample-of_url.com%2Fwelcome-back-user"
response = http.request(request)
puts response.read_body
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "${cryptr_service_url}/api/v2/magic-link-challenge");
request.Headers.Add("Authorization", "Bearer ...");
var collection = new List<KeyValuePair<string, string>>();
collection.Add(new("user_email", "john@misapret.com"));
collection.Add(new("redirect_uri", "https//example-of_url.com/welcome-back-user"));
var content = new FormUrlEncodedContent(collection);
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
Cette demande enverra un email à l’utilisateur. Celui-ci doit cliquer sur le lien pour procéder à son authentification avant d’être redirigé vers le redirect_uri
souhaité.
Méthode 2 : Envoyer vous-même vos emails
- cURL
- Java
- JavaScript
- PHP - Guzzle
- Python
- Ruby
- C#
curl -X POST '${cryptr_service_url}/api/v2/magic-link-challenge' \
-d user_email="john@misapret.com" \
-d redirect_uri="https//example-of_url.com/welcome-back-user" \
-d send_email="false"
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "user_email=john@misapret.com&redirect_uri=https//example-of_url.com/welcome-back-user&send_email=false");
Request request = new Request.Builder()
.url("${cryptr_service_url}/api/v2/magic-link-challenge")
.method("POST", body)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("Authorization", "Bearer ...")
.build();
Response response = client.newCall(request).execute();
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
myHeaders.append("Authorization", "Bearer ...");
const urlencoded = new URLSearchParams();
urlencoded.append("user_email", "john@misapret.com");
urlencoded.append("redirect_uri", "https//example-of_url.com/welcome-back-user");
urlencoded.append("send_email", "false");
const requestOptions = {
method: "POST",
headers: myHeaders,
body: urlencoded,
redirect: "follow"
};
fetch("${cryptr_service_url}/api/v2/magic-link-challenge", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
<?php
$client = new Client();
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => 'Bearer ...'
];
$options = [
'form_params' => [
'user_email' => 'john@misapret.com',
'redirect_uri' => 'https//example-of_url.com/welcome-back-user',
'send_email' => 'false'
]];
$request = new Request('POST', '${cryptr_service_url}/api/v2/magic-link-challenge', $headers);
$res = $client->sendAsync($request, $options)->wait();
echo $res->getBody();
import requests
url = "${cryptr_service_url}/api/v2/magic-link-challenge"
payload = 'user_email=john@misapret.com&redirect_uri=https%2F%2Fexample-of_url.com%2Fwelcome-back-user&send_email=false'
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ...'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
require "uri"
require "net/http"
url = URI("${cryptr_service_url}/api/v2/magic-link-challenge")
http = Net::HTTP.new(url.host, url.port);
request = Net::HTTP::Post.new(url)
request["Content-Type"] = "application/x-www-form-urlencoded"
request["Authorization"] = "Bearer ..."
request.body = "user_email=john@misapret.com&redirect_uri=https%2F%2Fexample-of_url.com%2Fwelcome-back-user&send_email=false"
response = http.request(request)
puts response.read_body
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "${cryptr_service_url}/api/v2/magic-link-challenge");
request.Headers.Add("Authorization", "Bearer ...");
var collection = new List<KeyValuePair<string, string>>();
collection.Add(new("user_email", "john@misapret.com"));
collection.Add(new("redirect_uri", "https//example-of_url.com/welcome-back-user"));
collection.Add(new("send_email", "false"));
var content = new FormUrlEncodedContent(collection);
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
Cette demande vous fournira un lien à envoyer par email à l’utilisateur. Comme précédemment, celui-ci doit cliquer sur le lien pour procéder à son authentification avant d’être redirigé vers le redirect_uri
souhaité.
Vous pouvez également cibler la connexion Magic Link appropriée à l’aide du domaine de l’organisation. Cette façon de faire est utile dans le cas où vous auriez plusieurs Organization utilisant les mêmes domaines d’emails.
Demandez le challenge Magic Link en utilisant le domaine de l’organisation
- cURL
- Java
- JavaScript
- PHP - Guzzle
- Python
- Ruby
- C#
curl -X POST '${cryptr_service_url}/api/v2/magic-link-challenge' \
-d user_email="john@misapret.com" \
-d org_domain="misapret" \
-d redirect_uri="https//example-of_url.com/welcome-back-user"
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "user_email=john@misapret.com&redirect_uri=https//example-of_url.com/welcome-back-user&org_domain=misapret");
Request request = new Request.Builder()
.url("${cryptr_service_url}/api/v2/magic-link-challenge")
.method("POST", body)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("Authorization", "Bearer ...")
.build();
Response response = client.newCall(request).execute();
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
myHeaders.append("Authorization", "Bearer ...");
const urlencoded = new URLSearchParams();
urlencoded.append("user_email", "john@misapret.com");
urlencoded.append("redirect_uri", "https//example-of_url.com/welcome-back-user");
urlencoded.append("org_domain", "misapret");
const requestOptions = {
method: "POST",
headers: myHeaders,
body: urlencoded,
redirect: "follow"
};
fetch("${cryptr_service_url}/api/v2/magic-link-challenge", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
<?php
$client = new Client();
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => 'Bearer ...'
];
$options = [
'form_params' => [
'user_email' => 'john@misapret.com',
'redirect_uri' => 'https//example-of_url.com/welcome-back-user',
'org_domain' => 'misapret'
]];
$request = new Request('POST', '${cryptr_service_url}/api/v2/magic-link-challenge', $headers);
$res = $client->sendAsync($request, $options)->wait();
echo $res->getBody();
import requests
url = "${cryptr_service_url}/api/v2/magic-link-challenge"
payload = 'user_email=john@misapret.com&redirect_uri=https%2F%2Fexample-of_url.com%2Fwelcome-back-user&org_domain=misapret'
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ...'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
require "uri"
require "net/http"
url = URI("${cryptr_service_url}/api/v2/magic-link-challenge")
http = Net::HTTP.new(url.host, url.port);
request = Net::HTTP::Post.new(url)
request["Content-Type"] = "application/x-www-form-urlencoded"
request["Authorization"] = "Bearer ..."
request.body = "user_email=john@misapret.com&redirect_uri=https%2F%2Fexample-of_url.com%2Fwelcome-back-user&org_domain=misapret"
response = http.request(request)
puts response.read_body
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "${cryptr_service_url}/api/v2/magic-link-challenge");
request.Headers.Add("Authorization", "Bearer ...");
var collection = new List<KeyValuePair<string, string>>();
collection.Add(new("user_email", "john@misapret.com"));
collection.Add(new("redirect_uri", "https//example-of_url.com/welcome-back-user"));
collection.Add(new("org_domain", "misapret"));
var content = new FormUrlEncodedContent(collection);
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
2. Obtenir les tokens après le succès de l’authentification
Une fois que l’utilisateur s’est authentifié avec succès avec son Magic Link, il sera redirigé vers le redirect_uri
fourni préalablement. Cryptr fournira à cette occasion un code d’autorisation code
par query params pour récupérer les tokens finaux : l’access_token
et de l’id_token
. Ce dernier contient les données utilisateurs récupérées lors de l’authentification.
- cURL
- Java
- JavaScript
- PHP - Guzzle
- Python
- Ruby
- C#
# ... your user finishes their Magic Link authentication,
# In your app, the user is redirected to your service,
# via the "redirect_uri" provided when you created the challenge (or the default one),
# with the query parameter "code" that we need to fetch the tokens.
curl -X POST '${cryptr_service_url}/oauth/token' \
-d code={code} \
-d grant_type="authorization_code"
# if result.success is true, then
## 1. you get the result.access_token,
## 2. and result.id_token, which contains signed user data.
# else
## your user is unauthorized
# end
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "code=your_code&grant_type=authorization_code");
Request request = new Request.Builder()
.url("${cryptr_service_url}/oauth/token")
.method("POST", body)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.build();
Response response = client.newCall(request).execute();
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
const urlencoded = new URLSearchParams();
urlencoded.append("code", "your_code");
urlencoded.append("grant_type", "authorization_code");
const requestOptions = {
method: "POST",
headers: myHeaders,
body: urlencoded,
redirect: "follow"
};
fetch("${cryptr_service_url}/oauth/token", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
<?php
$client = new Client();
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded'
];
$options = [
'form_params' => [
'code' => 'your_code',
'grant_type' => 'authorization_code'
]];
$request = new Request('POST', '${cryptr_service_url}/oauth/token', $headers);
$res = $client->sendAsync($request, $options)->wait();
echo $res->getBody();
import requests
url = "${cryptr_service_url}/oauth/token"
payload = 'code=your_code&grant_type=authorization_code'
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
require "uri"
require "net/http"
url = URI("${cryptr_service_url}/oauth/token")
http = Net::HTTP.new(url.host, url.port);
request = Net::HTTP::Post.new(url)
request["Content-Type"] = "application/x-www-form-urlencoded"
request.body = "code=your_code&grant_type=authorization_code"
response = http.request(request)
puts response.read_body
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "${cryptr_service_url}/oauth/token");
var collection = new List<KeyValuePair<string, string>>();
collection.Add(new("code", "your_code"));
collection.Add(new("grant_type", "authorization_code"));
var content = new FormUrlEncodedContent(collection);
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
Nous répondrons à cette demande avec les Json Web Token (JWT) de l’utilisateur, un access_token
et un id_token
. Ce dernier contient les éléments d’identité de l’utilisateur.
What’s next
Pour vérifier les tokens et garantir la confiance dans les données, vous pouvez utiliser notre guide : comment valider un JWT
Vous pouvez également consulter notre référentiel d'API pour réaliser ces actions par API Rest.