Embedded Link via Backend SDKs
This guide is meant for developers that are NOT using Descope Flows to design login screens and authentication methods.
If you'd like to use Descope Flows, Quick Start should be your starting point.
An embedded link generates a single-use token for authenticating an existing user. Once generated, the embedded link token can be sent to a user via various use cases such as email, SMS, etc, or you can use it manually similarly to a machine-to-machine implementation. Embedded link tokens are verified using the magic link verification function.
Backend SDK
Install SDK
npm i --save @descope/node-sdkpip3 install descopego get github.com/descope/go-sdk// Include the following in your `pom.xml` (Maven)
<dependency>
<artifactId>java-sdk</artifactId>
<groupId>com.descope</groupId>
<version>sdk-version</version> // Check https://github.com/descope/descope-java/releases for the latest versions
</dependency>gem install descopecomposer require descope/descope-phpdotnet add package descopeImport and initialize SDK
import DescopeClient from '@descope/node-sdk';
try{
// baseUrl="<URL>" // When initializing the Descope client, you can also configure the baseUrl ex: https://auth.company.com - this is useful when you utilize a custom domain within your Descope project.
const descopeClient = DescopeClient({ projectId: '__ProjectID__' });
} catch (error) {
// handle the error
console.log("failed to initialize: " + error)
}from descope import (
REFRESH_SESSION_TOKEN_NAME,
SESSION_TOKEN_NAME,
AuthException,
DeliveryMethod,
DescopeClient,
AssociatedTenant,
RoleMapping,
AttributeMapping,
LoginOptions
)
try:
# You can configure the baseURL by setting the env variable Ex: export DESCOPE_BASE_URI="https://auth.company.com - this is useful when you utilize custom domain within your Descope project."
descope_client = DescopeClient(project_id='__ProjectID__')
except Exception as error:
# handle the error
print ("failed to initialize. Error:")
print (error)import "github.com/descope/go-sdk/descope"
import "github.com/descope/go-sdk/descope/client"
// Utilizing the context package allows for the transmission of context capabilities like cancellation
// signals during the function call. In cases where context is absent, the context.Background()
// function serves as a viable alternative.
// Utilizing context within the Descope GO SDK is supported within versions 1.6.0 and higher.
import (
"context"
)
// DescopeBaseURL // within the client.Config, you can also configure the baseUrl ex: https://auth.company.com - this is useful when you utilize a custom domain within your Descope project.
descopeClient, err := client.NewWithConfig(&client.Config{ProjectID:"__ProjectID__"})
if err != nil {
// handle the error
log.Println("failed to initialize: " + err.Error())
}import com.descope.client.Config;
import com.descope.client.DescopeClient;
var descopeClient = new DescopeClient(Config.builder().projectId("__ProjectID__").build());require 'descope'
@project_id = ENV['__ProjectID__']
@client = Descope::Client.new({ project_id: @project_id})require 'vendor/autoload.php';
use Descope\SDK\DescopeSDK;
$descopeSDK = new DescopeSDK([
'projectId' => $_ENV['__ProjectID__'],
]);// appsettings.json
{
"Descope": {
"ProjectId": "__ProjectID__",
"ManagementKey": "DESCOPE_MANAGEMENT_KEY"
}
}
// Program.cs
using Descope;
using Microsoft.Extensions.Configuration;
// ... In your setup code
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var descopeProjectId = config["Descope:ProjectId"];
var descopeManagementKey = config["Descope:ManagementKey"];
var descopeConfig = new DescopeConfig(projectId: descopeProjectId);
var descopeClient = new DescopeClient(descopeConfig)
{
ManagementKey = descopeManagementKey,
};Generate Embedded Link
When authenticating via embedded link, you first need to generate the embedded link token. The code below shows how to generate the embedded link token.
Also note that signup is not complete without the user verification step below.
// Args:
// loginId: email - becomes the loginId for the user from here on and also used for delivery
const loginId = "email@company.com"
// customClaims: Additional claims to place on the jwt after verification
const customClaims = {"Key1": "Value1"}
const resp = await descopeClient.management.user.generateEmbeddedLink(loginId, customClaims);
if (!resp.ok) {
console.log("Failed to initialize signup flow")
console.log("Status Code: " + resp.code)
console.log("Error Code: " + resp.error.errorCode)
console.log("Error Description: " + resp.error.errorDescription)
console.log("Error Message: " + resp.error.errorMessage)
}
else {
console.log("Successfully initialized signup flow")
const token = resp.data.token;
console.log("Token " + token)
}# Args:
# login_id: email - becomes the login_id for the user from here on and also used for delivery
login_id = "email@company.com"
# custom_claims: Additional claims to place on the jwt after verification
custom_claims = {"Key1": "Value1"}
try:
token = descope_client.mgmt.user.generate_embedded_link(login_id=login_id, custom_claims=custom_claims)
print ("Successfully initialized signup flow")
print ("Token: " + str(token))
except AuthException as error:
print ("Failed to initialize signup flow")
print ("Status Code: " + str(error.status_code))
print ("Error: " + str(error.error_message))// Args:
// ctx: context.Context - Application context for the transmission of context capabilities like
// cancellation signals during the function call. In cases where context is absent, the context.Background()
// function serves as a viable alternative.
// Utilizing context within the Descope GO SDK is supported within versions 1.6.0 and higher.
ctx := context.Background()
// loginID: email or phone - Used as the unique ID for the user from here on and also used for delivery
loginID := "email@company.com"
// customClaims: Additional claims to place on the jwt after verification
customClaims := map[string]any{"key1":"value1"}
token, err := descopeClient.Management.User().GenerateEmbeddedLink(ctx, loginID, customClaims)
if (err != nil){
fmt.Println("Failed to initialize signup flow: ", err)
} else {
fmt.Println("Successfully initialized signup flow, token: ", token)
}// Args:
// loginID: email or phone - Used as the unique ID for the user from here on and also used for delivery
String loginId = "email@company.com";
// customClaims: Additional claims to place on the jwt after verification
Map<String, Object> customClaims = new HashMap<String, Object>() {{
put("custom-key1", "custom-value1");}}
UserService us = descopeClient.getManagementServices().getUserService();
try {
String token - us.generateEmbeddedLink("desmond@descope.com", customClaims);
} catch (DescopeException de) {
// Handle the error
}token = descope_client.generate_embedded_link(login_id: 'desmond@descope.com', custom_claims: {'key1':'value1'})Embedded Link Verification
Once the embedded token has been generated, you can send to a user via various use cases such as email, SMS, etc, or you can use it manually similarly to a machine-to-machine implementation. Embedded link tokens are verified using the magic link verification function. Below are examples of verifying the token utilizing the backend SDKs.
// Args:
// token: generated embedded link token
const token = "xxxx"
const resp = await descopeClient.magicLink.verify(token)
if (!resp.ok) {
console.log("Failed to verify user")
console.log("Status Code: " + resp.code)
console.log("Error Code: " + resp.error.errorCode)
console.log("Error Description: " + resp.error.errorDescription)
console.log("Error Message: " + resp.error.errorMessage)
}
else {
console.log("Successfully verified user")
}# Args:
# token: generated embedded link token
token = "xxxx"
try:
resp = descope_client.magiclink.verify(token=token)
print ("Successfully verified user")
print(json.dumps(resp, indent=4))
except AuthException as error:
print ("Failed to verify user")
print ("Status Code: " + str(error.status_code))
print ("Error: " + str(error.error_message))// Args:
// ctx: context.Context - Application context for the transmission of context capabilities like
// cancellation signals during the function call. In cases where context is absent, the context.Background()
// function serves as a viable alternative.
// Utilizing context within the Descope GO SDK is supported within versions 1.6.0 and higher.
ctx := context.Background()
// token: generated embedded link token
token := "xxxx"
// w: ResponseWriter to update with correct session details. You can return this to your client for setting the cookies which are used for session validation
err := descopeClient.Auth.MagicLink().Verify(ctx, token, w)
if (err != nil){
fmt.Println("Failed to verify user: ", err)
} else {
fmt.Println("Successfully verified user")
}// Args:
// token: generated embedded link token
String token = "xxxx"
MagicLinkService mls = descopeClient.getAuthenticationServices().getMagicLinkService();
try {
AuthenticationInfo info = mls.verify(token);
} catch (DescopeException de) {
// Handle the error
}# To verify an embedded link, your redirect page must call the validation function on the token (t) parameter (https://your-redirect-address.com/verify?t=<token>):
jwt_response = descope_client.magiclink_verify_token('token-here')Session Validation
The final step of completing the authentication with Descope is to validate the user session. Descope provides rich session management capabilities, including configurable session timeouts and logout functions. You can find the details and sample code for backend session validation here.
Checkpoint
Your application is now integrated with Descope. Please test with sign-up or sign-in use case.