All integrations

Serverless Integration

AWS Lambda, Vercel, Cloudflare Workers, and more

Turalogin works perfectly with serverless environments. Start auth to send a magic link email, handle the callback when users click the link, and verify the token. No long-lived connections or shared state required - ideal for edge deployments.

  • Works with AWS Lambda, Vercel Functions, Cloudflare Workers
  • Magic link flow with customizable callback URL
  • Stateless - no connection pooling needed
  • Fast cold starts with minimal dependencies
  • Edge-compatible for global deployments

Implementation Examples

1. Environment Configuration

Set up your environment variables or secrets.

Environment Variables
1# Your Turalogin API key from the dashboard
2TURALOGIN_API_KEY=tl_live_xxxxxxxxxxxxx
3
4# The URL where magic links will redirect to
5# Development:
6APP_LOGIN_VALIDATION_URL=http://localhost:3000/auth/callback
7
8# Production (update for your domain):
9# APP_LOGIN_VALIDATION_URL=https://myapp.com/auth/callback

2. AWS Lambda Handler

A Lambda function that starts authentication and sends a magic link email.

handler.ts
1import { APIGatewayProxyHandler } from "aws-lambda";
2
3const TURALOGIN_API_KEY = process.env.TURALOGIN_API_KEY!;
4const TURALOGIN_URL = "https://api.turalogin.com/api/v1";
5const VALIDATION_URL = process.env.APP_LOGIN_VALIDATION_URL!;
6
7async function turaloginRequest(endpoint: string, body: object) {
8 const response = await fetch(`${TURALOGIN_URL}${endpoint}`, {
9 method: "POST",
10 headers: {
11 "Authorization": `Bearer ${TURALOGIN_API_KEY}`,
12 "Content-Type": "application/json",
13 },
14 body: JSON.stringify(body),
15 });
16
17 const data = await response.json();
18
19 if (!response.ok) {
20 throw { statusCode: response.status, body: data };
21 }
22
23 return data;
24}
25
26export const startAuth: APIGatewayProxyHandler = async (event) => {
27 try {
28 const { email } = JSON.parse(event.body || "{}");
29
30 if (!email) {
31 return {
32 statusCode: 400,
33 body: JSON.stringify({ error: "Email is required" }),
34 };
35 }
36
37 await turaloginRequest("/auth/start", {
38 email,
39 validationUrl: VALIDATION_URL // Where the magic link redirects to
40 });
41
42 return {
43 statusCode: 200,
44 headers: { "Content-Type": "application/json" },
45 body: JSON.stringify({
46 success: true,
47 message: "Check your email for the login link"
48 }),
49 };
50 } catch (error: any) {
51 return {
52 statusCode: error.statusCode || 500,
53 body: JSON.stringify(error.body || { error: "Internal error" }),
54 };
55 }
56};

3. Lambda Callback Handler

Handle the callback when users click the magic link, verify the token, and set a session cookie.

handler.ts
1export const authCallback: APIGatewayProxyHandler = async (event) => {
2 try {
3 const token = event.queryStringParameters?.token;
4
5 if (!token) {
6 return {
7 statusCode: 302,
8 headers: { "Location": "/login?error=invalid_link" },
9 body: "",
10 };
11 }
12
13 const result = await turaloginRequest("/auth/verify", { sessionId: token });
14
15 // Create your session token
16 const sessionToken = createSessionToken(result.user);
17
18 return {
19 statusCode: 302,
20 headers: {
21 "Location": "/dashboard",
22 "Set-Cookie": `session=${sessionToken}; HttpOnly; Secure; SameSite=Lax; Max-Age=604800; Path=/`,
23 },
24 body: "",
25 };
26 } catch (error: any) {
27 return {
28 statusCode: 302,
29 headers: { "Location": "/login?error=verification_failed" },
30 body: "",
31 };
32 }
33};
34
35function createSessionToken(user: { id: string; email: string }): string {
36 // Use JWT or your preferred session token format
37 // For Lambda, consider using AWS KMS for signing
38 return Buffer.from(JSON.stringify(user)).toString("base64");
39}

4. Cloudflare Worker

A Cloudflare Worker that handles the full magic link auth flow.

worker.ts
1export interface Env {
2 TURALOGIN_API_KEY: string;
3 APP_LOGIN_VALIDATION_URL: string;
4}
5
6async function turaloginRequest(
7 env: Env,
8 endpoint: string,
9 body: object
10) {
11 const response = await fetch(`https://api.turalogin.com/api/v1${endpoint}`, {
12 method: "POST",
13 headers: {
14 "Authorization": `Bearer ${env.TURALOGIN_API_KEY}`,
15 "Content-Type": "application/json",
16 },
17 body: JSON.stringify(body),
18 });
19
20 const data = await response.json() as any;
21
22 if (!response.ok) {
23 return { error: true, data, status: response.status };
24 }
25
26 return { error: false, data };
27}
28
29export default {
30 async fetch(request: Request, env: Env): Promise<Response> {
31 const url = new URL(request.url);
32
33 // CORS headers
34 const corsHeaders = {
35 "Access-Control-Allow-Origin": "*",
36 "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
37 "Access-Control-Allow-Headers": "Content-Type",
38 };
39
40 if (request.method === "OPTIONS") {
41 return new Response(null, { headers: corsHeaders });
42 }
43
44 // Start auth - send magic link email
45 if (url.pathname === "/auth/start" && request.method === "POST") {
46 const { email } = await request.json() as { email: string };
47 const result = await turaloginRequest(env, "/auth/start", {
48 email,
49 validationUrl: env.APP_LOGIN_VALIDATION_URL
50 });
51
52 if (result.error) {
53 return new Response(JSON.stringify(result.data), {
54 status: result.status,
55 headers: { ...corsHeaders, "Content-Type": "application/json" },
56 });
57 }
58
59 return new Response(JSON.stringify({
60 success: true,
61 message: "Check your email for the login link"
62 }), {
63 headers: { ...corsHeaders, "Content-Type": "application/json" },
64 });
65 }
66
67 // Callback - verify magic link token
68 if (url.pathname === "/auth/callback" && request.method === "GET") {
69 const token = url.searchParams.get("token");
70
71 if (!token) {
72 return Response.redirect("/login?error=invalid_link", 302);
73 }
74
75 const result = await turaloginRequest(env, "/auth/verify", {
76 sessionId: token,
77 });
78
79 if (result.error) {
80 return Response.redirect("/login?error=verification_failed", 302);
81 }
82
83 return new Response(null, {
84 status: 302,
85 headers: {
86 "Location": "/dashboard",
87 "Set-Cookie": `session=${result.data.token}; HttpOnly; Secure; SameSite=Lax; Max-Age=604800; Path=/`,
88 },
89 });
90 }
91
92 return new Response("Not Found", { status: 404 });
93 },
94};

5. Vercel Edge Function

Edge functions for Vercel with magic link authentication.

api/auth/start.ts
1import { NextRequest } from "next/server";
2
3export const config = {
4 runtime: "edge",
5};
6
7const TURALOGIN_API_KEY = process.env.TURALOGIN_API_KEY!;
8const VALIDATION_URL = process.env.APP_LOGIN_VALIDATION_URL!;
9
10export default async function handler(req: NextRequest) {
11 if (req.method !== "POST") {
12 return new Response("Method not allowed", { status: 405 });
13 }
14
15 const { email } = await req.json();
16
17 if (!email) {
18 return Response.json({ error: "Email is required" }, { status: 400 });
19 }
20
21 const response = await fetch("https://api.turalogin.com/api/v1/auth/start", {
22 method: "POST",
23 headers: {
24 "Authorization": `Bearer ${TURALOGIN_API_KEY}`,
25 "Content-Type": "application/json",
26 },
27 body: JSON.stringify({
28 email,
29 validationUrl: VALIDATION_URL
30 }),
31 });
32
33 if (!response.ok) {
34 const error = await response.json();
35 return Response.json(error, { status: response.status });
36 }
37
38 return Response.json({
39 success: true,
40 message: "Check your email for the login link"
41 });
42}
43
44// api/auth/callback.ts - Handle magic link redirect
45export default async function callbackHandler(req: NextRequest) {
46 const url = new URL(req.url);
47 const token = url.searchParams.get("token");
48
49 if (!token) {
50 return Response.redirect(new URL("/login?error=invalid_link", req.url));
51 }
52
53 const response = await fetch("https://api.turalogin.com/api/v1/auth/verify", {
54 method: "POST",
55 headers: {
56 "Authorization": `Bearer ${TURALOGIN_API_KEY}`,
57 "Content-Type": "application/json",
58 },
59 body: JSON.stringify({ sessionId: token }),
60 });
61
62 if (!response.ok) {
63 return Response.redirect(new URL("/login?error=verification_failed", req.url));
64 }
65
66 const data = await response.json();
67
68 return new Response(null, {
69 status: 302,
70 headers: {
71 "Location": "/dashboard",
72 "Set-Cookie": `session=${data.token}; HttpOnly; Secure; SameSite=Lax; Max-Age=604800; Path=/`,
73 },
74 });
75}

6. Netlify Function

Authentication handler for Netlify Functions.

netlify/functions/auth-start.ts
1import type { Handler } from "@netlify/functions";
2
3const TURALOGIN_API_KEY = process.env.TURALOGIN_API_KEY!;
4const VALIDATION_URL = process.env.APP_LOGIN_VALIDATION_URL!;
5
6export const handler: Handler = async (event) => {
7 // Handle CORS
8 if (event.httpMethod === "OPTIONS") {
9 return {
10 statusCode: 204,
11 headers: {
12 "Access-Control-Allow-Origin": "*",
13 "Access-Control-Allow-Methods": "POST, OPTIONS",
14 "Access-Control-Allow-Headers": "Content-Type",
15 },
16 };
17 }
18
19 if (event.httpMethod !== "POST") {
20 return { statusCode: 405, body: "Method not allowed" };
21 }
22
23 try {
24 const { email } = JSON.parse(event.body || "{}");
25
26 const response = await fetch("https://api.turalogin.com/api/v1/auth/start", {
27 method: "POST",
28 headers: {
29 "Authorization": `Bearer ${TURALOGIN_API_KEY}`,
30 "Content-Type": "application/json",
31 },
32 body: JSON.stringify({
33 email,
34 validationUrl: VALIDATION_URL
35 }),
36 });
37
38 if (!response.ok) {
39 const error = await response.json();
40 return {
41 statusCode: response.status,
42 headers: { "Content-Type": "application/json" },
43 body: JSON.stringify(error),
44 };
45 }
46
47 return {
48 statusCode: 200,
49 headers: {
50 "Content-Type": "application/json",
51 "Access-Control-Allow-Origin": "*",
52 },
53 body: JSON.stringify({
54 success: true,
55 message: "Check your email for the login link"
56 }),
57 };
58 } catch (error) {
59 return {
60 statusCode: 500,
61 body: JSON.stringify({ error: "Internal server error" }),
62 };
63 }
64};

Complete Serverless Framework Setup

A complete serverless.yml configuration for AWS Lambda with magic link authentication.

serverless.yml + handlers
1# serverless.yml
2service: my-app-auth
3
4provider:
5 name: aws
6 runtime: nodejs18.x
7 environment:
8 TURALOGIN_API_KEY: ${env:TURALOGIN_API_KEY}
9 APP_LOGIN_VALIDATION_URL: ${env:APP_LOGIN_VALIDATION_URL}
10
11functions:
12 authStart:
13 handler: handlers/auth.start
14 events:
15 - http:
16 path: auth/start
17 method: post
18 cors: true
19
20 authCallback:
21 handler: handlers/auth.callback
22 events:
23 - http:
24 path: auth/callback
25 method: get
26
27 authLogout:
28 handler: handlers/auth.logout
29 events:
30 - http:
31 path: auth/logout
32 method: post
33 cors: true
34
35 getMe:
36 handler: handlers/user.me
37 events:
38 - http:
39 path: api/me
40 method: get
41 cors: true
42
43---
44// handlers/auth.ts
45const TURALOGIN_URL = "https://api.turalogin.com/api/v1";
46const TURALOGIN_API_KEY = process.env.TURALOGIN_API_KEY!;
47const VALIDATION_URL = process.env.APP_LOGIN_VALIDATION_URL!;
48
49const headers = {
50 "Content-Type": "application/json",
51 "Access-Control-Allow-Origin": "*",
52};
53
54async function turaloginRequest(endpoint: string, body: object) {
55 const response = await fetch(`${TURALOGIN_URL}${endpoint}`, {
56 method: "POST",
57 headers: {
58 "Authorization": `Bearer ${TURALOGIN_API_KEY}`,
59 "Content-Type": "application/json",
60 },
61 body: JSON.stringify(body),
62 });
63 return { data: await response.json(), status: response.status, ok: response.ok };
64}
65
66export const start = async (event: any) => {
67 const { email } = JSON.parse(event.body || "{}");
68 const result = await turaloginRequest("/auth/start", {
69 email,
70 validationUrl: VALIDATION_URL
71 });
72
73 if (!result.ok) {
74 return { statusCode: result.status, headers, body: JSON.stringify(result.data) };
75 }
76
77 return {
78 statusCode: 200,
79 headers,
80 body: JSON.stringify({ success: true, message: "Check your email for the login link" })
81 };
82};
83
84export const callback = async (event: any) => {
85 const token = event.queryStringParameters?.token;
86
87 if (!token) {
88 return { statusCode: 302, headers: { "Location": "/login?error=invalid_link" }, body: "" };
89 }
90
91 const result = await turaloginRequest("/auth/verify", { sessionId: token });
92
93 if (!result.ok) {
94 return { statusCode: 302, headers: { "Location": "/login?error=verification_failed" }, body: "" };
95 }
96
97 return {
98 statusCode: 302,
99 headers: {
100 "Location": "/dashboard",
101 "Set-Cookie": `session=${result.data.token}; HttpOnly; Secure; SameSite=Lax; Max-Age=604800; Path=/`,
102 },
103 body: "",
104 };
105};
106
107export const logout = async () => {
108 return {
109 statusCode: 200,
110 headers: {
111 ...headers,
112 "Set-Cookie": "session=; HttpOnly; Secure; Max-Age=0; Path=/",
113 },
114 body: JSON.stringify({ success: true }),
115 };
116};

Ready to integrate?

Create your Turalogin account and get your API key in minutes.