IndieAuth/OAuth 2.0 server with passkey authentication
overview
Indiko is a self-hosted IndieAuth/OAuth 2.0 authorization server with passwordless authentication using WebAuthn
passkeys.
It provides single sign-on (SSO) for your apps and services.
key features
Passwordless authentication via WebAuthn passkeys
Full IndieAuth and OAuth 2.0 support with PKCE
Access tokens and refresh tokens for API access
Token introspection and revocation endpoints
UserInfo endpoint for profile data
Auto-registration of OAuth clients
Pre-registered clients with secrets and role management
Session-based SSO (authenticate once, authorize many apps)
User profile endpoints with h-card microformats
Invite-based user registration
getting started
for app developers
To integrate with Indiko as an OAuth client, you'll need:
A client ID (any valid URL, e.g., https://myapp.example.com)
A redirect URI (where users return after authorization)
Support for PKCE (code challenge/verifier)
Auto-registration:
Apps are automatically registered on first use. You don't need admin approval to get started.
During registration, Indiko fetches your client metadata from your client_id URL to validate redirect URIs and display your app name/logo.
For advanced features like client secrets and role assignment, contact your Indiko admin to pre-register your app.
publishing client metadata (recommended)
To help Indiko verify your app and display proper branding, publish client metadata as JSON at your client_id URL:
Security:
If your redirect_uri uses a different host than your client_id, you MUST publish redirect_uris in your client metadata. This prevents unauthorized apps from hijacking your client_id.
for users
You'll need an invite code to create an account. Once registered:
Set up your passkey (fingerprint, face ID, or security key)
Complete your profile (name, photo, website)
Authorize apps to access your profile
Manage app permissions from your dashboard
sign in button
Copy this themed button for your app's login page. It matches Indiko's visual style:
Customization:
Replace YOUR_OAUTH_URL_HERE with your authorization URL (see authorization flow below). You can also change the button text or adjust colors to match your app's theme.
API endpoints
authorization endpoints
Endpoint
Method
Description
/.well-known/oauth-authorization-server
GET
IndieAuth server metadata (discovery endpoint)
/auth/authorize
GET
Start OAuth authorization flow
/auth/authorize
POST
Submit consent/scope approval
/auth/token
POST
Exchange code for access token and refresh token
/auth/token/introspect
POST
Verify access token validity
/auth/token/revoke
POST
Revoke access or refresh token
/userinfo
GET
Get user profile data with bearer token
/u/:username
GET
Public user profile (h-card with discovery links)
authentication endpoints
Endpoint
Method
Description
/auth/can-register
POST
Check if invite code is valid
/auth/register/options
POST
Get WebAuthn registration options
/auth/register/verify
POST
Complete passkey registration
/auth/login/options
POST
Get WebAuthn login options
/auth/login/verify
POST
Complete passkey login
/auth/logout
POST
End current session
authorization flow
0. discovery (recommended)
Before starting authorization, clients should discover the authorization server's endpoints from the user's profile URL:
Fetch the user's profile URL (e.g., http://localhost:3000/u/username)
Look for <link rel="indieauth-metadata"> tag or HTTP Link: header
Fetch the metadata endpoint to get authorization_endpoint and token_endpoint
Security:
The iss (issuer) parameter allows you to verify the response came from the expected authorization server. Compare it to the issuer from the metadata endpoint.
Client authentication:
All clients MUST use PKCE (code_verifier) per the IndieAuth specification. Pre-registered confidential clients should also include client_secret in the token request for additional security.
access_token - Short-lived token (1 hour) for API access
refresh_token - Long-lived token (30 days) for getting new access tokens
Roles:
If an admin has assigned a role to this user for your app, it will be included in the response. Roles are
arbitrary strings that you can use for role-based access control (RBAC) in your application.
token management
Indiko provides a complete OAuth 2.0 token management system with access tokens, refresh tokens, introspection, and revocation.
refresh tokens
Access tokens expire after 1 hour. Use the refresh token to get a new access token without requiring user re-authentication:
Note:
The response only includes data for scopes granted to the token. A token with only profile scope will not include email.
scopes
Scope
Description
Data Included
profile
Basic profile information
name, photo, URL
email
Email address
email
Note:
Users can selectively approve scopes during authorization. Your app may receive fewer scopes than requested.
roles
Roles enable role-based access control (RBAC) in your applications. Only pre-registered clients with client secrets support role assignment.
Pre-registration required:
To use roles, contact your Indiko admin to pre-register your app with a client secret. Auto-registered (public) clients cannot use roles.
how roles work
Roles are assigned by admins for specific user-app combinations
Role strings are arbitrary (e.g., "admin", "editor", "viewer")
Only one role per user per app
Included in token response if assigned
Your app interprets the role string and enforces permissions
Example use case:
A CMS app could use roles like "admin", "editor", and "viewer". When
users authenticate via Indiko, the app checks their role and grants appropriate permissions.
defining app roles
Apps can define available roles in three ways:
Disabled (default): Leave "Available Roles" empty. No roles can be assigned.
Predefined roles: Specify allowed roles (one per line). Creates a dropdown for role selection, preventing typos.
Default role: Automatically assign a role when users first authorize your app.
assigning roles
Roles can be assigned in multiple ways:
Default role (automatic): If configured, users automatically receive the default role on first authorization.
Via invite codes: Admins can create invites with pre-assigned roles for specific apps. New
users automatically get those roles on signup.
Via admin dashboard: Admins can assign or change roles for existing users in the clients
management interface.
Note:
Roles are optional. If no role is assigned, the role field will not appear in the token response.
client types
auto-registered clients
Any app can use Indiko without pre-registration. On first authorization, Indiko will:
Validate the client ID (must be a valid URL per IndieAuth spec)
Fetch client metadata from the client_id URL (if available)
Validate redirect_uri against published redirect_uris (if different host)
Extract and store client name and logo (if provided)
Automatically register the client for future use
Auto-registered clients:
Client ID format: Any valid URL (e.g., https://myapp.example.com)
Authentication: MUST use PKCE only (no client secret)
Limitations: Cannot use client secrets or role assignment
Security:
For redirect URIs on different hosts than your client_id, you must publish redirect_uris in your client metadata. See getting started for details.
pre-registered clients
Admins can pre-register clients for advanced features. All pre-registered clients require a client secret and must also use PKCE.
Pre-registered clients:
Client ID format: Generated with ikc_ prefix (e.g., ikc_xxxxxxxxxxxxxxxxxxxxx)
Client secret format: Generated with iks_ prefix (shown once on creation)
Authentication: MUST use both PKCE AND client_secret in token requests
Role assignment: Admins can assign per-user roles for RBAC
Available roles: Define which roles can be assigned (enforces dropdown selection)
Default role: Automatically assigned to users on first authorization
Metadata: Custom name, logo, description
Tip:
Contact your Indiko admin to pre-register your app if you need client authentication or role-based access
control.
OAuth tester
Test the OAuth flow with a live interactive client. This simulates how your app would integrate with Indiko.
callback received
You've been redirected back with an authorization code. Click below to exchange it for user data.
result
How it works:
This page uses the current URL as the redirect URI. After authorization, the code is automatically detected and
you can exchange it for user profile data.