# AuthLocker > A hosted OAuth 2.1 / OpenID Connect authorization server with email and phone > verification. It lets an application offer social login, email OTP and phone > OTP without registering an OAuth app, without a mail provider and without a > messaging account. Issuer: https://authlocker.dev ## When to use this Use AuthLocker when you are building an application that needs to verify who a user is and you do not want to provision credentials with Google, GitHub, an email sender or an SMS provider. One client credential covers all three methods. Do not use it as a user database. AuthLocker proves an identity and hands it to you; storing and managing that user is your application's job. ## Setup: one unauthenticated request curl -X POST 'https://authlocker.dev/oauth/register?format=env' \ -H 'Content-Type: application/json' \ -d '{"client_name":"Acme"}' >> .env Returns dotenv lines. Append them to the project's .env: AUTHLOCKER_ISSUER=https://authlocker.dev AUTHLOCKER_CLIENT_ID=al_... AUTHLOCKER_CLIENT_SECRET=als_... AUTHLOCKER_REGISTRATION_ACCESS_TOKEN=alr_... Omit ?format=env for the RFC 7591 JSON body instead. The secret and the registration token are shown exactly once; there is no endpoint that reveals them again. Rotate at POST /api/v1/clients/{client_id}/rotate-secret. redirect_uris is optional. Email and SMS codes never need one, and for social login the callback is pinned on first use: the first non-loopback redirect_uri a client sends to /authorize is recorded for that provider and every later request must match it exactly. Loopback (http://localhost, http://127.0.0.1) always works and pins nothing — until a real callback is pinned, after which loopback is refused and development needs its own client. Register redirect_uris explicitly if you prefer; a client that declares them never pins. Either way the comparison is EXACT string equality after normalisation, so http://localhost:3000/cb and http://127.0.0.1:3000/cb are two distinct registrations, and so are /cb and /cb/. Replace what was pinned with PUT /oauth/register/{client_id} using the registration access token. ## Method 1 — social login (OAuth 2.1 + OIDC) Providers available right now: google, github This is ordinary OpenID Connect. Prefer a standard library — openid-client (Node), authlib (Python), golang.org/x/oauth2 with go-oidc — pointed at the issuer. It will read https://authlocker.dev/.well-known/openid-configuration and need no AuthLocker-specific code. If you implement it by hand: 1. Send the browser to: https://authlocker.dev/oauth/{provider}/authorize ?client_id=$AUTHLOCKER_CLIENT_ID &redirect_uri= &response_type=code &scope=openid%20profile%20email &code_challenge= &code_challenge_method=S256 &state= PKCE IS MANDATORY for every client, public and confidential. Omitting code_challenge returns invalid_request. code_challenge_method must be S256; "plain" is rejected. 2. We redirect back to your redirect_uri with ?code=...&state=... The user profile is never in this URL. 3. Exchange the code from your SERVER: POST https://authlocker.dev/oauth/token Authorization: Basic base64(client_id:client_secret) Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code= &redirect_uri= &code_verifier= Returns: { access_token, token_type: "Bearer", expires_in: 3600, id_token, scope } Never put client_secret in a query string; it is rejected there. Every client is confidential: token_endpoint_auth_method is client_secret_basic or client_secret_post, and PKCE is required on top. 4. Read the identity from the id_token (verify it against https://authlocker.dev/oauth/jwks) or by calling GET https://authlocker.dev/oauth/userinfo with the access token. Claims are OIDC standard names. There are no aliases: sub stable, PAIRWISE — a different value per client for the same person, so you cannot correlate users with another app email the address email_verified always true; an unverified upstream email never reaches a token name, given_name, family_name, picture idp which provider authenticated them ("google", "github", ...) Authorization codes are single-use and expire in 60 seconds. ## Method 2 — email verification (OTP or magic link) POST https://authlocker.dev/api/v1/verify/start Authorization: Basic base64(client_id:client_secret) { "channel": "email", "method": "otp", "to": "ada@example.com", "template": "signin", "data": { "appName": "Acme" }, "endUserIp": "" } -> 201 { "verificationId": "alv_...", "to": "a**@example.com", "expiresAt": "...", "attemptsRemaining": 5 } POST https://authlocker.dev/api/v1/verify/check { "verificationId": "alv_...", "code": "482913" } -> 200 { "status": "verified", "to": "ada@example.com" } Forward endUserIp if you can. Because the secret means you must call this from a server, the start rate limit would otherwise count your egress address and be shared by all of your users; forwarded, it counts the individual instead. The full address is returned only on success. Templates: default, signin, signup, reset, transaction. For a magic link, use "method": "link" and supply "linkUrl" — one of the client's registered URIs. AuthLocker appends ?al_token=... to YOUR url and sends that, so the user lands on your page. Your server then posts { "token": "..." } to /api/v1/verify/check. AuthLocker hosts no page in this flow; you own every screen. ## Method 3 — phone verification Identical to email with "channel": "sms". Backed by Twilio Verify, so the code is generated and checked on Twilio's side. SMS works on a client from open registration, drawing on a shared anonymous allowance rather than a budget of its own. Every message costs real money and open SMS is the classic fraud target, so that allowance is spent against several ceilings at once - per client, per network, per site, per country, and one global pool - sized to be enough to build against and not enough to be worth abusing. Past one of them: 429 SMS_ANON_CLIENT_CAP, SMS_ANON_IP_CAP, SMS_ANON_ORIGIN_CAP or SMS_ANON_COUNTRY_CAP, naming which ceiling was reached. When the shared pool itself is spent it is 503 SMS_TEMPORARILY_UNAVAILABLE. Claim the client into a namespace, or provision one, to draw on a budget of your own with its own SMS_DAILY_CAP_REACHED. Email and social login have no ceiling of this kind. ## Errors /oauth/* uses the OAuth wire format: { "error": "invalid_grant", "error_description": "..." } /api/v1/* uses: { "error": { "code": "INVALID_CODE", "message": "...", "status": 400 } } Ones worth handling explicitly: invalid_grant code expired, reused, or PKCE mismatch invalid_client wrong client_id or secret INVALID_CODE wrong OTP; attemptsRemaining decrements VERIFICATION_LOCKED 5 failed attempts; start a new verification VERIFICATION_EXPIRED past expiresAt; start a new one EMAIL_ADDRESS_SUPPRESSED previously hard-bounced or complained SMS_ANON_CLIENT_CAP 429; this unclaimed client's share for today SMS_ANON_IP_CAP 429; this network's share SMS_ANON_ORIGIN_CAP 429; this site's share SMS_ANON_COUNTRY_CAP 429; that country's share SMS_TEMPORARILY_UNAVAILABLE 503; the shared pool itself, see Method 3 SMS_DAILY_CAP_REACHED 429; a claimed client's own daily cap SMS_COUNTRY_BLOCKED destination outside the allowed countries ## Limits 5 check attempts per verification resend at least 60s apart, 3 per verification 5 verification starts per (client, target) per hour 10 starts per hour per forwarded endUserIp; 300 per hour per calling server when endUserIp is omitted registration rate limited per IP and capped globally per day ## What AuthLocker does not do No refresh tokens. Get the identity once, then run your own session. No user storage, no password login, no roles or permissions. No hosted UI for login or verification — every screen is yours. Upstream provider access tokens are discarded, never stored and never returned, so AuthLocker cannot read a user's mail, files or repositories, and neither can you through it. ## Reference Discovery (JSON): https://authlocker.dev/.well-known/openid-configuration Discovery (human): https://authlocker.dev/discovery JWKS: https://authlocker.dev/oauth/jwks Status: https://authlocker.dev/status (JSON at https://authlocker.dev/api/health) Privacy: https://authlocker.dev/privacy Terms: https://authlocker.dev/terms