WAY Auth Service
API Documentation
Overview
WAY Auth is a standalone JWT authentication service for email/password logins. It issues short-lived access tokens, maintains refresh sessions via HttpOnly cookies, and publishes a JWKS endpoint so your backends can verify tokens without storing private keys.
Quickstart Flow
Create a user
Call /api/v1/signup with email + password and x-way-signup-secret.
Log in
Call /api/v1/login to set the refresh cookie and receive an access token.
Use the access token
Send Authorization: Bearer <token> to /api/v1/me or your own APIs.
Refresh when needed
Call /api/v1/refresh with credentials included to rotate sessions and get a new access token.
Client login example
const response = await fetch('https://way-my-auth-service.vercel.app/api/v1/login', {
method: 'POST',
headers: { 'content-type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ email, password }),
});
const payload = await response.json();
// payload.accessToken contains the JWTRefresh example
const refreshed = await fetch('https://way-my-auth-service.vercel.app/api/v1/refresh', {
method: 'POST',
credentials: 'include',
});
const data = await refreshed.json();
// data.accessToken is a new JWTAuthentication Model
The service returns access tokens in JSON responses and stores refresh tokens in an HttpOnly cookie. Keep access tokens in memory and refresh them when they expire. Use the JWKS endpoint to verify tokens on your backend.
Signup secret
Bearer access tokens
Token Details
Access Token TTL
15 min
Signed with RS256 using JWT_ISSUER and JWT_AUDIENCE.
Refresh Token TTL
30 days
Rotated on every refresh to prevent replay.
JWT Claims
sub, sid
Standard claims include iss, aud, iat, exp, jti.
JWKS
/api/v1/jwks
Use the published key set to verify JWTs server-side.
CORS
CORS is enforced against a server-managed allowlist. Same-origin requests are always allowed. If you need a browser app from another origin, add it via the admin CORS endpoints.
Allowed headers
content-type, authorization, x-way-signup-secret
Allowed methods
GET, POST, PATCH, DELETE, OPTIONS
Rate Limits
Sliding-window limits are enforced per IP. When exceeded, the API returns 429 with error code rate_limited.
/api/v1/signup
5 / 10 min
/api/v1/login
10 / 10 min
/api/v1/refresh
20 / 10 min
Error Format
All error responses are JSON with a consistent error.code and error.message.
{
"error": {
"code": "invalid_input",
"message": "Request payload is invalid."
}
}Endpoints
Each endpoint below includes required auth, payloads, and examples.
Auth Endpoints
Core email/password auth and JWT issuance.
Create account
Create a user and issue access + refresh tokens.
Auth
Required signup secret header.
Rate Limit
5 requests / 10 minutes / IP
Headers
- content-type: application/json
- x-way-signup-secret: <secret>
Request body
{
"email": "you@example.com",
"password": "strong-password"
}Response
{
"user": {
"id": "user_123",
"email": "you@example.com"
},
"accessToken": "<jwt>",
"tokenType": "Bearer",
"expiresIn": 900
}Errors
Example request
curl -X POST https://way-my-auth-service.vercel.app/api/v1/signup \
-H 'content-type: application/json' \
-H 'x-way-signup-secret: <secret>' \
-d '{"email":"you@example.com","password":"strong-password"}'Example response
{
"user": {
"id": "user_123",
"email": "you@example.com"
},
"accessToken": "<jwt>",
"tokenType": "Bearer",
"expiresIn": 900
}Log in
Authenticate credentials and issue access + refresh tokens.
Auth
Email/password.
Rate Limit
10 requests / 10 minutes / IP
Headers
- content-type: application/json
Request body
{
"email": "you@example.com",
"password": "strong-password"
}Response
{
"user": {
"id": "user_123",
"email": "you@example.com"
},
"accessToken": "<jwt>",
"tokenType": "Bearer",
"expiresIn": 900
}Errors
Example request
curl -X POST https://way-my-auth-service.vercel.app/api/v1/login \
-H 'content-type: application/json' \
-d '{"email":"you@example.com","password":"strong-password"}'Example response
{
"user": {
"id": "user_123",
"email": "you@example.com"
},
"accessToken": "<jwt>",
"tokenType": "Bearer",
"expiresIn": 900
}Refresh access token
Rotate the refresh session and issue a new access token. No request body is required.
Auth
Refresh token cookie.
Rate Limit
20 requests / 10 minutes / IP
Headers
- cookie: <refresh cookie>
Response
{
"accessToken": "<jwt>",
"tokenType": "Bearer",
"expiresIn": 900
}Errors
Example request
curl -X POST https://way-my-auth-service.vercel.app/api/v1/refresh \
--cookie 'way_refresh=<refresh-token>'Example response
{
"accessToken": "<jwt>",
"tokenType": "Bearer",
"expiresIn": 900
}Log out
Revoke the refresh session and clear the refresh cookie.
Auth
Refresh token cookie (if present).
Headers
- cookie: <refresh cookie>
Response
{
"success": true
}Example request
curl -X POST https://way-my-auth-service.vercel.app/api/v1/logoutExample response
{
"success": true
}Get current user
Resolve the current user from the access token.
Auth
Bearer access token.
Headers
- authorization: Bearer <access-token>
Response
{
"user": {
"id": "user_123",
"email": "you@example.com"
},
"sessionId": "session_123"
}Errors
Example request
curl https://way-my-auth-service.vercel.app/api/v1/me \
-H 'authorization: Bearer <access-token>'Example response
{
"user": {
"id": "user_123",
"email": "you@example.com"
},
"sessionId": "session_123"
}JWKS
Fetch public keys used to verify access tokens.
Auth
None.
Errors
Example request
curl https://way-my-auth-service.vercel.app/api/v1/jwksExample response
{
"keys": [
{
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"kid": "<kid>",
"n": "<modulus>",
"e": "AQAB"
}
]
}Discovery configuration
Fetch issuer, audience, JWKS URL, and endpoint URLs for SDK auto-configuration.
Auth
None.
Example request
curl https://way-my-auth-service.vercel.app/.well-known/way-auth-configurationExample response
{
"version": "1",
"issuer": "https://way-my-auth-service.vercel.app",
"audience": "way-clients",
"jwks_url": "https://way-my-auth-service.vercel.app/api/v1/jwks",
"endpoints": {
"signup": "https://way-my-auth-service.vercel.app/api/v1/signup",
"login": "https://way-my-auth-service.vercel.app/api/v1/login",
"refresh": "https://way-my-auth-service.vercel.app/api/v1/refresh",
"logout": "https://way-my-auth-service.vercel.app/api/v1/logout",
"me": "https://way-my-auth-service.vercel.app/api/v1/me"
}
}Admin: Users, CORS, Sessions
Manage users, allowed cross-origin apps, and refresh sessions via admin-only routes.
List CORS origins
List allowed origins.
Auth
Admin refresh session cookie.
Headers
- cookie: <refresh cookie>
Response
{
"origins": [
{
"id": "origin_123",
"origin": "https://app.example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-01T12:00:00.000Z"
}
]
}Errors
Example request
curl https://way-my-auth-service.vercel.app/api/v1/admin/corsExample response
{
"origins": [
{
"id": "origin_123",
"origin": "https://app.example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-01T12:00:00.000Z"
}
]
}Add CORS origin
Add an allowed origin (http/https).
Auth
Admin refresh session cookie.
Headers
- content-type: application/json
- cookie: <refresh cookie>
Request body
{
"origin": "https://app.example.com"
}Response
{
"origin": {
"id": "origin_123",
"origin": "https://app.example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-01T12:00:00.000Z"
}
}Errors
Example request
curl -X POST https://way-my-auth-service.vercel.app/api/v1/admin/cors \
-H 'content-type: application/json' \
-d '{"origin":"https://app.example.com"}'Example response
{
"origin": {
"id": "origin_123",
"origin": "https://app.example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-01T12:00:00.000Z"
}
}Remove CORS origin
Remove an allowed origin by id.
Auth
Admin refresh session cookie.
Headers
- cookie: <refresh cookie>
Response
{
"success": true
}Errors
Example request
curl -X DELETE https://way-my-auth-service.vercel.app/api/v1/admin/cors/origin_123Example response
{
"success": true
}List users
List enrolled users with pagination.
Auth
Admin refresh session cookie.
Headers
- cookie: way_refresh=<admin_refresh_session_cookie>
Response
{
"users": [
{
"id": "user_123",
"email": "you@example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-01T12:00:00.000Z"
}
],
"currentPage": 1,
"pageSize": 50,
"totalCount": 1,
"totalPages": 1
}Errors
Example request
curl https://way-my-auth-service.vercel.app/api/v1/admin/usersExample response
{
"users": [
{
"id": "user_123",
"email": "you@example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-01T12:00:00.000Z"
}
],
"currentPage": 1,
"pageSize": 50,
"totalCount": 1,
"totalPages": 1
}Create user
Create a user credential directly from admin.
Auth
Admin refresh session cookie.
Headers
- content-type: application/json
- cookie: way_refresh=<admin_refresh_session_cookie>
Request body
{
"email": "you@example.com",
"password": "strong-password"
}Response
{
"user": {
"id": "user_123",
"email": "you@example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-01T12:00:00.000Z"
}
}Errors
Example request
curl -X POST https://way-my-auth-service.vercel.app/api/v1/admin/users \
-H 'content-type: application/json' \
-d '{"email":"you@example.com","password":"strong-password"}'Example response
{
"user": {
"id": "user_123",
"email": "you@example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-01T12:00:00.000Z"
}
}Update user credentials
Update a user's email and/or password.
Auth
Admin refresh session cookie.
Headers
- content-type: application/json
- cookie: way_refresh=<admin_refresh_session_cookie>
Request body
{
"email": "updated@example.com",
"password": "new-strong-password"
}Response
{
"user": {
"id": "user_123",
"email": "updated@example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-02T12:00:00.000Z"
}
}Errors
Example request
curl -X PATCH https://way-my-auth-service.vercel.app/api/v1/admin/users/user_123 \
-H 'content-type: application/json' \
-d '{"email":"updated@example.com"}'Example response
{
"user": {
"id": "user_123",
"email": "updated@example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-02T12:00:00.000Z"
}
}Delete user
Delete a user and cascade-delete their sessions.
Auth
Admin refresh session cookie.
Headers
- cookie: way_refresh=<admin_refresh_session_cookie>
Response
{
"success": true,
"user": {
"id": "user_123",
"email": "you@example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-01T12:00:00.000Z"
}
}Errors
Example request
curl -X DELETE https://way-my-auth-service.vercel.app/api/v1/admin/users/user_123Example response
{
"success": true,
"user": {
"id": "user_123",
"email": "you@example.com",
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-01T12:00:00.000Z"
}
}List sessions
List refresh sessions with user metadata.
Auth
Admin refresh session cookie.
Headers
- cookie: way_refresh=<admin_refresh_session_cookie>
Response
{
"sessions": [
{
"id": "session_123",
"user": {
"id": "user_123",
"email": "you@example.com"
},
"createdAt": "2025-01-01T12:00:00.000Z",
"expiresAt": "2025-02-01T12:00:00.000Z",
"revokedAt": null,
"replacedBySessionId": null,
"status": "active"
}
]
}Errors
Example request
curl https://way-my-auth-service.vercel.app/api/v1/admin/sessionsExample response
{
"sessions": [
{
"id": "session_123",
"user": {
"id": "user_123",
"email": "you@example.com"
},
"createdAt": "2025-01-01T12:00:00.000Z",
"expiresAt": "2025-02-01T12:00:00.000Z",
"revokedAt": null,
"replacedBySessionId": null,
"status": "active"
}
]
}Revoke session
Revoke a refresh session by id.
Auth
Admin refresh session cookie.
Headers
- cookie: way_refresh=<admin_refresh_session_cookie>
Response
{
"session": {
"id": "session_123",
"user": {
"id": "user_123",
"email": "you@example.com"
},
"createdAt": "2025-01-01T12:00:00.000Z",
"expiresAt": "2025-02-01T12:00:00.000Z",
"revokedAt": "2025-01-05T12:00:00.000Z",
"replacedBySessionId": null,
"status": "revoked"
}
}Errors
Example request
curl -X DELETE https://way-my-auth-service.vercel.app/api/v1/admin/sessions/session_123Example response
{
"session": {
"id": "session_123",
"user": {
"id": "user_123",
"email": "you@example.com"
},
"createdAt": "2025-01-01T12:00:00.000Z",
"expiresAt": "2025-02-01T12:00:00.000Z",
"revokedAt": "2025-01-05T12:00:00.000Z",
"replacedBySessionId": null,
"status": "revoked"
}
}Admin Access
Admin endpoints require a valid refresh session cookie and an email present in the ADMIN_EMAILS allowlist. These routes are intended for managing CORS origins and reviewing active refresh sessions.
Admin emails
Debug Endpoint
Use GET /api/v1/_debug/cookie in development to confirm the refresh cookie is present. Returns 404 outside of development.
{
"cookieName": "way_refresh",
"present": true,
"checkedAt": "2025-01-01T12:00:00.000Z"
}