Complete Authentication Example
This guide provides a complete working example that integrates all the authentication flows we've covered in this Day 1 Essentials series. You can use this as a starting point for your own applications.
Complete Example Application
Below is a complete implementation that includes:
- Registration
- Login
- Session management
- Social sign-in
- Logout
- Protected routes
- JavaScript/Node.js
- React
- Next.js
- Go
- cURL
Complete Express.js Example
const express = require("express")
const { Configuration, FrontendApi } = require("@ory/client")
const app = express()
// Initialize Ory SDK
const ory = new FrontendApi(
new Configuration({
basePath: process.env.ORY_URL || "http://localhost:4000",
baseOptions: {
withCredentials: true,
},
}),
)
// Middleware
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.set("view engine", "ejs")
// Authentication middleware
const requireAuth = async (req, res, next) => {
try {
const { data: session } = await ory.toSession({
cookie: req.header("cookie"),
})
req.session = session
next()
} catch (err) {
res.redirect("/login?return_to=" + encodeURIComponent(req.originalUrl))
}
}
// Public routes
app.get("/", (req, res) => {
res.render("home")
})
// Registration flow
app.get("/registration", async (req, res) => {
try {
const { data: flow } = await ory.createBrowserRegistrationFlow({
returnTo: req.query.return_to,
})
res.render("registration", { flow })
} catch (err) {
res.status(500).render("error", { message: "Could not initialize registration flow" })
}
})
app.post("/registration", async (req, res) => {
try {
const { data: completion } = await ory.updateRegistrationFlow({
flow: req.query.flow,
updateRegistrationFlowBody: {
method: "password",
password: req.body.password,
traits: {
email: req.body.email,
name: req.body.name,
},
csrf_token: req.body.csrf_token,
},
})
// Registration successful, redirect
res.redirect(completion.redirect_to || "/dashboard")
} catch (err) {
// Handle errors
const { data: flow } = await ory.getRegistrationFlow({
id: req.query.flow,
})
res.status(400).render("registration", { flow, error: err.response?.data?.error })
}
})
// Login flow
app.get("/login", async (req, res) => {
try {
const { data: flow } = await ory.createBrowserLoginFlow({
returnTo: req.query.return_to,
})
// Extract social providers
const socialProviders = flow.ui.nodes
.filter((node) => node.group === "oidc")
.map((node) => ({
name: node.attributes.value,
label: node.meta.label?.text || node.attributes.value,
url: `${flow.ui.action}?flow=${flow.id}&method=oidc&provider=${node.attributes.value}`,
}))
res.render("login", { flow, socialProviders })
} catch (err) {
res.status(500).render("error", { message: "Could not initialize login flow" })
}
})
app.post("/login", async (req, res) => {
try {
const { data: completion } = await ory.updateLoginFlow({
flow: req.query.flow,
updateLoginFlowBody: {
method: "password",
identifier: req.body.identifier,
password: req.body.password,
csrf_token: req.body.csrf_token,
},
})
// Login successful, redirect
res.redirect(completion.redirect_to || "/dashboard")
} catch (err) {
// Handle errors
const { data: flow } = await ory.getLoginFlow({
id: req.query.flow,
})
res.status(400).render("login", { flow, error: err.response?.data?.error })
}
})
// OAuth callback - you don't need a special route for this
// Ory handles the callback and redirects to your return_to URL
// Logout
app.get("/logout", async (req, res) => {
try {
const { data: logoutFlow } = await ory.createBrowserLogoutFlow({
cookie: req.header("cookie"),
})
res.redirect(logoutFlow.logout_url)
} catch (err) {
// User is not logged in
res.redirect("/")
}
})
// Protected routes
app.get("/dashboard", requireAuth, (req, res) => {
res.render("dashboard", { user: req.session.identity })
})
app.get("/profile", requireAuth, (req, res) => {
// Access provider-specific data
const providerData = req.session.identity.metadata_public?.providers || {}
res.render("profile", {
user: req.session.identity,
providerData,
})
})
app.get("/settings", requireAuth, (req, res) => {
res.render("settings", { user: req.session.identity })
})
// Session refresh
app.get("/refresh-session", requireAuth, async (req, res) => {
try {
const { data: refreshedSession } = await ory.extendSession({
cookie: req.header("cookie"),
})
res.redirect(req.query.return_to || "/dashboard")
} catch (err) {
res.redirect("/login")
}
})
// Start server
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
})
EJS Templates (examples):
login.ejs
<!doctype html>
<html>
<head>
<title>Login</title>
<style>
.social-providers {
margin-top: 20px;
}
.social-button {
display: block;
margin-bottom: 10px;
padding: 10px;
background: #f0f0f0;
text-decoration: none;
color: #333;
}
</style>
</head>
<body>
<h1>Login</h1>
<% if (error) { %>
<div style="color: red;"><%= error.message %></div>
<% } %>
<form action="<%= flow.ui.action %>" method="POST">
<div>
<label for="identifier">Email:</label>
<input type="text" id="identifier" name="identifier" required />
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required />
</div>
<input
type="hidden"
name="csrf_token"
value="<%= flow.ui.nodes.find(n => n.attributes.name === 'csrf_token')?.attributes.value %>"
/>
<button type="submit">Login</button>
</form>
<div>
<a href="/registration">Don't have an account? Register</a>
</div>
<% if (socialProviders && socialProviders.length > 0) { %>
<div class="social-providers">
<h3>Or sign in with:</h3>
<% socialProviders.forEach(provider => { %>
<a href="<%= provider.url %>" class="social-button"> <%= provider.label %> </a>
<% }); %>
</div>
<% } %>
</body>
</html>
dashboard.ejs
<!doctype html>
<html>
<head>
<title>Dashboard</title>
</head>
<body>
<h1>Welcome to Your Dashboard</h1>
<p>Hello, <%= user.traits.name || user.traits.email %></p>
<nav>
<ul>
<li><a href="/profile">Your Profile</a></li>
<li><a href="/settings">Settings</a></li>
<li><a href="/logout">Logout</a></li>
</ul>
</nav>
<div>
<h2>Dashboard Content</h2>
<p>This is your protected dashboard. Only authenticated users can see this.</p>
</div>
</body>
</html>
profile.ejs
<!doctype html>
<html>
<head>
<title>Profile</title>
</head>
<body>
<h1>Your Profile</h1>
<div>
<h2>Profile Information</h2>
<p>Email: <%= user.traits.email %></p>
<% if (user.traits.name) { %>
<p>Name: <%= user.traits.name %></p>
<% } %>
<p>User ID: <%= user.id %></p>
</div>
<% if (Object.keys(providerData).length > 0) { %>
<div>
<h2>Connected Accounts</h2>
<% Object.entries(providerData).forEach(([provider, data]) => { %>
<div>
<h3><%= provider %></h3>
<pre><%= JSON.stringify(data, null, 2) %></pre>
</div>
<% }); %>
</div>
<% } %>
<a href="/dashboard">Back to Dashboard</a>
</body>
</html>
Complete React Example
App.jsx
import React from "react"
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"
import { ory } from "./lib/ory"
// Components
import Login from "./components/Login"
import Registration from "./components/Registration"
import Dashboard from "./components/Dashboard"
import Profile from "./components/Profile"
import Layout from "./components/Layout"
import ProtectedRoute from "./components/ProtectedRoute"
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="login" element={<Login />} />
<Route path="registration" element={<Registration />} />
{/* Protected routes */}
<Route
path="dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
<Route
path="profile"
element={
<ProtectedRoute>
<Profile />
</ProtectedRoute>
}
/>
<Route path="*" element={<Navigate to="/" replace />} />
</Route>
</Routes>
</BrowserRouter>
)
}
const Home = () => (
<div>
<h1>Welcome to Our App</h1>
<p>This is a complete authentication example using Ory and React.</p>
<div>
<a href="/login">Login</a> | <a href="/registration">Register</a>
</div>
</div>
)
export default App
lib/ory.js
import { Configuration, FrontendApi } from "@ory/client"
// Get the URL from environment variables or use default
const basePath = process.env.REACT_APP_ORY_URL || "http://localhost:4000"
// Initialize the SDK
export const ory = new FrontendApi(
new Configuration({
basePath,
baseOptions: {
withCredentials: true,
},
}),
)
components/ProtectedRoute.jsx
import React, { useEffect, useState } from "react"
import { Navigate, useLocation } from "react-router-dom"
import { ory } from "../lib/ory"
const ProtectedRoute = ({ children }) => {
const [isAuthenticated, setIsAuthenticated] = useState(false)
const [loading, setLoading] = useState(true)
const location = useLocation()
useEffect(() => {
ory
.toSession()
.then(() => {
setIsAuthenticated(true)
setLoading(false)
})
.catch(() => {
setIsAuthenticated(false)
setLoading(false)
})
}, [])
if (loading) {
return <div>Loading...</div>
}
if (!isAuthenticated) {
// Redirect to login with return_to
return <Navigate to={`/login?return_to=${encodeURIComponent(location.pathname)}`} replace />
}
return children
}
export default ProtectedRoute
components/Login.jsx
import React, { useState, useEffect } from "react"
import { useNavigate, useLocation } from "react-router-dom"
import { ory } from "../lib/ory"
function Login() {
const [flow, setFlow] = useState(null)
const [socialProviders, setSocialProviders] = useState([])
const [error, setError] = useState(null)
const navigate = useNavigate()
const location = useLocation()
// Get return_to from URL
const searchParams = new URLSearchParams(location.search)
const returnTo = searchParams.get("return_to") || "/dashboard"
useEffect(() => {
// Initialize login flow
ory
.createBrowserLoginFlow({ returnTo })
.then(({ data }) => {
setFlow(data)
// Extract social providers
const providers = data.ui.nodes
.filter((node) => node.group === "oidc")
.map((node) => ({
name: node.attributes.value,
label: node.meta.label?.text || node.attributes.value,
url: `${data.ui.action}?flow=${data.id}&method=oidc&provider=${node.attributes.value}`,
}))
setSocialProviders(providers)
})
.catch((err) => {
setError("Could not initialize login flow")
console.error(err)
})
}, [returnTo])
const handleSubmit = async (e) => {
e.preventDefault()
const formData = new FormData(e.target)
const values = Object.fromEntries(formData.entries())
try {
const { data: completion } = await ory.updateLoginFlow({
flow: flow.id,
updateLoginFlowBody: {
method: "password",
identifier: values.identifier,
password: values.password,
csrf_token: values.csrf_token,
},
})
// Login successful
navigate(completion.redirect_to || returnTo)
} catch (err) {
// Handle errors
if (err.response?.data?.error) {
setError(err.response.data.error.message)
} else {
setError("An unknown error occurred")
}
// Refresh the flow to get a new CSRF token
ory
.getLoginFlow({ id: flow.id })
.then(({ data }) => setFlow(data))
.catch(console.error)
}
}
if (!flow) {
return <div>Loading...</div>
}
return (
<div>
<h2>Login</h2>
{error && <div style={{ color: "red" }}>{error}</div>}
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="identifier">Email:</label>
<input type="text" id="identifier" name="identifier" required />
</div>
<div>
<label htmlFor="password">Password:</label>
<input type="password" id="password" name="password" required />
</div>
<input
type="hidden"
name="csrf_token"
value={flow.ui.nodes.find((n) => n.attributes.name === "csrf_token")?.attributes.value}
/>
<button type="submit">Login</button>
</form>
<div>
<a href="/registration">Don't have an account? Register</a>
</div>
{socialProviders.length > 0 && (
<div className="social-providers">
<h3>Or sign in with:</h3>
{socialProviders.map((provider) => (
<a
key={provider.name}
href={provider.url}
className="social-button"
style={{
display: "block",
margin: "10px 0",
padding: "10px",
background: "#f0f0f0",
textDecoration: "none",
color: "#333",
}}
>
{provider.label}
</a>
))}
</div>
)}
</div>
)
}
export default Login
components/Dashboard.jsx
import React, { useState, useEffect } from "react"
import { ory } from "../lib/ory"
function Dashboard() {
const [user, setUser] = useState(null)
useEffect(() => {
ory
.toSession()
.then(({ data }) => {
setUser(data.identity)
})
.catch((err) => {
console.error("Session check failed:", err)
// The ProtectedRoute component will handle redirect
})
}, [])
if (!user) {
return <div>Loading...</div>
}
return (
<div>
<h1>Welcome to Your Dashboard</h1>
<p>Hello, {user.traits.name || user.traits.email}</p>
<nav>
<ul>
<li>
<a href="/profile">Your Profile</a>
</li>
<li>
<a href="/logout">Logout</a>
</li>
</ul>
</nav>
<div>
<h2>Dashboard Content</h2>
<p>This is your protected dashboard. Only authenticated users can see this.</p>
</div>
</div>
)
}
export default Dashboard
components/Profile.jsx
import React, { useState, useEffect } from "react"
import { ory } from "../lib/ory"
function Profile() {
const [user, setUser] = useState(null)
const [providerData, setProviderData] = useState({})
useEffect(() => {
ory
.toSession()
.then(({ data }) => {
setUser(data.identity)
// Extract provider data if it exists
if (data.identity.metadata_public?.providers) {
setProviderData(data.identity.metadata_public.providers)
}
})
.catch((err) => {
console.error("Session check failed:", err)
// The ProtectedRoute component will handle redirect
})
}, [])
if (!user) {
return <div>Loading...</div>
}
return (
<div>
<h1>Your Profile</h1>
<div>
<h2>Profile Information</h2>
<p>Email: {user.traits.email}</p>
{user.traits.name && <p>Name: {user.traits.name}</p>}
<p>User ID: {user.id}</p>
</div>
{Object.keys(providerData).length > 0 && (
<div>
<h2>Connected Accounts</h2>
{Object.entries(providerData).map(([provider, data]) => (
<div key={provider}>
<h3>{provider}</h3>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
))}
</div>
)}
<a href="/dashboard">Back to Dashboard</a>
</div>
)
}
export default Profile
Complete Go Example
package main
import (
"context"
"encoding/json"
"fmt"
"html/template"
"log"
"net/http"
"net/url"
"os"
"strings"
ory "github.com/ory/client-go"
)
type SocialProvider struct {
Name string
Label string
URL string
}
type App struct {
OryURL string
Client *ory.APIClient
Templates *template.Template
}
func NewApp() *App {
oryURL := os.Getenv("ORY_URL")
if oryURL == "" {
oryURL = "http://localhost:4000"
}
configuration := ory.NewConfiguration()
configuration.Servers = []ory.ServerConfiguration{
{
URL: oryURL,
},
}
return &App{
OryURL: oryURL,
Client: ory.NewAPIClient(configuration),
Templates: template.Must(template.ParseGlob("templates/*.html")),
}
}
func (a *App) authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check if user is authenticated
cookie := r.Header.Get("Cookie")
session, _, err := a.Client.FrontendApi.ToSession(context.Background()).Cookie(cookie).Execute()
if err != nil {
// Not authenticated, redirect to login
returnTo := url.QueryEscape(r.URL.Path)
http.Redirect(w, r, fmt.Sprintf("/login?return_to=%s", returnTo), http.StatusFound)
return
}
// User is authenticated, add session to context
ctx := context.WithValue(r.Context(), "session", session)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func (a *App) homeHandler(w http.ResponseWriter, r *http.Request) {
a.Templates.ExecuteTemplate(w, "home.html", nil)
}
func (a *App) loginHandler(w http.ResponseWriter, r *http.Request) {
returnTo := r.URL.Query().Get("return_to")
// Create login flow
flow, _, err := a.Client.FrontendApi.CreateBrowserLoginFlow(context.Background()).
ReturnTo(returnTo).
Execute()
if err != nil {
http.Error(w, "Failed to initialize login flow", http.StatusInternalServerError)
return
}
// Extract social providers
var socialProviders []SocialProvider
for _, node := range flow.Ui.Nodes {
if node.Group == "oidc" {
var label string
if node.Meta != nil && node.Meta.Label != nil {
label = node.Meta.Label.Text
} else {
label = node.Attributes.Value.(string)
}
socialProviders = append(socialProviders, SocialProvider{
Name: node.Attributes.Value.(string),
Label: label,
URL: flow.Ui.Action + "?flow=" + flow.Id + "&method=oidc&provider=" + node.Attributes.Value.(string),
})
}
}
// Find CSRF token
var csrfToken string
for _, node := range flow.Ui.Nodes {
if node.Attributes != nil && node.Attributes.Name == "csrf_token" {
csrfToken = node.Attributes.Value.(string)
break
}
}
// Render template
a.Templates.ExecuteTemplate(w, "login.html", map[string]interface{}{
"Flow": flow,
"SocialProviders": socialProviders,
"CSRFToken": csrfToken,
"Error": r.URL.Query().Get("error"),
})
}
func (a *App) loginSubmitHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "Failed to parse form", http.StatusBadRequest)
return
}
flowID := r.URL.Query().Get("flow")
// Create update login flow body
updateLoginFlowBody := map[string]interface{}{
"method": "password",
"identifier": r.Form.Get("identifier"),
"password": r.Form.Get("password"),
"csrf_token": r.Form.Get("csrf_token"),
}
// Convert to JSON
bodyJSON, _ := json.Marshal(updateLoginFlowBody)
// Make the request
req, _ := http.NewRequest("POST", a.OryURL+"/self-service/login?flow="+flowID, strings.NewReader(string(bodyJSON)))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
req.Header.Set("Cookie", r.Header.Get("Cookie"))
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
resp, err := client.Do(req)
if err != nil {
http.Error(w, "Login request failed", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// Check response status
if resp.StatusCode >= 400 {
var errorResponse struct {
Error struct {
Message string `json:"message"`
} `json:"error"`
}
if err := json.NewDecoder(resp.Body).Decode(&errorResponse); err != nil {
http.Error(w, "Failed to parse error response", http.StatusInternalServerError)
return
}
// Redirect back to login with error
http.Redirect(w, r, "/login?flow="+flowID+"&error="+url.QueryEscape(errorResponse.Error.Message), http.StatusFound)
return
}
// Success - Check for redirection
if resp.StatusCode == 302 {
redirectURL := resp.Header.Get("Location")
// Copy cookies from response to our response
for _, cookie := range resp.Cookies() {
http.SetCookie(w, cookie)
}
http.Redirect(w, r, redirectURL, http.StatusFound)
return
}
// Default redirect to dashboard
http.Redirect(w, r, "/dashboard", http.StatusFound)
}
func (a *App) registrationHandler(w http.ResponseWriter, r *http.Request) {
returnTo := r.URL.Query().Get("return_to")
// Create registration flow
flow, _, err := a.Client.FrontendApi.CreateBrowserRegistrationFlow(context.Background()).
ReturnTo(returnTo).
Execute()
if err != nil {
http.Error(w, "Failed to initialize registration flow", http.StatusInternalServerError)
return
}
// Find CSRF token
var csrfToken string
for _, node := range flow.Ui.Nodes {
if node.Attributes != nil && node.Attributes.Name == "csrf_token" {
csrfToken = node.Attributes.Value.(string)
break
}
}
// Render template
a.Templates.ExecuteTemplate(w, "registration.html", map[string]interface{}{
"Flow": flow,
"CSRFToken": csrfToken,
"Error": r.URL.Query().Get("error"),
})
}
func (a *App) dashboardHandler(w http.ResponseWriter, r *http.Request) {
// Get session from context
session := r.Context().Value("session").(*ory.Session)
a.Templates.ExecuteTemplate(w, "dashboard.html", map[string]interface{}{
"User": session.Identity,
})
}
func (a *App) profileHandler(w http.ResponseWriter, r *http.Request) {
// Get session from context
session := r.Context().Value("session").(*ory.Session)
// Extract provider data if it exists
var providerData map[string]interface{}
if session.Identity.MetadataPublic != nil {
if providers, ok := session.Identity.MetadataPublic["providers"].(map[string]interface{}); ok {
providerData = providers
}
}
a.Templates.ExecuteTemplate(w, "profile.html", map[string]interface{}{
"User": session.Identity,
"ProviderData": providerData,
})
}
func (a *App) logoutHandler(w http.ResponseWriter, r *http.Request) {
// Create logout flow
logoutFlow, _, err := a.Client.FrontendApi.CreateBrowserLogoutFlow(context.Background()).
Cookie(r.Header.Get("Cookie")).
Execute()
if err != nil {
// User is probably not logged in
http.Redirect(w, r, "/", http.StatusFound)
return
}
// Redirect to logout URL
http.Redirect(w, r, logoutFlow.LogoutUrl, http.StatusFound)
}
func main() {
app := NewApp()
mux := http.NewServeMux()
// Public routes
mux.HandleFunc("/", app.homeHandler)
mux.HandleFunc("/login", app.loginHandler)
mux.HandleFunc("/login-submit", app.loginSubmitHandler)
mux.HandleFunc("/registration", app.registrationHandler)
mux.HandleFunc("/logout", app.logoutHandler)
// Protected routes
dashboard := app.authMiddleware(http.HandlerFunc(app.dashboardHandler))
profile := app.authMiddleware(http.HandlerFunc(app.profileHandler))
mux.Handle("/dashboard", dashboard)
mux.Handle("/profile", profile)
// Serve static files
fs := http.FileServer(http.Dir("static"))
mux.Handle("/static/", http.StripPrefix("/static/", fs))
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("Server starting on port %s", port)
if err := http.ListenAndServe(":"+port, mux); err != nil {
log.Fatal(err)
}
}
Example HTML Templates:
templates/home.html
<!doctype html>
<html>
<head>
<title>Home</title>
<link rel="stylesheet" href="/static/styles.css" />
</head>
<body>
<div class="container">
<h1>Welcome to Our App</h1>
<p>This is a complete authentication example using Ory and Go.</p>
<div class="nav-links">
<a href="/login" class="button">Login</a>
<a href="/registration" class="button">Register</a>
</div>
</div>
</body>
</html>
templates/login.html
<!doctype html>
<html>
<head>
<title>Login</title>
<link rel="stylesheet" href="/static/styles.css" />
</head>
<body>
<div class="container">
<h1>Login</h1>
{{if .Error}}
<div class="error">{{.Error}}</div>
{{end}}
<form action="/login-submit?flow={{.Flow.Id}}" method="POST">
<div class="form-group">
<label for="identifier">Email:</label>
<input type="text" id="identifier" name="identifier" required />
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" name="password" required />
</div>
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}" />
<button type="submit" class="button">Login</button>
</form>
<div class="register-link">
<a href="/registration">Don't have an account? Register</a>
</div>
{{if .SocialProviders}}
<div class="social-providers">
<h3>Or sign in with:</h3>
{{range .SocialProviders}}
<a href="{{.URL}}" class="social-button"> {{.Label}} </a>
{{end}}
</div>
{{end}}
</div>
</body>
</html>
templates/dashboard.html
<!doctype html>
<html>
<head>
<title>Dashboard</title>
<link rel="stylesheet" href="/static/styles.css" />
</head>
<body>
<div class="container">
<h1>Welcome to Your Dashboard</h1>
<p>Hello, {{if .User.Traits.name}}{{.User.Traits.name}}{{else}}{{.User.Traits.email}}{{end}}</p>
<nav>
<ul>
<li><a href="/profile">Your Profile</a></li>
<li><a href="/logout">Logout</a></li>
</ul>
</nav>
<div class="dashboard-content">
<h2>Dashboard Content</h2>
<p>This is your protected dashboard. Only authenticated users can see this.</p>
</div>
</div>
</body>
</html>
Recommended Directory Structure
Here's a recommended directory structure for your authentication-enabled application:
- JavaScript/Node.js
- React
- Next.js
- Go
- cURL
my-auth-app/
├── node_modules/
├── public/
│ └── styles.css
├── views/
│ ├── dashboard.ejs
│ ├── error.ejs
│ ├── home.ejs
│ ├── login.ejs
│ ├── profile.ejs
│ └── registration.ejs
├── middleware/
│ └── auth.js
├── routes/
│ ├── auth.js
│ └── dashboard.js
├── app.js
├── package.json
└── .env
my-auth-app/
├── node_modules/
├── public/
│ ├── index.html
│ └── favicon.ico
├── src/
│ ├── components/
│ │ ├── Dashboard.jsx
│ │ ├── Layout.jsx
│ │ ├── Login.jsx
│ │ ├── Profile.jsx
│ │ ├── ProtectedRoute.jsx
│ │ └── Registration.jsx
│ ├── lib/
│ │ └── ory.js
│ ├── App.jsx
│ ├── index.jsx
│ └── styles.css
├── package.json
└── .env
my-auth-app/
├── static/
│ └── styles.css
├── templates/
│ ├── dashboard.html
│ ├── error.html
│ ├── home.html
│ ├── login.html
│ ├── profile.html
│ └── registration.html
├── main.go
├── auth.go
├── handlers.go
└── .env