net/http and popular frameworks
Turalogin integration in Go is lightweight and explicit. Use net/http or any HTTP client library. Start auth to send a magic link email, handle the callback when users click the link, and verify the token. Ideal for APIs, microservices, and high-performance backends.
Set up your environment variables for local development and production.
1 # Your Turalogin API key from the dashboard 2 TURALOGIN_API_KEY=tl_live_xxxxxxxxxxxxx 3 4 # The URL where magic links will redirect to 5 # Development: 6 APP_LOGIN_VALIDATION_URL=http://localhost:8080/auth/callback 7 8 # Production (update for your domain): 9 # APP_LOGIN_VALIDATION_URL=https://myapp.com/auth/callback
Create an HTTP handler to initiate authentication. This sends a magic link to the user's email.
1 package handlers 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "net/http" 7 "os" 8 ) 9 10 var ( 11 turaloginAPIKey = os.Getenv("TURALOGIN_API_KEY") 12 turaloginURL = "https://api.turalogin.com/api/v1" 13 validationURL = os.Getenv("APP_LOGIN_VALIDATION_URL") 14 ) 15 16 type StartAuthRequest struct { 17 Email string `json:"email"` 18 } 19 20 func StartAuth(w http.ResponseWriter, r *http.Request) { 21 var req StartAuthRequest 22 if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 23 http.Error(w, `{"error": "Invalid request"}`, http.StatusBadRequest) 24 return 25 } 26 27 if req.Email == "" { 28 http.Error(w, `{"error": "Email is required"}`, http.StatusBadRequest) 29 return 30 } 31 32 // Call Turalogin API 33 payload, _ := json.Marshal(map[string]string{ 34 "email": req.Email, 35 "validationUrl": validationURL, // Where the magic link redirects to 36 }) 37 38 httpReq, _ := http.NewRequest("POST", turaloginURL+"/auth/start", bytes.NewBuffer(payload)) 39 httpReq.Header.Set("Authorization", "Bearer "+turaloginAPIKey) 40 httpReq.Header.Set("Content-Type", "application/json") 41 42 client := &http.Client{} 43 resp, err := client.Do(httpReq) 44 if err != nil { 45 http.Error(w, `{"error": "Failed to contact auth service"}`, http.StatusInternalServerError) 46 return 47 } 48 defer resp.Body.Close() 49 50 if resp.StatusCode != http.StatusOK { 51 var errResp map[string]interface{} 52 json.NewDecoder(resp.Body).Decode(&errResp) 53 w.Header().Set("Content-Type", "application/json") 54 w.WriteHeader(resp.StatusCode) 55 json.NewEncoder(w).Encode(errResp) 56 return 57 } 58 59 w.Header().Set("Content-Type", "application/json") 60 json.NewEncoder(w).Encode(map[string]interface{}{ 61 "success": true, 62 "message": "Check your email for the login link", 63 }) 64 }
Create a callback handler that receives the token from the magic link and verifies it.
1 type User struct { 2 ID string `json:"id"` 3 Email string `json:"email"` 4 } 5 6 type VerifyAuthResponse struct { 7 Token string `json:"token"` 8 User User `json:"user"` 9 } 10 11 func AuthCallback(w http.ResponseWriter, r *http.Request) { 12 token := r.URL.Query().Get("token") 13 14 if token == "" { 15 http.Redirect(w, r, "/login?error=invalid_link", http.StatusFound) 16 return 17 } 18 19 // Call Turalogin API to verify the token 20 payload, _ := json.Marshal(map[string]string{ 21 "sessionId": token, 22 }) 23 24 httpReq, _ := http.NewRequest("POST", turaloginURL+"/auth/verify", bytes.NewBuffer(payload)) 25 httpReq.Header.Set("Authorization", "Bearer "+turaloginAPIKey) 26 httpReq.Header.Set("Content-Type", "application/json") 27 28 client := &http.Client{} 29 resp, err := client.Do(httpReq) 30 if err != nil { 31 http.Redirect(w, r, "/login?error=server_error", http.StatusFound) 32 return 33 } 34 defer resp.Body.Close() 35 36 if resp.StatusCode != http.StatusOK { 37 http.Redirect(w, r, "/login?error=verification_failed", http.StatusFound) 38 return 39 } 40 41 var result VerifyAuthResponse 42 json.NewDecoder(resp.Body).Decode(&result) 43 44 // Create session cookie (using your session package) 45 sessionToken := createSession(result.User.ID, result.User.Email) 46 47 http.SetCookie(w, &http.Cookie{ 48 Name: "session", 49 Value: sessionToken, 50 HttpOnly: true, 51 Secure: true, 52 SameSite: http.SameSiteLaxMode, 53 MaxAge: 7 * 24 * 60 * 60, // 7 days 54 Path: "/", 55 }) 56 57 http.Redirect(w, r, "/dashboard", http.StatusFound) 58 }
A reusable client struct for Turalogin API calls.
1 package turalogin 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 ) 9 10 type Client struct { 11 apiKey string 12 validationURL string 13 baseURL string 14 http *http.Client 15 } 16 17 type User struct { 18 ID string `json:"id"` 19 Email string `json:"email"` 20 } 21 22 type AuthResult struct { 23 Token string `json:"token"` 24 User User `json:"user"` 25 } 26 27 type Error struct { 28 Message string 29 Code string 30 Status int 31 } 32 33 func (e *Error) Error() string { 34 return e.Message 35 } 36 37 func NewClient(apiKey, validationURL string) *Client { 38 return &Client{ 39 apiKey: apiKey, 40 validationURL: validationURL, 41 baseURL: "https://api.turalogin.com/api/v1", 42 http: &http.Client{}, 43 } 44 } 45 46 func (c *Client) StartAuth(email string) error { 47 payload, _ := json.Marshal(map[string]string{ 48 "email": email, 49 "validationUrl": c.validationURL, 50 }) 51 52 req, _ := http.NewRequest("POST", c.baseURL+"/auth/start", bytes.NewBuffer(payload)) 53 req.Header.Set("Authorization", "Bearer "+c.apiKey) 54 req.Header.Set("Content-Type", "application/json") 55 56 resp, err := c.http.Do(req) 57 if err != nil { 58 return err 59 } 60 defer resp.Body.Close() 61 62 if resp.StatusCode != http.StatusOK { 63 var result map[string]interface{} 64 json.NewDecoder(resp.Body).Decode(&result) 65 return &Error{ 66 Message: fmt.Sprintf("%v", result["error"]), 67 Code: fmt.Sprintf("%v", result["code"]), 68 Status: resp.StatusCode, 69 } 70 } 71 72 return nil 73 } 74 75 func (c *Client) VerifyToken(sessionID string) (*AuthResult, error) { 76 payload, _ := json.Marshal(map[string]string{ 77 "sessionId": sessionID, 78 }) 79 80 req, _ := http.NewRequest("POST", c.baseURL+"/auth/verify", bytes.NewBuffer(payload)) 81 req.Header.Set("Authorization", "Bearer "+c.apiKey) 82 req.Header.Set("Content-Type", "application/json") 83 84 resp, err := c.http.Do(req) 85 if err != nil { 86 return nil, err 87 } 88 defer resp.Body.Close() 89 90 if resp.StatusCode != http.StatusOK { 91 var errResult map[string]interface{} 92 json.NewDecoder(resp.Body).Decode(&errResult) 93 return nil, &Error{ 94 Message: fmt.Sprintf("%v", errResult["error"]), 95 Code: fmt.Sprintf("%v", errResult["code"]), 96 Status: resp.StatusCode, 97 } 98 } 99 100 var result AuthResult 101 json.NewDecoder(resp.Body).Decode(&result) 102 103 return &result, nil 104 }
Middleware to protect routes and validate sessions.
1 package middleware 2 3 import ( 4 "context" 5 "net/http" 6 ) 7 8 type contextKey string 9 10 const UserContextKey contextKey = "user" 11 12 type User struct { 13 ID string 14 Email string 15 } 16 17 func RequireAuth(next http.Handler) http.Handler { 18 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 19 cookie, err := r.Cookie("session") 20 if err != nil { 21 http.Error(w, `{"error": "Authentication required"}`, http.StatusUnauthorized) 22 return 23 } 24 25 // Validate session token (implement your session validation) 26 user, err := validateSession(cookie.Value) 27 if err != nil { 28 http.Error(w, `{"error": "Invalid session"}`, http.StatusUnauthorized) 29 return 30 } 31 32 // Add user to context 33 ctx := context.WithValue(r.Context(), UserContextKey, user) 34 next.ServeHTTP(w, r.WithContext(ctx)) 35 }) 36 } 37 38 func GetUser(r *http.Request) *User { 39 user, ok := r.Context().Value(UserContextKey).(*User) 40 if !ok { 41 return nil 42 } 43 return user 44 } 45 46 func validateSession(token string) (*User, error) { 47 // Implement your session validation logic 48 // This could be JWT validation, database lookup, etc. 49 return &User{ID: "...", Email: "..."}, nil 50 }
A complete Go application with magic link authentication and middleware.
1 package main 2 3 import ( 4 "encoding/json" 5 "log" 6 "net/http" 7 "os" 8 9 "myapp/handlers" 10 "myapp/middleware" 11 "myapp/turalogin" 12 ) 13 14 var client *turalogin.Client 15 16 func main() { 17 // Initialize Turalogin client 18 client = turalogin.NewClient( 19 os.Getenv("TURALOGIN_API_KEY"), 20 os.Getenv("APP_LOGIN_VALIDATION_URL"), 21 ) 22 23 // Create router 24 mux := http.NewServeMux() 25 26 // Public auth routes 27 mux.HandleFunc("POST /auth/start", handlers.StartAuth) 28 mux.HandleFunc("GET /auth/callback", handlers.AuthCallback) 29 mux.HandleFunc("POST /auth/logout", handlers.Logout) 30 31 // Protected routes (with auth middleware) 32 protected := http.NewServeMux() 33 protected.HandleFunc("GET /api/me", handleMe) 34 protected.HandleFunc("GET /api/dashboard", handleDashboard) 35 36 mux.Handle("/api/", middleware.RequireAuth(protected)) 37 38 // Start server 39 log.Println("Server starting on :8080") 40 log.Fatal(http.ListenAndServe(":8080", mux)) 41 } 42 43 func handleMe(w http.ResponseWriter, r *http.Request) { 44 user := middleware.GetUser(r) 45 46 w.Header().Set("Content-Type", "application/json") 47 json.NewEncoder(w).Encode(map[string]interface{}{ 48 "id": user.ID, 49 "email": user.Email, 50 }) 51 } 52 53 func handleDashboard(w http.ResponseWriter, r *http.Request) { 54 user := middleware.GetUser(r) 55 56 w.Header().Set("Content-Type", "application/json") 57 json.NewEncoder(w).Encode(map[string]interface{}{ 58 "message": "Welcome to the dashboard, " + user.Email + "!", 59 }) 60 } 61 62 // handlers/auth.go - Logout handler 63 func Logout(w http.ResponseWriter, r *http.Request) { 64 http.SetCookie(w, &http.Cookie{ 65 Name: "session", 66 Value: "", 67 HttpOnly: true, 68 Secure: true, 69 MaxAge: -1, 70 Path: "/", 71 }) 72 73 w.Header().Set("Content-Type", "application/json") 74 json.NewEncoder(w).Encode(map[string]bool{"success": true}) 75 }