Overview
- Headless frontend SDK
- Backend SDK
This guide walks through integrating email magic links using Stytch’s headless frontend SDKs. This approach gives you complete control over your UI while handling authentication logic on the frontend.Both and Organization-specific authentication flows are supported.
- Discovery authentication
- Organization-specific authentication
The Discovery flow is designed for situations where end users are signing up or logging in from a central landing page, and have not specified which they are trying to access.
Prerequisites
- Enable the Frontend SDKs in your Stytch Dashboard
- Enable Create Organizations under “Enabled Methods”
Implementation
Initiate magic link email
In your application’s UI, introduce a way for end users to input their email and trigger a
magicLinks.email.discovery.send() method call with the provided email.Report incorrect code
Copy
Ask AI
import { useStytchB2BClient } from '@stytch/react/b2b';
export const Login = () => {
const stytch = useStytchB2BClient();
const sendDiscoveryEmail = () => {
stytch.magicLinks.email.discovery.send({
email_address: 'sandbox@stytch.com',
});
};
return <button onClick={sendDiscoveryEmail}>Send email</button>;
};
Handle callback
Stytch will redirect the user to the Discovery RedirectURL you specified on the Redirect URLs page earlier. Exchange the token for a list of Discovered Organizations that the user can choose to log into.
Report incorrect code
Copy
Ask AI
export const DiscoveryAuthenticate = () => {
const stytch = useStytchB2BClient();
useEffect(() => {
const authenticate = async () => {
const result = await stytch.authenticateByUrl();
if (result?.handled) {
const { email_address, discovered_organizations } = result.data;
// surface discovered organizations to user to select from
console.log({ email_address, discovered_organizations });
}
};
authenticate();
}, [stytch]);
return <div>Loading</div>;
};
An will be returned. The headless SDK will automatically set this in cookies and include it in follow-up calls.
Handle organization selection
When the end user selects an Organization to log into, call the exchange intermediate session method with the selected Organization ID.
Report incorrect code
Copy
Ask AI
import { useEffect } from 'react';
import { useStytchB2BClient } from '@stytch/react/b2b';
export const ExchangeIntermediateSession = () => {
const stytch = useStytchB2BClient();
useEffect(() => {
stytch.discovery.intermediateSessions.exchange({
organization_id: 'organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931'
});
});
return <div>Log In</div>;
};
(Optional) Support organization creation
Allow users to create a new Organization instead of logging into an existing one.
Report incorrect code
Copy
Ask AI
import { useEffect } from 'react';
import { useStytchB2BClient } from '@stytch/react/b2b';
export const CreateOrganization = () => {
const stytch = useStytchB2BClient();
useEffect(() => {
stytch.discovery.organizations.create();
});
return <div>Create Organization</div>;
};
You can optionally prompt the user for the name and slug of their new Organization. If not provided, Stytch will auto-generate them based on the end user’s email address.
If end users login via a page that indicates which Organization they’re trying to access (e.g.,
<org-slug>.your-app.com or your-app.com/team/<org-slug>), you can offer Organization-specific authentication.Prerequisites
- Enable the Frontend SDKs in your Stytch Dashboard
Implementation
Initiate magic link email
On your login page for the organization, take in a user’s email address and call the
magicLinks.email.loginOrSignup() method.Report incorrect code
Copy
Ask AI
import { useStytchB2BClient } from '@stytch/react/b2b';
export const Login = () => {
const stytch = useStytchB2BClient();
const sendEmailMagicLink = () => {
stytch.magicLinks.email.loginOrSignup({
email_address: 'sandbox@stytch.com',
organization_id: 'organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931',
});
};
return <button onClick={sendEmailMagicLink}>Send email</button>;
};
Handle callback
Stytch will redirect the user to the Login or Signup RedirectURL that you specified in the
magicLinks.email.loginOrSignup() call, or the relevant default in the Redirect URLs page of the Stytch Dashboard. The URL’s query parameters will contain stytch_token_type=multi_tenant_magic_links and an authentication token. Your application should extract the token from the URL and call the appropriate authentication method to finish the login process.Report incorrect code
Copy
Ask AI
import { useEffect } from 'react';
import { useStytchB2BClient, useStytchMemberSession } from '@stytch/react/b2b';
export const Authenticate = () => {
const stytch = useStytchB2BClient();
const { session } = useStytchMemberSession();
useEffect(() => {
if (session) {
window.location.href = 'https://example.com/profile';
} else {
stytch.authenticateByUrl({
session_duration_minutes: 60,
});
}
}, [stytch, session]);
return <div>Loading</div>;
}
This guide walks through integrating magic links using Stytch’s backend SDKs. This approach handles all authentication logic on your backend, giving you complete control over the user experience.Both and Organization-specific authentication flows are supported.
- Discovery authentication
- Organization-specific authentication
The Discovery flow is designed for situations where end users are signing up or logging in from a central landing page, and have not specified which they are trying to access.
Implementation
Initiate magic link email
Prompt the user to input their email and then call the SendDiscoveryEmail API to initiate the magic link flow.
Python
Report incorrect code
Copy
Ask AI
@app.route("/send-discovery-eml", methods=["POST"])
def send_discovery_eml() -> str:
resp = stytch_client.magic_links.email.discovery.send(
email_address=email
)
if resp.status_code != 200:
return resp.error
return "Success", 200
Handle callback
Stytch will make a callback to the
discovery Redirect URL that you specified on the Redirect URLs page of the Stytch Dashboard. Since the same Redirect URL can be used for multiple authentication methods, the stytch_token_type in the callback parameters indicates which authentication flow the token belongs to. Your application should handle checking the stytch_token_type and calling the appropriate authentication method with the token.If your RedirectURL was http://localhost:3000/discovery you would add the following route to your application:Report incorrect code
Copy
Ask AI
@app.route("/discovery", methods=["GET"])
def discovery() -> str:
token_type = request.args["stytch_token_type"]
token = request.args["token"]
if token_type != "discovery":
# add handling for other discovery token types like discovery_oauth in the future
return "Unsupported auth method", 400
resp = stytch_client.magic_links.discovery.authenticate(
discovery_magic_links_token=token
)
if resp.status_code != 200:
return resp.error
# store IST as cookie to include in org selection or creation request
session['ist'] = resp.intermediate_session_token
return resp.discovered_organizations
Handle organization selection
When the end user selects an Organization to log into, call the ExchangeIntermediateSession API with the selected
organization_id and the intermediate_session_token returned from the discovery authenticate call.Report incorrect code
Copy
Ask AI
@app.route("/login/<string:organization_id>", methods=["GET"])
def login_to_org(organization_id):
ist = session.get('ist')
if not ist:
return 500
resp = stytch_client.discovery.intermediate_sessions.exchange(
intermediate_session_token=ist,
organization_id=organization_id
)
if resp.status_code != 200:
return resp.error
# Clear IST and set stytch session
session.pop('ist', None)
session['stytch_session'] = resp.session_token
return resp.member
(Optional) Support organization creation
Allow users to create a new Organization instead of logging into an existing one.
You can optionally prompt the user for the name and slug of their new Organization. If not provided, Stytch will auto-generate them based on the end user’s email address.
Report incorrect code
Copy
Ask AI
@app.route("/create_org", methods=["GET"])
def create_org() -> str:
ist = session.get('ist')
if not ist:
return "No IST found", 400
resp = stytch_client.discovery.organizations.create(
intermediate_session_token=ist
)
if resp.status_code != 200:
return resp.error
# Clear IST and set stytch session
session.pop('ist', None)
session['stytch_session'] = resp.session_token
return resp.member
If end users login via a page that indicates which Organization they’re trying to access (e.g.,
<org-slug>.your-app.com or your-app.com/team/<org-slug>), you can offer Organization-specific authentication.Implementation
Initiate magic link email
After the user inputs their email on your Organization-specific login page, make a call to Stytch to initiate the magic link login flow with the provided email and the
organization_id they are logging into.Python
Report incorrect code
Copy
Ask AI
@app.route("/send_magic_link", methods=["POST"])
def send_magic_link_login() -> str:
email = request.form.get('email', None)
organization_id = request.form.get('organization_id', None)
if email is None or organization_id is None:
return "Email and OrgID are required", 400
resp = stytch_client.magic_links.email.login_or_signup(
email_address=email,
organization_id=organization_id
)
if resp.status_code != 200:
return "Error sending EML", 500
return "Success"
Handle callback
Stytch will make a callback to the
login or signup Redirect URL that you specified on the Redirect URLs page of the Stytch Dashboard, depending on if this is a returning user or a new user . Since the same Redirect URL can be used for multiple authentication methods, the stytch_token_type in the callback parameters indicates which authentication flow the token belongs to. Your application should handle checking the stytch_token_type and calling the appropriate authentication method with the token.If your RedirectURL was http://localhost:3000/authenticate you would add the following route to your application:Python
Report incorrect code
Copy
Ask AI
def authenticate() -> str:
token_type = request.args["stytch_token_type"]
if token_type != "multi_tenant_magic_links":
return "unsupported authentication method", 400
resp = stytch_client.magic_links.authenticate(magic_links_token=request.arg["token"])
if resp.status_code != 200:
return "something went wrong authenticating token", 500
session["stytch_session"] = resp.session_jwt
return resp.member