Documentation
Integrate in minutes.
SDKs for .NET, Go, and Python. Standard OIDC for everything else. Full REST API for programmatic access.
Getting Started
Quick Start
Install the NuGet, Go, or Python package, add two lines, and you have OIDC login, org switching, and logout. Or skip the SDK and validate JWTs directly.
// Install: dotnet add package Alkeyon.Sdk
builder.AddFFAuth();
app.MapFFAuthEndpoints();
// That's it. Login, callback, logout, org switching - all handled.
{
"FFAuth": {
"AuthDomain": "alkeyon.fastflow.tech",
"DefaultOrg": "your-org",
"ClientId": "your-app-client-id",
"ClientSecret": "...",
"Audience": "your-audience"
}
}
// go get gitea.lab/fastflowtech/auth/sdk/go-sdk
opts := &ffauth.Options{AuthDomain: "alkeyon.fastflow.tech"}
validator := ffauth.NewJWTValidator(opts)
mux.Handle("/api/", ffauth.AuthMiddleware(validator)(apiHandler))
ffauth.MapAuthEndpoints(mux, opts, validator, onAuth)
// Middleware, OIDC, tenant resolution - stdlib only.
// Any framework, any language - standard OIDC
builder.Services
.AddAuthentication("Bearer")
.AddJwtBearer(o => {
o.Authority = "https://alkeyon.fastflow.tech";
});
SDKs
Alkeyon.Sdk for .NET
Full OIDC Authorization Code + PKCE flow. Cookie auth, JWKS caching, org switching.
Install
dotnet add package Alkeyon.Sdk
What you get
-
GET
/auth/login- PKCE authorize redirect -
GET
/auth/callback- Code exchange + cookie sign-in -
GET
/auth/logout- Sign out + ALKEYON logout -
GET
/switch-org- Re-authenticate with different org
Configuration
AuthDomain
ALKEYON domain (e.g. alkeyon.fastflow.tech)
DefaultOrg
Default org slug for login
ClientId
OIDC client ID (from ALKEYON admin)
ClientSecret
OIDC client secret
Audience
Audience for legacy token validation
Claims available after sign-in
sub
email
name
org_slug
org_id
org_role
org_name
role
ffauth for Go
Stdlib only - net/http, no framework dependencies. OIDC, JWKS, tenant middleware.
Install
go get gitea.lab/fastflowtech/auth/sdk/go-sdk
What you get
-
func
AuthMiddleware- JWT cookie validation middleware -
func
TenantMiddleware- Subdomain org extraction -
func
MapAuthEndpoints- OIDC login/callback/logout -
func
NewJWTValidator- JWKS-cached token validation
Configuration
AuthDomain
ALKEYON domain (e.g. alkeyon.fastflow.tech)
DefaultOrg
Default org slug for login
ClientID
OIDC client ID
ClientSecret
OIDC client secret
BaseDomain
Product domain for tenant resolution
StateCookieSecret
32-byte hex key for OIDC state encryption
Design
-
Stdlib only -
net/http, no framework deps - DB-agnostic tenant middleware via callback
- AES-GCM encrypted state cookies
-
onAuthenticatedcallback for custom sessions
ffauth for Python
JWT validation for stateless API services. Thread-safe JWKS caching, RS256 verification, zero framework dependencies.
Install
pip install ffauth \
--extra-index-url http://srv-119-gitea.lab:3000/api/packages/fastflowtech/pypi/simple \
--trusted-host srv-119-gitea.lab
# pip install ffauth
from ffauth import JWTValidator, AuthOptions
validator = JWTValidator(AuthOptions(
auth_domain="alkeyon.fastflow.tech"
))
# In request handler:
auth = validator.validate(token)
print(auth.email, auth.org_slug, auth.roles)
from ffauth import AuthError
try:
auth = validator.validate(token)
except AuthError as e:
return {"error": str(e)}, 401
# Or use validate_or_none for silent failure:
auth = validator.validate_or_none(token)
if auth is None:
return {"error": "unauthorized"}, 401
AuthOptions
auth_domain
ALKEYON domain (e.g. alkeyon.fastflow.tech)
audience
Expected JWT audience. Empty disables check
issuer
Override JWT issuer. Defaults to https://{auth_domain}
jwks_ttl
JWKS cache TTL in seconds (default 3600)
AuthContext result
sub
email
org_slug
org_id
roles
claims
user_id
has_role()
is_admin_or_owner()
Design
- Thread-safe JWKS caching (configurable TTL)
- Stale cache fallback on JWKS fetch errors
- RS256 verification via PyJWT[crypto]
- No OIDC flows - designed for stateless APIs
RUN pip install --no-cache-dir -r requirements.txt \
--extra-index-url http://srv-119-gitea.lab:3000/api/packages/fastflowtech/pypi/simple \
--trusted-host srv-119-gitea.lab
REST API
Full programmatic access
Standard OIDC discovery, token exchange, user management, and org operations. All endpoints return JSON.
OIDC Discovery
/.well-known/openid-configuration
OIDC discovery document
/.well-known/jwks.json
RSA public keys for JWT validation
/v1/connect/token
Token exchange (authorization_code, client_credentials)
/v1/connect/userinfo
User info from access token
Authentication
/v1/auth/login
Email + password login, returns JWT
/v1/auth/mfa/verify
MFA TOTP verification
/v1/auth/refresh
Refresh token exchange
/v1/auth/token
Client credentials token (machine-to-machine)
/v1/auth/me
Current user profile
/v1/auth/introspect
Token introspection (RFC 7662)
Organizations
/v1/orgs
Create organization
/v1/orgs
List user's organizations
/v1/orgs/{slug}
Get organization details
/v1/orgs/{slug}/members
List org members
/v1/orgs/{slug}/apps
List OAuth clients
External Auth
/auth/external/{provider}
Initiate external login (Google, Microsoft, etc.)
/auth/callback/{provider}
External auth callback
/v1/auth/validate-key
Validate API key and return user context
/v1/auth/switch-org
Switch org context, re-issues JWT
Self-Registration
/Account/Register
Registration form (requires returnUrl with a valid client_id)
/Account/Register
Create account with email, password, and display name
Note: Registration is gated per OAuth application. Only apps with AllowSelfRegistration enabled will show the registration form. Users arriving without a valid client context see a "Registration is closed" message.
Guides
Three ways to authenticate
OIDC for browser apps, client credentials for service-to-service, API keys for scripts and integrations.
1 OIDC Authorization Code + PKCE
For browser-based apps. User logs in via ALKEYON, your app receives a JWT.
Flow
-
1.
Create an OAuth client in the ALKEYON admin panel. Note the
client_idandclient_secret. -
2.
Redirect the user to
/Connect/Authorizewith PKCE challenge, client ID, redirect URI, and scopes. -
3.
User authenticates on ALKEYON and is redirected back with an authorization
code. -
4.
Exchange the code at
POST /v1/connect/tokenfor access + refresh tokens. -
5.
Use the access token as
Bearerheader or create a cookie session.
Tip: The .NET, Go, and Python SDKs handle this entire flow for you. Use them unless you need custom control.
Token exchange
POST /v1/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code={authorization_code}
&client_id={your_client_id}
&client_secret={your_client_secret}
&redirect_uri={your_redirect_uri}
&code_verifier={pkce_verifier}
Response
{
"access_token": "eyJhbG...",
"token_type": "Bearer",
"expires_in": 900,
"id_token": "eyJhbG...",
"refresh_token": "dGhpcyBp...",
"scope": "openid profile email org"
}
2 Client Credentials
For service-to-service communication. No user interaction - your backend authenticates directly.
When to use
- Backend services calling other services
- Cron jobs and background workers
- CI/CD pipelines
The token is scoped to the org that owns the OAuth client. It carries org_id and org_slug claims.
Request
POST /v1/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id={your_client_id}
&client_secret={your_client_secret}
Response
{
"access_token": "eyJhbG...",
"token_type": "Bearer",
"expires_in": 3600
}
3 API Keys
For scripts, CLI tools, and simple integrations. Create keys in the ALKEYON dashboard, validate them server-side.
Key management
/v1/auth/api-keys
Create a new API key (returns raw key once)
/v1/auth/api-keys
List your API keys (prefix only, not the raw key)
/v1/auth/api-keys/{id}
Revoke an API key
Important: The raw API key is only returned at creation time. Store it securely - it cannot be retrieved later.
Validate a key (server-side)
POST /v1/auth/validate-key
Content-Type: application/json
{
"apiKey": "ak_live_abc123...",
"scope": "read:data"
}
Response
{
"userId": "550e8400-...",
"email": "[email protected]",
"displayName": "Jane Smith",
"orgId": "org-id-...",
"orgSlug": "acme",
"scopes": "read:data,write:data"
}
4 Self-Registration
Let users create their own accounts. Registration is controlled per OAuth application - enable it for public-facing products, disable it for internal tools.
How it works
-
1.
Enable
AllowSelfRegistrationon your OAuth application in the ALKEYON admin panel. - 2. Users visiting the login page see a "Create account" link. The registration form collects display name, email, and password.
-
3.
On successful registration, the user is automatically assigned the
memberrole in the org and signed in with JWT cookies. -
4.
The user is redirected back to the original
returnUrl(your product), or to the ALKEYON dashboard if no return URL was provided.
Registration URL
https://alkeyon.fastflow.tech/Account/Register
?returnUrl=/Connect/Authorize
%3Fclient_id%3D{your_client_id}
%26redirect_uri%3D{your_redirect_uri}
%26response_type%3Dcode
%26scope%3Dopenid+profile+email+org
&org={org_slug}
The returnUrl must contain a valid client_id so ALKEYON can verify that the app allows self-registration.
Security
- Rate limited: 5 attempts per IP per 15 minutes
- Password minimum: 8 characters
- Duplicate emails rejected with login suggestion
- Redirect URLs validated against allowlist
Linking from your product
If you use the SDK, the login page already includes a "Create account" link when registration is enabled. For custom integrations, link directly to the registration page:
.NET (Razor)
<a href="https://@(host)/Account/Register
?returnUrl=@(Uri.EscapeDataString(
$"/Connect/Authorize?client_id={clientId}"
+ $"&redirect_uri={redirectUri}"
+ "&response_type=code"
+ "&scope=openid profile email org"))
&org=your-org">
Create account
</a>
JavaScript
const registerUrl = new URL(
'/Account/Register',
'https://alkeyon.fastflow.tech'
);
const authUrl = new URLSearchParams({
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
response_type: 'code',
scope: 'openid profile email org'
});
registerUrl.searchParams.set(
'returnUrl',
`/Connect/Authorize?${authUrl}`
);
registerUrl.searchParams.set(
'org', 'your-org'
);
5 MFA (Two-Factor Authentication)
Users can enable TOTP-based two-factor authentication on their accounts. When enabled, login requires both password and a 6-digit code from an authenticator app.
Setup flow
-
1.
User navigates to
/Account/MfaSetupfrom their dashboard (requires authentication). - 2. ALKEYON generates a TOTP secret and displays a QR code (or manual key) for the user to scan with their authenticator app (Google Authenticator, Authy, 1Password, etc.).
- 3. User enters the 6-digit verification code to confirm setup. ALKEYON validates the code before enabling MFA.
- 4. On success, ALKEYON displays one-time recovery codes. These are shown only once and must be saved securely.
Login with MFA
-
1.
User submits email + password at
/Account/Login. -
2.
If MFA is enabled, ALKEYON issues a temporary
mfa_pendingcookie (encrypted via Data Protection) and redirects to/Account/MfaVerify. -
3.
User enters the 6-digit TOTP code (or a recovery code). On success, ALKEYON issues the JWT with
amr: ["pwd", "mfa"]auth methods.
API endpoints
/v1/auth/mfa/verify
Verify TOTP code during login
For integrators: If your product uses the SDK, MFA is handled transparently - the login redirect goes through ALKEYON's MFA flow before returning the JWT. No additional code needed in your product.
Disabling MFA
Users can disable MFA at /Account/MfaDisable. This requires entering a valid TOTP code to confirm identity before removal.
- Requires authentication (logged-in user only)
- Requires valid TOTP code to confirm
- Recovery codes are invalidated on disable
Pages
/Account/MfaSetup
Enable MFA (QR code + verification)
/Account/MfaVerify
TOTP challenge during login
/Account/MfaDisable
Remove MFA from account
7 Managing Your Organization
Your organization is created when you subscribe to a product. As an org admin, you manage your team, configure login options, and control how members access your workspace.
Invite and manage your team
Org admins can invite members, assign roles, and remove users - no need to contact support.
- 1. Go to Organization Settings from the admin panel and select Members.
- 2. Enter the person's email, choose a role, and send the invite.
- 3. If they're new to ALKEYON, they'll receive a welcome email with a link to set their password. If they already have an account, they're added immediately.
Self-registration: If enabled for your product, users can also create their own accounts from the login page and are automatically added as member.
Roles
Roles are per-org. A user can be org_admin in one workspace and member in another. The JWT's role claim reflects the active org's role.
Configure social login
Let your team sign in with Google or GitHub instead of a password. Each org brings their own OAuth credentials.
- Create OAuth credentials in Google Cloud Console or GitHub Settings
- Go to Social Providers in your org settings
- Enter the Client ID and Secret, then enable
See the Social Providers guide for full setup details and callback URLs.
Workspace switching
Users who belong to multiple organizations can switch between them without logging out.
- 1. At login, multi-org users see a workspace picker to choose which org to sign into.
- 2. Once signed in, the header dropdown allows switching without re-entering credentials.
- 3. Each switch issues a fresh JWT scoped to the selected org.
Single-org users are auto-scoped at login - no picker shown.
API access for member management
If you need to manage members programmatically (e.g. sync from an HR system), use the REST API. Requires org_admin role.
/v1/orgs/{slug}/members
Invite by email + role
/v1/orgs/{slug}/members
List members with roles
/v1/orgs/{slug}/members/{id}
Remove member
Using your token
Bearer header
GET /v1/auth/me
Authorization: Bearer eyJhbG...
Standard for API calls. Pass the access token in the Authorization header.
Validate via JWKS
JWKS URL:
https://alkeyon.fastflow.tech/.well-known/jwks.json
Algorithm: RS256
Issuer: https://alkeyon.fastflow.tech
Fetch public keys from the JWKS endpoint. All tokens are RSA-256 signed.
Refresh
POST /v1/auth/refresh
{
"refreshToken": "dGhpcyBp..."
}
Access tokens expire in 15 minutes. Use the refresh token to get a new one.
Reference
Token anatomy
Every JWT carries user identity and org context. Scoped tokens include the active org; unscoped tokens carry all memberships.
Always present
sub
User ID (GUID)
email
User email address
name
Display name
org_role
Array of "slug:role" for all memberships
org_name
Array of "slug:DisplayName" for all memberships
Present when org-scoped
org_id
Scoped organization ID (GUID)
org_slug
Scoped organization slug
role
Scoped role (e.g. platform_admin, org_admin, member)
Org scoping happens at login via the org query parameter: org=pick shows the picker, org=slug scopes directly, omitted auto-scopes if single org.
Ready to integrate?
Two lines for .NET, Go, or Python. Standard OIDC for everything else.