All integrations

Bun & Deno Integration

Modern JavaScript runtimes

Modern runtimes work out of the box with Turalogin. No SDK required - just use native fetch APIs. Start auth to send a magic link email, handle the callback when users click the link, and verify the token. Clean, fast, and edge-friendly.

  • Zero dependencies - uses native fetch
  • Magic link flow with customizable callback URL
  • Built-in TypeScript support
  • Edge-friendly and fast cold starts
  • Works with Bun.serve and Deno.serve

Implementation Examples

1. Environment Configuration

Set up your environment variables for local development and production.

.env
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. Start Authentication (Bun)

Create a simple Bun server endpoint to start authentication. This sends a magic link to the user's email.

server.ts
1const TURALOGIN_API_KEY = Bun.env.TURALOGIN_API_KEY!;
2const TURALOGIN_URL = "https://api.turalogin.com/api/v1";
3const VALIDATION_URL = Bun.env.APP_LOGIN_VALIDATION_URL!;
4
5async function startAuth(email: string) {
6 const response = await fetch(`${TURALOGIN_URL}/auth/start`, {
7 method: "POST",
8 headers: {
9 "Authorization": `Bearer ${TURALOGIN_API_KEY}`,
10 "Content-Type": "application/json",
11 },
12 body: JSON.stringify({
13 email,
14 validationUrl: VALIDATION_URL // Where the magic link redirects to
15 }),
16 });
17
18 if (!response.ok) {
19 const error = await response.json();
20 throw new Error(error.error || "Failed to start authentication");
21 }
22
23 return response.json();
24}
25
26const server = Bun.serve({
27 port: 3000,
28 async fetch(req) {
29 const url = new URL(req.url);
30
31 if (url.pathname === "/auth/start" && req.method === "POST") {
32 try {
33 const { email } = await req.json();
34 await startAuth(email);
35
36 return Response.json({
37 success: true,
38 message: "Check your email for the login link"
39 });
40 } catch (error) {
41 return Response.json(
42 { error: error.message },
43 { status: 400 }
44 );
45 }
46 }
47
48 return new Response("Not Found", { status: 404 });
49 },
50});
51
52console.log(`Server running on port ${server.port}`);

3. Handle Magic Link Callback (Bun)

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

server.ts
1async function verifyToken(sessionId: string) {
2 const response = await fetch(`${TURALOGIN_URL}/auth/verify`, {
3 method: "POST",
4 headers: {
5 "Authorization": `Bearer ${TURALOGIN_API_KEY}`,
6 "Content-Type": "application/json",
7 },
8 body: JSON.stringify({ sessionId }),
9 });
10
11 if (!response.ok) {
12 const error = await response.json();
13 throw new Error(error.error || "Verification failed");
14 }
15
16 return response.json();
17}
18
19// In your fetch handler:
20if (url.pathname === "/auth/callback" && req.method === "GET") {
21 const token = url.searchParams.get("token");
22
23 if (!token) {
24 return Response.redirect("/login?error=invalid_link", 302);
25 }
26
27 try {
28 const result = await verifyToken(token);
29
30 // Create session token (using jose or similar)
31 const sessionToken = await createSessionToken(result.user);
32
33 // Redirect to dashboard with session cookie
34 return new Response(null, {
35 status: 302,
36 headers: {
37 "Location": "/dashboard",
38 "Set-Cookie": `session=${sessionToken}; HttpOnly; Secure; SameSite=Lax; Max-Age=604800; Path=/`,
39 },
40 });
41 } catch (error) {
42 return Response.redirect("/login?error=verification_failed", 302);
43 }
44}

4. Deno Server

The same pattern works with Deno's native server.

server.ts
1const TURALOGIN_API_KEY = Deno.env.get("TURALOGIN_API_KEY")!;
2const TURALOGIN_URL = "https://api.turalogin.com/api/v1";
3const VALIDATION_URL = Deno.env.get("APP_LOGIN_VALIDATION_URL")!;
4
5interface TuraloginUser {
6 id: string;
7 email: string;
8}
9
10async function turaloginRequest<T>(endpoint: string, body: object): Promise<T> {
11 const response = await fetch(`${TURALOGIN_URL}${endpoint}`, {
12 method: "POST",
13 headers: {
14 "Authorization": `Bearer ${TURALOGIN_API_KEY}`,
15 "Content-Type": "application/json",
16 },
17 body: JSON.stringify(body),
18 });
19
20 const data = await response.json();
21
22 if (!response.ok) {
23 throw new Error(data.error || "Turalogin API error");
24 }
25
26 return data;
27}
28
29Deno.serve({ port: 3000 }, async (req) => {
30 const url = new URL(req.url);
31
32 // Start auth - send magic link
33 if (url.pathname === "/auth/start" && req.method === "POST") {
34 const { email } = await req.json();
35
36 try {
37 await turaloginRequest("/auth/start", {
38 email,
39 validationUrl: VALIDATION_URL
40 });
41 return Response.json({
42 success: true,
43 message: "Check your email for the login link"
44 });
45 } catch (error) {
46 return Response.json({ error: error.message }, { status: 400 });
47 }
48 }
49
50 // Callback - verify magic link token
51 if (url.pathname === "/auth/callback" && req.method === "GET") {
52 const token = url.searchParams.get("token");
53
54 if (!token) {
55 return Response.redirect("/login?error=invalid_link", 302);
56 }
57
58 try {
59 const result = await turaloginRequest<{ token: string; user: TuraloginUser }>(
60 "/auth/verify",
61 { sessionId: token }
62 );
63
64 return new Response(null, {
65 status: 302,
66 headers: {
67 "Location": "/dashboard",
68 "Set-Cookie": `session=${result.token}; HttpOnly; Secure; SameSite=Lax; Path=/`,
69 },
70 });
71 } catch (error) {
72 return Response.redirect("/login?error=verification_failed", 302);
73 }
74 }
75
76 return new Response("Not Found", { status: 404 });
77});

5. Hono Framework (Bun/Deno)

Using the Hono framework for a cleaner routing experience.

server.ts
1import { Hono } from "hono";
2import { setCookie, getCookie } from "hono/cookie";
3
4const app = new Hono();
5
6const TURALOGIN_API_KEY = process.env.TURALOGIN_API_KEY!;
7const TURALOGIN_URL = "https://api.turalogin.com/api/v1";
8const VALIDATION_URL = process.env.APP_LOGIN_VALIDATION_URL!;
9
10// Turalogin client
11const turalogin = {
12 async startAuth(email: string) {
13 const res = await fetch(`${TURALOGIN_URL}/auth/start`, {
14 method: "POST",
15 headers: {
16 "Authorization": `Bearer ${TURALOGIN_API_KEY}`,
17 "Content-Type": "application/json",
18 },
19 body: JSON.stringify({
20 email,
21 validationUrl: VALIDATION_URL
22 }),
23 });
24 if (!res.ok) throw new Error((await res.json()).error);
25 return res.json();
26 },
27
28 async verifyToken(sessionId: string) {
29 const res = await fetch(`${TURALOGIN_URL}/auth/verify`, {
30 method: "POST",
31 headers: {
32 "Authorization": `Bearer ${TURALOGIN_API_KEY}`,
33 "Content-Type": "application/json",
34 },
35 body: JSON.stringify({ sessionId }),
36 });
37 if (!res.ok) throw new Error((await res.json()).error);
38 return res.json();
39 },
40};
41
42// Routes
43app.post("/auth/start", async (c) => {
44 const { email } = await c.req.json();
45
46 try {
47 await turalogin.startAuth(email);
48 return c.json({ success: true, message: "Check your email for the login link" });
49 } catch (error) {
50 return c.json({ error: error.message }, 400);
51 }
52});
53
54app.get("/auth/callback", async (c) => {
55 const token = c.req.query("token");
56
57 if (!token) {
58 return c.redirect("/login?error=invalid_link");
59 }
60
61 try {
62 const result = await turalogin.verifyToken(token);
63
64 setCookie(c, "session", result.token, {
65 httpOnly: true,
66 secure: true,
67 sameSite: "Lax",
68 maxAge: 60 * 60 * 24 * 7,
69 path: "/",
70 });
71
72 return c.redirect("/dashboard");
73 } catch (error) {
74 return c.redirect("/login?error=verification_failed");
75 }
76});
77
78// Protected route
79app.get("/api/me", async (c) => {
80 const session = getCookie(c, "session");
81 if (!session) {
82 return c.json({ error: "Unauthorized" }, 401);
83 }
84
85 // Validate session and get user
86 // ...
87
88 return c.json({ user: { /* ... */ } });
89});
90
91export default app;

6. Authentication Middleware

Create middleware for protecting routes.

middleware/auth.ts
1import { Context, Next } from "hono";
2import { getCookie } from "hono/cookie";
3
4// Simple JWT validation (use a proper library in production)
5async function validateSession(token: string) {
6 // Implement your session validation
7 // This could be JWT verification, database lookup, etc.
8 return { id: "user-id", email: "user@example.com" };
9}
10
11export async function requireAuth(c: Context, next: Next) {
12 const sessionToken = getCookie(c, "session");
13
14 if (!sessionToken) {
15 return c.json({ error: "Authentication required" }, 401);
16 }
17
18 try {
19 const user = await validateSession(sessionToken);
20 c.set("user", user);
21 await next();
22 } catch {
23 return c.json({ error: "Invalid session" }, 401);
24 }
25}
26
27// Usage in Hono:
28// app.get("/api/me", requireAuth, (c) => {
29// const user = c.get("user");
30// return c.json({ user });
31// });
32
33// Or protect a group of routes:
34// const api = new Hono();
35// api.use("*", requireAuth);
36// api.get("/me", (c) => c.json({ user: c.get("user") }));
37// app.route("/api", api);

Complete Bun Application with Elysia

A complete Bun application using the Elysia framework with magic link authentication.

server.ts
1import { Elysia, t } from "elysia";
2import { cookie } from "@elysiajs/cookie";
3
4const TURALOGIN_API_KEY = Bun.env.TURALOGIN_API_KEY!;
5const TURALOGIN_URL = "https://api.turalogin.com/api/v1";
6const VALIDATION_URL = Bun.env.APP_LOGIN_VALIDATION_URL!;
7
8// Turalogin service
9class TuraloginService {
10 private async request<T>(endpoint: string, body: object): Promise<T> {
11 const response = await fetch(`${TURALOGIN_URL}${endpoint}`, {
12 method: "POST",
13 headers: {
14 "Authorization": `Bearer ${TURALOGIN_API_KEY}`,
15 "Content-Type": "application/json",
16 },
17 body: JSON.stringify(body),
18 });
19
20 const data = await response.json();
21
22 if (!response.ok) {
23 throw new Error(data.error || "API error");
24 }
25
26 return data;
27 }
28
29 startAuth(email: string) {
30 return this.request("/auth/start", {
31 email,
32 validationUrl: VALIDATION_URL
33 });
34 }
35
36 verifyToken(sessionId: string) {
37 return this.request<{ token: string; user: { id: string; email: string } }>(
38 "/auth/verify",
39 { sessionId }
40 );
41 }
42}
43
44const turalogin = new TuraloginService();
45
46// Create app
47const app = new Elysia()
48 .use(cookie())
49 .decorate("turalogin", turalogin)
50
51 // Start auth - send magic link email
52 .post(
53 "/auth/start",
54 async ({ body, turalogin }) => {
55 await turalogin.startAuth(body.email);
56 return { success: true, message: "Check your email for the login link" };
57 },
58 {
59 body: t.Object({
60 email: t.String({ format: "email" }),
61 }),
62 }
63 )
64
65 // Callback - handle magic link redirect
66 .get(
67 "/auth/callback",
68 async ({ query, turalogin, cookie: { session }, redirect }) => {
69 const { token } = query;
70
71 if (!token) {
72 return redirect("/login?error=invalid_link");
73 }
74
75 try {
76 const result = await turalogin.verifyToken(token);
77
78 session.set({
79 value: result.token,
80 httpOnly: true,
81 secure: true,
82 sameSite: "lax",
83 maxAge: 60 * 60 * 24 * 7,
84 path: "/",
85 });
86
87 return redirect("/dashboard");
88 } catch (error) {
89 return redirect("/login?error=verification_failed");
90 }
91 },
92 {
93 query: t.Object({
94 token: t.Optional(t.String()),
95 }),
96 }
97 )
98
99 .post("/auth/logout", ({ cookie: { session } }) => {
100 session.remove();
101 return { success: true };
102 })
103
104 // Protected routes
105 .guard(
106 {
107 beforeHandle: ({ cookie: { session }, set }) => {
108 if (!session.value) {
109 set.status = 401;
110 return { error: "Authentication required" };
111 }
112 },
113 },
114 (app) =>
115 app
116 .get("/api/me", ({ cookie: { session } }) => {
117 // Decode session and return user
118 return { user: { /* decoded from session */ } };
119 })
120 .get("/api/dashboard", () => {
121 return { message: "Welcome to the dashboard!" };
122 })
123 )
124
125 .listen(3000);
126
127console.log(`🚀 Server running at ${app.server?.url}`);
128
129export type App = typeof app;

Ready to integrate?

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