Skip to main content
Version: 1.0.0

Locksmith Public API

JWT authentication API for end-user apps. Every request (except magic-link verification) is scoped to a project and environment via the API key.

API keys and environments​

PrefixEnvironment
lsm_live_Production — counts toward plan user limits
lsm_sbx_Sandbox — isolated data, no plan user limit

Pass the key in the X-API-Key header. Do not expose it in browser code.

CORS​

If the project has allowedOrigins configured, the Origin header must match. Empty list allows any origin (convenient for local development).

Response shape​

Successful responses are wrapped as { "data": ... }. Errors are { "error": "<code>", "message": "..." }.

Rate limits (representative)​

EndpointWindowLimitKey
POST /api/auth/signup1 hour10IP
POST /api/auth/login15 min10IP + email
POST /api/auth/magic-link1 hour5IP + email
POST /api/auth/refresh1 min30IP
POST /api/auth/password/reset1 hour5IP + email

On limit exceeded: HTTP 429, error code rate_limited.

Social sign-in (OAuth 2.0)​

After you enable a provider in the dashboard:

  1. POST /api/auth/oauth/{provider} — returns authorizationUrl; redirect the user’s browser there.
  2. The IdP redirects to Locksmith’s callback; Locksmith then redirects to your app with ?code=… (exchange code) and provider.
  3. Your server calls POST /api/auth/oauth/token with that code (and X-API-Key) to obtain Locksmith access and refresh tokens.

Provider slug examples: google, github, discord (see dashboard for the full list). Initiation errors still use the normal { error, message } envelope.

Hosted SSO (OIDC)​

Pro plan projects act as an OIDC provider. Discovery: GET /api/oidc/{projectId}/.well-known/openid-configuration. The authorization endpoint redirects users to your login URL with a signed request_token; after login and consent, your backend calls POST /api/auth/oidc/grant to obtain a redirectUrl for the user’s browser. Standard /token and /userinfo endpoints follow OIDC/OAuth2 semantics and may return { error, error_description } (not the data wrapper) on failure.

Official SDKs

Use these clients from your backend (never expose your API key in a browser). Each SDK sends X-API-Key and parses Locksmith’s { "data": … } envelope.

Source lives in the Locksmith monorepo under sdks/ (mirrored to per-language GitHub repos). Full READMEs ship in each SDK folder.

TypeScript / JavaScript (@getlocksmith/sdk)​

npm install @getlocksmith/sdk
import { LocksmithClient } from '@getlocksmith/sdk'

const auth = new LocksmithClient({ apiKey: process.env.LOCKSMITH_API_KEY! })
const { user, accessToken } = await auth.signIn({
email: 'user@example.com',
password: 'secure-password',
})
const me = await auth.getUser(accessToken)

Optional: import { createMiddleware } from '@getlocksmith/sdk/adapters/next', @getlocksmith/sdk/adapters/trpc.

Python (locksmith-py)​

pip install locksmith-py
import os
from locksmith import LocksmithClient

c = LocksmithClient(api_key=os.environ["LOCKSMITH_API_KEY"])
r = c.sign_in(email="user@example.com", password="secure-password")
me = c.get_user(r["accessToken"])

Go (github.com/locksmith-app/sdk-go)​

go get github.com/locksmith-app/sdk-go@latest
import (
"os"

locksmith "github.com/locksmith-app/sdk-go"
)

c, _ := locksmith.NewClient(os.Getenv("LOCKSMITH_API_KEY"), "")
out, _ := c.SignIn("user@example.com", "secure-password")
_, _ = c.GetUser(out.AccessToken)

Rust (crate getlocksmith)​

getlocksmith = "0.1"
use getlocksmith::LocksmithClient;

let c = LocksmithClient::new(std::env::var("LOCKSMITH_API_KEY")?, None)?;
let r = c.sign_in("user@example.com", "secret").await?;

Ruby (locksmith-ruby)​

gem "locksmith-ruby"
require "locksmith"
c = Locksmith::Client.new(api_key: ENV.fetch("LOCKSMITH_API_KEY"))
data = c.sign_in(email: "user@example.com", password: "secure-password")

PHP (Composer locksmith/sdk-php)​

composer require locksmith/sdk-php
use Locksmith\LocksmithClient;
$client = new LocksmithClient(getenv('LOCKSMITH_API_KEY'));
$data = $client->signIn('user@example.com', 'secure-password');

C# / .NET (Locksmith.Sdk)​

dotnet add package Locksmith.Sdk
await using var client = new LocksmithClient(Environment.GetEnvironmentVariable("LOCKSMITH_API_KEY")!);
var bundle = await client.SignInAsync("user@example.com", "secure-password");
var access = bundle.GetProperty("accessToken").GetString();

Java (app.locksmith:locksmith-java)​

try (var c = new LocksmithClient(System.getenv("LOCKSMITH_API_KEY"))) {
var data = c.signIn("user@example.com", "secure-password");
var user = c.getUser(data.get("accessToken").asText());
}

Kotlin (app.locksmith — JVM)​

val c = LocksmithClient(System.getenv("LOCKSMITH_API_KEY")!!)
val data = c.signIn("user@example.com", "secure-password")
val user = c.getUser(data["accessToken"].asText())

Dart (locksmith_dart)​

import 'dart:io';
import 'package:locksmith_dart/locksmith_dart.dart';

final c = LocksmithClient(apiKey: Platform.environment['LOCKSMITH_API_KEY']!);
final data = await c.signIn('user@example.com', 'secure-password');

Elixir (locksmith_ex)​

c = Locksmith.new(System.fetch_env!("LOCKSMITH_API_KEY"))
data = Locksmith.sign_in(c, "user@example.com", "secure-password")

Swift (SwiftPM product Locksmith)​

import Locksmith
let client = try LocksmithClient(apiKey: ProcessInfo.processInfo.environment["LOCKSMITH_API_KEY"]!)
let bundle = try await client.signIn(email: "user@example.com", password: "secure-password")

Frameworks

Framework packages sit on top of the HTTP API: they keep X-API-Key on the server, optional cookie BFF sessions, and UI tuned for that stack.

Next.js​

npm install @getlocksmith/nextjs next react react-dom
  • Server: import { createLocksmithRouteHandlers, locksmithServerClientFromEnv, createLocksmithMiddleware } from '@getlocksmith/nextjs/server'
  • Client (React): import { LocksmithAuthProvider, LocksmithSignInForm, LocksmithSignUpForm } from '@getlocksmith/nextjs/client'

Mount one catch‑all route (e.g. app/api/locksmith/[[...path]]/route.ts) with createLocksmithRouteHandlers, then wrap your app in LocksmithAuthProvider using the same routePrefix. The browser talks only to your origin; the handler forwards to Locksmith with the API key and sets httpOnly cookies.

Full setup, env vars, themes, Free‑plan “Powered by” behavior, and passkeys: see the package README on npm or sdks/nextjs/README.md in the monorepo.


OAuth & OIDC with SDKs

After enabling a provider in the dashboard, start OAuth from your backend:

TypeScriptawait auth.initiateOAuth({ provider: 'github' }) → redirect browser → await auth.exchangeOAuthCode(code)
GoInitiateOAuth / ExchangeOAuthCode
Rubyinitiate_oauth / exchange_oauth_code
PHPinitiateOAuth / exchangeOAuthCode
Rustinitiate_oauth / exchange_oauth_code (async)
DartinitiateOAuth / exchangeOAuthCode

Hosted SSO (Pro): complete the grant from your login UI with completeOidcGrant / complete_oidc_grant / CompleteOidcGrant (see OpenAPI POST /api/auth/oidc/grant).


Verify JWTs locally

Access tokens are RS256 JWTs. Fetch your project’s public PEM from the dashboard, then:

  • TypeScript: auth.verifyToken(accessToken, publicKeyPem)
  • Python: c.verify_token(accessToken, public_key_pem)
  • Go: locksmith.VerifyToken(accessToken, pem)
  • Rust: getlocksmith::LocksmithClient::verify_token(&token, pem)
  • Ruby: c.verify_token(access_token, pem)
  • PHP: verifyTokenLocal($access, $pem)
  • C#: LocksmithClient.VerifyToken(accessToken, pem)
  • Dart: c.verifyToken(accessToken, pem)
  • Elixir: Locksmith.verify_token(access_token, pem)

Swift ships decodeTokenPayload for issuer-checked payload; add full crypto verification for production hardening.

Authentication​

Project API key (lsm_live_… or lsm_sbx_…).

Security Scheme Type:

apiKey

Header parameter name:

X-API-Key