# Session Quick Start

Sessions are how your application remembers that a user is logged in. This guide will get you from zero to a working login/logout flow in just a few minutes.

By the end, you’ll have:

- A backend endpoint that creates sessions on login
- Session validation for protected routes
- A complete logout flow

## Prerequisites

Before starting, make sure you have:

1. [PropelAuth BYO installed and running](https://docs.byo.propelauth.com/getting-started/installation)
2. [Backend client library set up](https://docs.byo.propelauth.com/getting-started/backend-setup)
3. A way to authenticate users (we’ll assume you have this - could be email/password, OAuth, etc.)

## Step 1: Create Your First Session

When a user successfully logs in, create a session for them and store it as an HTTP-only cookie:

### Node
```
app.post("/api/login", async (req, res) => {

// Verify the user's credentials (your existing logic)

const userId = await verifyUserCredentials(req.body);

const result = await client.session.create({

userId: userId,

});

if (!result.ok) {

console.error("Session creation failed:", result.error);

return res.status(500).json({ error: "Failed to create session" });

}

// Store the session token as an HTTP-only cookie

res.cookie("sessionToken", result.data.sessionToken, {

httpOnly: true,

secure: true,

sameSite: "lax",

});

res.json({ success: true });
});
```

### Python
```
class LoginRequest(BaseModel):

# Your login fields

pass

@app.post("/api/login")
async def login(request: LoginRequest, response: Response):
    # Verify the user's credentials (your existing logic)
    user_id = await verify_user_credentials(request)

result = await client.session.create(user_id=user_id)

if is_err(result):
        print("Session creation failed:", result.error)
        raise HTTPException(status_code=500, detail="Failed to create session")

# Store the session token as an HTTP-only cookie
    response.set_cookie(
        key="sessionToken",
        value=result.data.session_token,
        httponly=True,
        secure=True,
        samesite="lax"
    )

return {"success": True}
```

### Go
```
mux.HandleFunc("POST /api/login", func(w http.ResponseWriter, r *http.Request) {
    // Verify the user's credentials (your existing logic)
    userID := verifyUserCredentials(r)

result, err := client.Session.Create(r.Context(), byo.CreateSessionCommand{
        UserID: userID,
    })
    if err != nil {
        log.Println("Session creation failed:", err)
        http.Error(w, "Failed to create session", http.StatusInternalServerError)
        return
    }

// Store the session token as an HTTP-only cookie
    http.SetCookie(w, &http.Cookie{
        Name:     "sessionToken",
        Value:    result.SessionToken,
        HttpOnly: true,
        Secure:   true,
        SameSite: http.SameSiteLaxMode,
    })
    json.NewEncoder(w).Encode(map[string]any{"success": true})
})
```

### Java
```
@PostMapping("/api/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request, HttpServletResponse response) {
    // Verify the user's credentials (your existing logic)
    String userId = verifyUserCredentials(request);

try {
        CreateSessionResponse result = client.session.create(
            CreateSessionCommand.builder()
                .userId(userId)
                .build()
        );

// Store the session token as an HTTP-only cookie
        Cookie cookie = new Cookie("sessionToken", result.getSessionToken());
        cookie.setHttpOnly(true);
        cookie.setSecure(true);
        cookie.setPath("/");
        response.addCookie(cookie);

return ResponseEntity.ok(Map.of("success", true));
    } catch (CreateSessionException e) {
        System.err.println("Session creation failed: " + e.getMessage());
        return ResponseEntity.status(500).body(Map.of("error", "Failed to create session"));
    }
}
```

### .NET
```
[HttpPost("/api/login")]
public async Task<IActionResult> Login([FromBody] LoginRequest request)
{
    // Verify the user's credentials (your existing logic)
    var userId = await VerifyUserCredentials(request);
    try
    {
        var result = await client.Session.CreateAsync(new CreateSessionCommand
        {
            UserId = userId
        });

// Store the session token as an HTTP-only cookie
        Response.Cookies.Append("sessionToken", result.SessionToken, new CookieOptions
        {
            HttpOnly = true,
            Secure = true,
            SameSite = SameSiteMode.Lax
        });
        return Ok(new { Success = true });
    }
    catch (CreateSessionException ex)
    {
        Console.Error.WriteLine($"Session creation failed: {ex.Message}");
        return StatusCode(500, new { Error = "Failed to create session" });
    }
}
```

Now when the user logs in, they’ll receive a cookie that automatically gets sent with every request.

## Step 2: Validate Sessions on Protected Routes

For any route that requires authentication, validate the session token:

### Node
```
app.get("/api/protected-route", async (req, res) => {
    const sessionToken = req.cookies.sessionToken;
    const validation = await client.session.validate({
        sessionToken,
    });
    if (!validation.ok) {
        console.error("Session validation failed:", validation.error);
        return res.status(401).json({ error: "Unauthorized" });
    }
    // The session is valid! You can access the user ID and metadata
    const { userId, metadata } = validation.data;
    res.json({
        message: "You're authenticated!",
        userId,
        metadata,
    });
});
```

### Python
```
@app.get("/api/protected-route")
async def protected_route(request: Request):
    session_token = request.cookies.get("sessionToken")
    validation = await client.session.validate(session_token=session_token)
    if is_err(validation):
        print("Session validation failed:", validation.error)
        raise HTTPException(status_code=401, detail="Unauthorized")
    user_id = validation.data.user_id
    metadata = validation.data.metadata
    return {
        "message": "You're authenticated!",
        "userId": user_id,
        "metadata": metadata,
    }
```

### Go
```
mux.HandleFunc("GET /api/protected-route", func(w http.ResponseWriter, r *http.Request) {
    cookie, err := r.Cookie("sessionToken")
    if err != nil {
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }
    validation, err := client.Session.Validate(r.Context(), byo.ValidateSessionCommand{
        SessionToken: byo.String(cookie.Value),
    })
    if err != nil {
        log.Println("Session validation failed:", err)
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }
    json.NewEncoder(w).Encode(map[string]any{
        "message":  "You're authenticated!",
        "userId":   validation.UserID,
        "metadata": validation.Metadata,
    })
})
```

### Java
```
@GetMapping("/api/protected-route")
public ResponseEntity<?> protectedRoute(@CookieValue(name = "sessionToken", required = false) String sessionToken) {
    try {
        ValidateSessionResponse validation = client.session.validate(
            ValidateSessionCommand.builder()
                .sessionToken(sessionToken)
                .build()
        );
        String userId = validation.getUserId();
        JsonValue metadata = validation.getMetadata();
        return ResponseEntity.ok(Map.of(
            "message", "You're authenticated!",
            "userId", userId,
            "metadata", metadata
        ));
    } catch (ValidateSessionException e) {
        System.err.println("Session validation failed: " + e.getMessage());
        return ResponseEntity.status(401).body(Map.of("error", "Unauthorized"));
    }
}
```

### .NET
```
[HttpGet("/api/protected-route")]
public async Task<IActionResult> ProtectedRoute()
{
    var sessionToken = Request.Cookies["sessionToken"];
    try
    {
        var validation = await client.Session.ValidateAsync(new ValidateSessionCommand
        {
            SessionToken = sessionToken
        });
        var userId = validation.UserId;
        var metadata = validation.Metadata;
        return Ok(new
        {
            Message = "You're authenticated!",
            UserId = userId,
            Metadata = metadata
        });
    }
    catch (ValidateSessionException ex)
    {
        Console.Error.WriteLine($"Session validation failed: {ex.Message}");
        return StatusCode(401, new { Error = "Unauthorized" });
    }
}
```

The Validate Session function checks if:

- The token exists and is valid
- The session hasn’t expired
- The session hasn’t been invalidated

If validation succeeds, you get back the user ID and any metadata you stored when creating the session. If validation fails, you get a detailed error explaining exactly why (e.g. IP address not allowed, session expired, etc.).

## Step 3: Add a Frontend

Now let’s connect a frontend. Here’s a simple login form that sends credentials to your backend:

```
function LoginForm() {
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");
    const handleLogin = async (e) => {
        e.preventDefault();
        const response = await fetch("/api/login", {
            method: "POST",
            credentials: "include", // Important: include cookies
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ email, password }),
        });
        if (response.ok) {
            // Redirect to dashboard or protected area
            window.location.href = "/dashboard";
        } else {
            // Handle error
            const error = await response.json();
            alert(error.error);
        }
    };
    return (
        <form onSubmit={handleLogin}>
            <input
                type="email"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                placeholder="Email"
                required
            />
            <input
                type="password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                placeholder="Password"
                required
            />
            <button type="submit">Log In</button>
        </form>
    );
}
```

## Step 4: Handle Logout

When users log out, invalidate their session to ensure it can’t be used again:

### Node
```
app.post("/api/logout", async (req, res) => {
    const sessionToken = req.cookies.sessionToken;
    await client.session.invalidateByToken({
        sessionToken,
    });
    // Clear the cookie
    res.clearCookie("sessionToken");
    res.json({ success: true });
});
```

### Python
```
@app.post("/api/logout")
async def logout(request: Request, response: Response):
    session_token = request.cookies.get("sessionToken")
    await client.session.invalidate_by_token(session_token=session_token)
    response.delete_cookie("sessionToken")
    return {"success": True}
```

### Go
```
mux.HandleFunc("POST /api/logout", func(w http.ResponseWriter, r *http.Request) {
    cookie, err := r.Cookie("sessionToken")
    if err == nil {
        _, _ = client.Session.InvalidateByToken(r.Context(), byo.InvalidateSessionByTokenCommand{
            SessionToken: byo.String(cookie.Value),
        })
    }
    http.SetCookie(w, &http.Cookie{Name: "sessionToken", Value: "", MaxAge: -1})
    json.NewEncoder(w).Encode(map[string]any{"success": true})
})
```

### Java
```
@PostMapping("/api/logout")
public ResponseEntity<?> logout(@CookieValue(name = "sessionToken", required = false) String sessionToken, HttpServletResponse response) {
    client.session.invalidateByToken(
        InvalidateSessionByTokenCommand.builder()
            .sessionToken(sessionToken)
            .build()
    );
    Cookie cookie = new Cookie("sessionToken", "");
    cookie.setMaxAge(0);
    response.addCookie(cookie);
    return ResponseEntity.ok(Map.of("success", true));
}
```

### .NET
```
[HttpPost("/api/logout")]
public async Task<IActionResult> Logout()
{
    var sessionToken = Request.Cookies["sessionToken"];
    await client.Session.InvalidateByTokenAsync(new InvalidateSessionByTokenCommand
    {
        SessionToken = sessionToken
    });
    Response.Cookies.Delete("sessionToken");
    return Ok(new { Success = true });
}
```

And the corresponding frontend:
```
function LogoutButton() {
    const handleLogout = async () => {
        const response = await fetch("/api/logout", {
            method: "POST",
            credentials: "include", // Include the session cookie
        });
        if (response.ok) {
            // Redirect to login page
            window.location.href = "/login";
        }
    };
    return <button onClick={handleLogout}>Log Out</button>;
}
```

## You’re Done!

You now have a working session management system. Users can log in, stay logged in across requests, and securely log out.

## Adding Security Features

Your basic session management is working, but you can enhance security with just a few additions:

### Detect User Agent Changes

When creating the session and when validating, include the user agent:

### Node
```javascript
const session = await auth.session.create({
    userId: userId,
    userAgent: req.headers["user-agent"],
});
```

### Python
```python
session = await client.session.create(
    user_id=user_id,
    user_agent=request.headers.get("user-agent")
)
```

### Go
```go
session, err := client.Session.Create(ctx, byo.CreateSessionCommand{
    UserID:    userID,
    UserAgent: byo.String(r.UserAgent()),
})
```

### Java
```java
CreateSessionResponse session = client.session.create(
    CreateSessionCommand.builder()
        .userId(userId)
        .userAgent(request.getHeader("User-Agent"))
        .build()
);
```

### .NET
```csharp
var session = await client.Session.CreateAsync(new CreateSessionCommand
{
    UserId = userId,
    UserAgent = Request.Headers["User-Agent"].ToString()
});
```

### Add IP Address Checks / Logging

When creating the session and when validating, include the IP address:

### Node
```javascript
const session = await client.session.create({
    userId: userId,
    ipAddress: req.ip,
});
```

### Python
```python
session = await client.session.create(
    user_id=user_id,
    ip_address=request.client.host
)
```

### Go
```go
session, err := client.Session.Create(ctx, byo.CreateSessionCommand{
    UserID:    userID,
    IPAddress: byo.String(r.RemoteAddr),
})
```

### Java
```java
CreateSessionResponse session = client.session.create(
    CreateSessionCommand.builder()
        .userId(userId)
        .ipAddress(request.getRemoteAddr())
        .build()
);
```

### Store User Data in Sessions

When creating the session, you can also store user metadata:

### Node
```javascript
const session = await client.session.create({
    userId: userId,
    metadata: {
        email: user.email,
        role: user.role,
        teamId: user.teamId,
    },
});
```

### Python
```python
session = await client.session.create(
    user_id=user_id,
    metadata={
        "email": user.email,
        "role": user.role,
        "teamId": user.team_id,
    }
)
```

## What’s Next?

Now that you have sessions working, you can:

- **Configure session behavior** 
- **Add device registration** 
- **Use session tags** 
- **Add JWTs**

Check out the [Session Overview](https://docs.byo.propelauth.com/sessions/overview) for a complete guide to all features, or dive into the [API Reference](https://docs.byo.propelauth.com/sessions/reference) for detailed documentation.
