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.

Program.cs - with Alkeyon.Sdk
// Install: dotnet add package Alkeyon.Sdk

builder.AddFFAuth();

app.MapFFAuthEndpoints();

// That's it. Login, callback, logout, org switching - all handled.
appsettings.json
{
  "FFAuth": {
    "AuthDomain": "alkeyon.fastflow.tech",
    "DefaultOrg": "your-org",
    "ClientId": "your-app-client-id",
    "ClientSecret": "...",
    "Audience": "your-audience"
  }
}
main.go - with ffauth
// 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.
Or skip the SDK - raw JWT
// 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
  • onAuthenticated callback 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
Quick Start
# 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)
Error Handling
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
Dockerfile
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

GET
/.well-known/openid-configuration

OIDC discovery document

GET
/.well-known/jwks.json

RSA public keys for JWT validation

POST
/v1/connect/token

Token exchange (authorization_code, client_credentials)

GET
/v1/connect/userinfo

User info from access token

Authentication

POST
/v1/auth/login

Email + password login, returns JWT

POST
/v1/auth/mfa/verify

MFA TOTP verification

POST
/v1/auth/refresh

Refresh token exchange

POST
/v1/auth/token

Client credentials token (machine-to-machine)

GET
/v1/auth/me

Current user profile

POST
/v1/auth/introspect

Token introspection (RFC 7662)

Organizations

POST
/v1/orgs

Create organization

GET
/v1/orgs

List user's organizations

GET
/v1/orgs/{slug}

Get organization details

GET
/v1/orgs/{slug}/members

List org members

GET
/v1/orgs/{slug}/apps

List OAuth clients

External Auth

GET
/auth/external/{provider}

Initiate external login (Google, Microsoft, etc.)

GET
/auth/callback/{provider}

External auth callback

POST
/v1/auth/validate-key

Validate API key and return user context

POST
/v1/auth/switch-org

Switch org context, re-issues JWT

Self-Registration

GET
/Account/Register

Registration form (requires returnUrl with a valid client_id)

POST
/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. 1. Create an OAuth client in the ALKEYON admin panel. Note the client_id and client_secret.
  2. 2. Redirect the user to /Connect/Authorize with PKCE challenge, client ID, redirect URI, and scopes.
  3. 3. User authenticates on ALKEYON and is redirected back with an authorization code.
  4. 4. Exchange the code at POST /v1/connect/token for access + refresh tokens.
  5. 5. Use the access token as Bearer header 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

POST
/v1/auth/api-keys

Create a new API key (returns raw key once)

GET
/v1/auth/api-keys

List your API keys (prefix only, not the raw key)

DELETE
/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. 1. Enable AllowSelfRegistration on your OAuth application in the ALKEYON admin panel.
  2. 2. Users visiting the login page see a "Create account" link. The registration form collects display name, email, and password.
  3. 3. On successful registration, the user is automatically assigned the member role in the org and signed in with JWT cookies.
  4. 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. 1. User navigates to /Account/MfaSetup from their dashboard (requires authentication).
  2. 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. 3. User enters the 6-digit verification code to confirm setup. ALKEYON validates the code before enabling MFA.
  4. 4. On success, ALKEYON displays one-time recovery codes. These are shown only once and must be saved securely.

Login with MFA

  1. 1. User submits email + password at /Account/Login.
  2. 2. If MFA is enabled, ALKEYON issues a temporary mfa_pending cookie (encrypted via Data Protection) and redirects to /Account/MfaVerify.
  3. 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

POST
/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

6 Social Login Providers

Enable Google and GitHub login for your organization. Social login is configured per-org by org admins - each org brings their own OAuth credentials.

How it works

  1. 1. Org admin creates OAuth credentials in the provider's console (Google Cloud Console or GitHub OAuth Apps).
  2. 2. Admin enters the Client ID and Client Secret at /OrgSettings/SocialProviders and enables the provider.
  3. 3. The login page for that org shows "Sign in with Google" / "Sign in with GitHub" buttons.
  4. 4. On first social login, ALKEYON creates a user account and assigns member role. Subsequent logins match on the provider's user ID.

Supported providers

Google

OAuth 2.0 via Google Cloud Console. Callback URL: https://{org}.alkeyon.fastflow.tech/auth/callback/google

GitHub

OAuth Apps via GitHub Settings. Callback URL: https://{org}.alkeyon.fastflow.tech/auth/callback/github

Auth endpoints

GET
/auth/external/{provider}

Initiates OAuth flow (redirects to provider)

GET
/auth/callback/{provider}

Handles OAuth callback, creates/links account

Account linking

Existing users can link social accounts from their dashboard. This allows them to log in with either method.

  • Pass ?link=true to /auth/external/{provider}
  • User must be authenticated (has valid JWT cookie)
  • Redirects to dashboard with ?linked=true on success

Security details

  • State parameter encrypted via ASP.NET Data Protection
  • State cookies expire after 10 minutes
  • Per-org provider credentials (not shared across orgs)
  • JWT carries amr: ["social:google"] to indicate auth method

For integrators: Social login is transparent to your product. The JWT issued after social login has the same claims as password login - your app doesn't need any special handling.

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. 1. Go to Organization Settings from the admin panel and select Members.
  2. 2. Enter the person's email, choose a role, and send the invite.
  3. 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

member Use products, manage their own API keys and profile, switch workspaces.
org_admin Everything a member can do, plus: invite and remove members, configure social login, manage email settings.

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.

  1. Create OAuth credentials in Google Cloud Console or GitHub Settings
  2. Go to Social Providers in your org settings
  3. 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. 1. At login, multi-org users see a workspace picker to choose which org to sign into.
  2. 2. Once signed in, the header dropdown allows switching without re-entering credentials.
  3. 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.

POST /v1/orgs/{slug}/members

Invite by email + role

GET /v1/orgs/{slug}/members

List members with roles

DELETE /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.