Overview
Memos supports three authentication methods for API access:- JWT Access Tokens (V2) - Short-lived tokens (15 minutes) for active sessions
- Personal Access Tokens (PAT) - Long-lived tokens for scripts and automation
- Refresh Tokens - Long-lived tokens (30 days) for session renewal
Authorization header with Bearer token format.
See: server/auth/authenticator.go:17-166
Authentication Methods
JWT Access Tokens
Access tokens are short-lived JWT tokens used for authenticated API requests. They are stateless and validated by signature only. Characteristics:- Lifetime: 15 minutes
- Validation: Stateless (signature verification only)
- Storage: Store in memory only (never in localStorage)
- Format: Standard JWT with HS256 signing
Token type, always
"access"Issuer, always
"memos"Audience, always
["user.access-token"]Subject - user ID as string
Issued at timestamp (Unix epoch)
Expiration timestamp (iat + 15 minutes)
Username for display purposes
User role:
"USER", "ADMIN"User status:
"ACTIVE", "ARCHIVED"server/auth/token.go:133-160
Personal Access Tokens (PAT)
PATs are long-lived tokens designed for programmatic access, scripts, and third-party integrations. Characteristics:- Lifetime: Configurable (30 days to never expires)
- Validation: Stateful (database lookup required)
- Storage: Store securely (environment variables, secrets manager)
- Format:
memos_pat_prefix + 32 random characters - Hash: SHA-256 hash stored in database
server/auth/token.go:189-203
Refresh Tokens
Refresh tokens are long-lived tokens stored in HttpOnly cookies for session renewal. Characteristics:- Lifetime: 30 days
- Validation: Stateful (database lookup for revocation)
- Storage: HttpOnly cookie (
memos_refresh) - Rotation: New refresh token generated on each refresh
- Format: Standard JWT with HS256 signing
Token type, always
"refresh"Token ID (UUID) for database lookup and revocation
server/auth/token.go:162-187
Authentication Flow
Password Authentication
1. Sign In Request:server/router/api/v1/auth_service.go:64-189
2. Authenticated Request:
server/router/api/v1/connect_interceptors.go:177-227
Token Refresh Flow
When the access token expires (after 15 minutes), refresh it without re-authenticating: Refresh Request:- Client sends old refresh token
- Server validates old token
- Server generates new refresh token (fresh 30-day expiry)
- Server stores new token and removes old token
- Client receives new access token and new refresh token cookie
- Sliding window sessions - Active users stay logged in indefinitely
- Better security - Stolen refresh tokens become invalid after legitimate refresh
server/router/api/v1/auth_service.go:272-357
Personal Access Token Flow
1. Create Personal Access Token:server/router/api/v1/user_service.go (CreatePersonalAccessToken)
2. Use PAT for Authentication:
server/auth/authenticator.go:101-124
3. List Personal Access Tokens:
401 Unauthenticated.
Authentication Priority
When multiple authentication methods are present, Memos authenticates in this order:-
Access Token (V2) - Check
Authorization: Bearer <token>header- If prefix is NOT
memos_pat_, validate as JWT access token - Stateless validation (signature check only)
- If prefix is NOT
-
Personal Access Token - Check
Authorization: Bearer memos_pat_*header- If prefix is
memos_pat_, hash and lookup in database - Stateful validation (database query + expiration check)
- If prefix is
-
Refresh Token - Check
Cookie: memos_refresh=<token>- Used only for
/auth/refreshendpoint - Not used for general API authentication
- Used only for
server/auth/authenticator.go:136-165
Security Best Practices
Token Storage
Access Tokens
Access Tokens
DO:
- Store in memory only (JavaScript variable)
- Clear on logout
- Implement automatic refresh before expiry
- Never store in localStorage or sessionStorage
- Never store in cookies (use refresh token cookie instead)
- Never log tokens in console or analytics
Personal Access Tokens
Personal Access Tokens
DO:
- Store in environment variables for scripts
- Use secrets manager for production (AWS Secrets Manager, HashiCorp Vault)
- Set appropriate expiration (30-90 days)
- Rotate regularly
- Use descriptive names (“GitHub Actions CI”, “Mobile App”)
- Never commit to version control
- Never share tokens between services
- Never use the same token for multiple purposes
- Never set “never expires” for automated systems
Refresh Tokens
Refresh Tokens
DO:
- Rely on HttpOnly cookie (automatic handling)
- Implement token rotation (built-in)
- Clear on explicit logout
- Never access cookie from JavaScript
- Never transmit in URL parameters
- Never store client-side beyond cookie
HTTPS Configuration
Secure Cookie (HTTPS):Secure attribute is automatically added when the request origin is https://.
See: server/router/api/v1/auth_service.go:369-401
Token Revocation
Refresh Tokens:- Revoked on logout via
/api/v1/auth/signout - Automatically rotated on refresh
- Stored in
user_settingtable with expiration
- Manually revoked via DELETE endpoint
- Immediately invalidated (database removal)
- Cannot be restored after revocation
- Cannot be revoked (stateless)
- Short 15-minute lifetime minimizes risk
- To “revoke” access, sign out to remove refresh token
Examples
Complete Authentication Example (curl)
Complete PAT Example (curl)
SSO Authentication Example
server/router/api/v1/auth_service.go:91-170
Troubleshooting
”authentication required” (401)
Cause: Missing or invalid authentication token Solution:“invalid access token” (401)
Cause: Expired, malformed, or revoked token Solution:“refresh token not found” (401)
Cause: Refresh token cookie not sent or expired Solution:“PAT expired” (401)
Cause: Personal Access Token past expiration date Solution:“permission denied” (403)
Cause: Authenticated but insufficient permissions Solution:Get Current User
Returns the authenticated user’s information. Similar to OIDC’s
/userinfo endpoint.GET /api/v1/auth/me
Request
No parameters needed. The access token in theAuthorization header identifies the user.
Response
The authenticated user’s information
Example Response
Error Responses
Unauthenticated - Invalid or expired access token
Sign Out
Terminates the user’s authentication session by revoking the refresh token and clearing the authentication cookie.
POST /api/v1/auth/signout
Request
No parameters needed. The endpoint will:- Revoke the refresh token (from
memos_refreshcookie) - Clear the authentication cookie
- Invalidate the current session
The access token in the
Authorization header is still technically valid until expiry (15 minutes), but the refresh token is immediately revoked, preventing session renewal.Response
Returns an empty response on success.Cookie Changes
Thememos_refresh cookie is cleared:
Example
Error Responses
Unauthenticated - User not authenticated
Refresh Access Token
Exchanges a valid refresh token for a new access token. The refresh token is read from the
memos_refresh HttpOnly cookie.POST /api/v1/auth/refresh
Request
No request body needed. The refresh token is automatically read from thememos_refresh cookie.
Memos implements refresh token rotation for security. Each refresh generates a new refresh token and invalidates the old one.
Response
The new short-lived access token (15-minute expiry)
When the access token expires. Clients should refresh before this time.
Example Response
Cookie Changes
A newmemos_refresh cookie is set with a fresh 30-day expiry:
Token Rotation Flow
Example Usage
Error Responses
Unauthenticated - Refresh token missing, expired, or revokedCommon causes:
- No
memos_refreshcookie sent - Refresh token expired (older than 30 days)
- Token was revoked via sign out
- Token was already used (rotation invalidated it)
Next Steps
API Reference
Explore detailed endpoint documentation
SDKs & Libraries
Use official SDKs for your programming language