Drop-in authentication and identity for web, mobile, and backend apps.
Framework-native SDKs for sessions, user management, and auth UI. One session protocol across the stack.
Quick Start Β· Packages Β· Examples Β· Development Β· Contributing
- π§© Framework-native β an idiomatic package per framework. No glue code. See Packages.
- π One session across the stack β web, Node, Python, Go, Rust, JVM, PHP, Ruby, C#, and mobile clients share one
authdog-sessioncookie, one OIDCuserinfoflow, and one identity-host allowlist. - π Secure by default β token validation, cookie handling, and session lifecycle managed for you.
- π¨ Batteries-included UI β ready-made, accessible components (sign-in, user profile, TOTP, navbar) you can drop in or restyle.
- β‘ Tiny & tree-shakeable β ESM-first,
sideEffects: false, dual CJS/ESM builds via tsup. - π¦ Type-safe end to end β written in TypeScript with first-class types shipped in every package.
- π Server & client split β explicit
/clientand/serverentry points keep secrets server-side.
| Package | Version | Description | CI |
|---|---|---|---|
@authdog/react-elements |
React UI components (Account, UserButton, β¦) | ||
@authdog/react |
Framework-agnostic React SDK (provider, hooks, AccountButton) | β | |
@authdog/nextjs-app |
Next.js App Router SDK | ||
@authdog/remix-node |
Remix SDK | ||
@authdog/tanstack-start |
TanStack Start SDK | ||
@authdog/vue |
Vue SDK | ||
@authdog/astro |
Astro SDK | ||
@authdog/sveltekit |
SvelteKit SDK | ||
@authdog/gatsby |
Gatsby SDK | ||
@authdog/redwood |
RedwoodJS SDK | ||
@authdog/angular |
Angular SDK | ||
@authdog/express |
Express SDK | ||
@authdog/fastify |
Fastify SDK | ||
@authdog/hono |
Hono SDK | ||
@authdog/koa |
Koa SDK | ||
@authdog/elysia |
Elysia SDK | ||
@authdog/react-router |
React Router 7 SDK | ||
@authdog/nuxt |
Nuxt SDK | ||
@authdog/solid |
SolidJS SDK | ||
@authdog/javascript |
Framework-free browser client | ||
@authdog/tauri |
Tauri desktop SDK | ||
@authdog/electron |
Electron desktop SDK | ||
@authdog/react-native |
React Native / Expo SDK | ||
@authdog/chrome-extension |
Chrome Extension SDK | ||
@authdog/node-commons |
Shared Node utilities |
| Package | Registry | Description | CI |
|---|---|---|---|
authdog-fastapi |
PyPI | Python SDK β FastAPI, Flask, Django, Starlette, aiohttp | |
authdog (Go) |
github.com/authdog/web-sdk/packages/go |
Go SDK with Gin middleware | |
authdog-core |
crates.io | Framework-agnostic Rust core | |
authdog-axum |
crates.io | Rust SDK for Axum | |
authdog-actix |
crates.io | Rust SDK for Actix Web | |
authdog-rocket |
crates.io | Rust SDK for Rocket | |
authdog-warp |
crates.io | Rust SDK for Warp | |
authdog-poem |
crates.io | Rust SDK for Poem | |
authdog-ktor |
Maven Central | Kotlin / Ktor SDK | |
authdog-java |
Maven (com.authdog:authdog-java) |
Java SDK β Jakarta Servlet | |
authdog/authdog |
Packagist | PHP SDK β PSR-15 and Laravel | |
authdog |
RubyGems | Ruby SDK β Rack, Rails, and Sinatra | |
Authdog |
NuGet | C# SDK β ASP.NET Core | |
authdog-android |
Maven (com.authdog:authdog-android) |
Android client | |
Authdog |
Swift Package | iOS client | |
authdog |
pub.dev | Flutter / Dart client |
These SDKs validate the same Authdog sessions as the Node packages.
| Package | Description |
|---|---|
@authdog/eslint-config |
Shared ESLint configurations |
@authdog/typescript-config |
Shared TypeScript configurations |
Pick the package for your framework and install it with your favorite package manager:
# Next.js (App Router)
bun add @authdog/nextjs-app @authdog/react-elements
# Remix
bun add @authdog/remix-node @authdog/react-elements
# TanStack Start
bun add @authdog/tanstack-start @authdog/react-elements
# Vue
bun add @authdog/vue
# Astro
bun add @authdog/astro
# SvelteKit
bun add @authdog/sveltekit
# Gatsby
bun add @authdog/gatsby @authdog/react-elements
# RedwoodJS
bun add @authdog/redwood @authdog/react-elements
# Angular
bun add @authdog/angular
# Express (backend)
bun add @authdog/express
# Fastify (backend)
bun add @authdog/fastify
# React Native / Expo
bun add @authdog/react-native
# Chrome Extension
bun add @authdog/chrome-extension
# Electron
bun add @authdog/electronBackend SDKs for other languages install with their native package managers:
# Python β one install, pick your framework's extra
pip install "authdog-fastapi[fastapi]" # or [flask], [django], [starlette], [aiohttp]
# Go (Gin)
go get github.com/authdog/web-sdk/packages/go@latest
# Rust β add the crate for your framework to Cargo.toml
cargo add authdog-axum # or authdog-actix / authdog-rocket / authdog-warp / authdog-poem
# Kotlin (Ktor) β add to build.gradle.kts
# implementation("com.authdog:authdog-ktor:0.1.0")
# Java (Jakarta Servlet)
# implementation("com.authdog:authdog-java:0.1.0")
# PHP
composer require authdog/authdog
# Ruby (Rails / Sinatra)
gem install authdog
# C# (ASP.NET Core)
dotnet add package AuthdogProvide your Authdog public key (pk_β¦). Each framework reads it from a different
place β use the variable that matches your package:
| Framework | Variable | Where it's read |
|---|---|---|
| Next.js (client) | NEXT_PUBLIC_PK_AUTHDOG |
Exposed to the browser by Next.js |
| Next.js (server) | PK_AUTHDOG |
Server-only (logout, etc.) |
| Remix | PK_AUTHDOG |
Loaders / actions |
| TanStack Start | PK_AUTHDOG |
Server functions / route loaders |
| Express (backend) | PK_AUTHDOG |
createAuthdog({ publicKey }) |
| Fastify (backend) | AUTHDOG_PK |
authdogPlugin({ publicKey }) |
| Vue | VITE_AUTHDOG_PUBLIC_KEY |
Vite-exposed (use the VITE_ prefix) |
| Astro | PUBLIC_AUTHDOG_PUBLIC_KEY |
Exposed via Astro's PUBLIC_ prefix |
| SvelteKit | PUBLIC_AUTHDOG_PUBLIC_KEY |
Exposed via SvelteKit's PUBLIC_ prefix |
| Gatsby (client) | GATSBY_AUTHDOG_PUBLIC_KEY |
Exposed via Gatsby's GATSBY_ prefix |
| Gatsby (functions) | PK_AUTHDOG |
Gatsby Functions (server-only) |
| RedwoodJS (web) | REDWOOD_ENV_AUTHDOG_PUBLIC_KEY |
Exposed via Redwood's REDWOOD_ENV_ prefix |
| RedwoodJS (api) | PK_AUTHDOG |
API functions / services (server-only) |
| React Native / Expo | EXPO_PUBLIC_PK_AUTHDOG |
Exposed to the app by Expo |
| Chrome Extension | PLASMO_PUBLIC_AUTHDOG_PUBLIC_KEY |
Exposed to the extension by Plasmo |
| Angular | β | Passed directly to provideAuthdog(...) |
| Python (all) | PK_AUTHDOG |
Authdog(public_key=...) |
| Go (Gin) | PK_AUTHDOG |
authdog.New(authdog.Config{PublicKey}) |
| Rust (all) | PK_AUTHDOG |
Authdog::new(...) |
| Kotlin (Ktor) | PK_AUTHDOG |
Authdog(System.getenv("PK_AUTHDOG")) |
| Java (Servlet) | PK_AUTHDOG |
new Authdog(System.getenv("PK_AUTHDOG")) |
| PHP | PK_AUTHDOG |
new Authdog\Authdog(getenv('PK_AUTHDOG')) |
| Ruby | PK_AUTHDOG |
Authdog::Client.new(ENV["PK_AUTHDOG"]) |
| C# | PK_AUTHDOG |
new AuthdogClient(Environment.GetEnvironmentVariable("PK_AUTHDOG")) |
| Electron | PK_AUTHDOG |
createAuthdogClient({ publicKey }) |
| Android | β | Passed to AuthdogClient(publicKey:) |
| iOS | β | Passed to AuthdogClient(publicKey:) |
| Flutter | β | Passed to AuthdogClient(publicKey:) |
# Next.js (App Router)
NEXT_PUBLIC_PK_AUTHDOG=pk_xxxxxxxxxxxxxxxxYour public key (
pk_β¦) is available in the Authdog dashboard. It is safe to expose to the browser. Your secret key (sk_β¦), used only by backend packages, must never be committed or shipped to the client.
Wrap your app with the provider in app/layout.tsx:
import "@authdog/react-elements/styles.css";
import { AuthdogProvider } from "@authdog/nextjs-app/client";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<AuthdogProvider>{children}</AuthdogProvider>
</body>
</html>
);
}Read the current user from any client component:
"use client";
import { useUser } from "@authdog/nextjs-app";
import { UserProfile } from "@authdog/react-elements";
export default function Profile() {
const { user, isLoading } = useUser();
return <UserProfile loading={isLoading} user={user} />;
}<script setup lang="ts">
import { AuthdogProvider } from "@authdog/vue/client";
import { useUser } from "@authdog/vue";
const { user, isLoading } = useUser();
</script>
<template>
<AuthdogProvider>
<p v-if="isLoading">Loadingβ¦</p>
<p v-else>Welcome, {{ user?.name }}</p>
</AuthdogProvider>
</template>Wire the middleware once (SSR mode), then read the session anywhere via
Astro.locals.authdog:
// src/middleware.ts
import { defineMiddleware } from "astro:middleware";
import { authdogMiddleware } from "@authdog/astro/server";
export const onRequest = defineMiddleware(
authdogMiddleware({ publicKey: import.meta.env.PUBLIC_AUTHDOG_PUBLIC_KEY }),
);---
// src/pages/profile.astro
import { createAuthdogServer } from "@authdog/astro/server";
const authdog = createAuthdogServer({ publicKey: import.meta.env.PUBLIC_AUTHDOG_PUBLIC_KEY });
const user = Astro.locals.authdog.isAuthenticated ? await authdog.getUser(Astro.request) : null;
---
{user ? <p>Welcome, {user.user?.displayName}</p> : <a href="/login">Sign in</a>}Wire the handle hook once, then read the session anywhere via
event.locals.authdog:
// src/hooks.server.ts
import { createAuthdogHandle } from "@authdog/sveltekit/server";
export const handle = createAuthdogHandle({
publicKey: import.meta.env.PUBLIC_AUTHDOG_PUBLIC_KEY,
});// src/routes/profile/+page.server.ts
import { createAuthdogServer } from "@authdog/sveltekit/server";
import type { PageServerLoad } from "./$types";
const authdog = createAuthdogServer({
publicKey: import.meta.env.PUBLIC_AUTHDOG_PUBLIC_KEY,
});
export const load: PageServerLoad = async ({ request, locals }) => {
const user = locals.authdog.isAuthenticated
? await authdog.getUser(request)
: null;
return { user: user?.user ?? null };
};Wrap the app once via wrapRootElement, then gate a Gatsby Function with
requireAuth:
// gatsby-browser.js
import { AuthdogProvider } from "@authdog/gatsby/client";
export const wrapRootElement = ({ element }) => (
<AuthdogProvider>{element}</AuthdogProvider>
);// src/api/me.ts
import { createAuthdog } from "@authdog/gatsby/server";
const authdog = createAuthdog({ publicKey: process.env.PK_AUTHDOG! });
export default authdog.requireAuth(async (req, res) => {
res.json(req.authdog.user);
});Wrap the web app in web/src/App.tsx, then gate an API function with
requireAuth:
// web/src/App.tsx
import { AuthdogProvider } from "@authdog/redwood/web";
const App = () => (
<AuthdogProvider>
<RedwoodProvider>
<Routes />
</RedwoodProvider>
</AuthdogProvider>
);// api/src/functions/me.ts
import { createAuthdog } from "@authdog/redwood/api";
const authdog = createAuthdog({ publicKey: process.env.PK_AUTHDOG! });
export const handler = authdog.requireAuth(async (event) => ({
statusCode: 200,
body: JSON.stringify(event.authdog.user),
}));// app/routes/_index.tsx
import { remixAuthLoader } from "@authdog/remix-node";
export const loader = remixAuthLoader;Resolve the session from a server function (TanStack Start speaks the Web Fetch
API, so the loader returns a standard Response):
// app/routes/index.tsx
import { createServerFn } from "@tanstack/react-start";
import { getWebRequest } from "@tanstack/react-start/server";
import { identityLoader } from "@authdog/tanstack-start";
export const loadIdentity = createServerFn({ method: "GET" }).handler(
async () => {
const response = await identityLoader()({ request: getWebRequest() });
return response.json();
},
);Wrap your app with the provider (strips the ?token=β¦ from the URL after the
server persists the session):
import { AuthdogProvider } from "@authdog/tanstack-start/client";
<AuthdogProvider>{children}</AuthdogProvider>;Register the SDK (standalone, app.config.ts) and add the interceptor to your HttpClient:
import { provideHttpClient, withInterceptors } from "@angular/common/http";
import { provideAuthdog, authdogInterceptor } from "@authdog/angular";
export const appConfig = {
providers: [
provideAuthdog({ publicKey: "pk_xxxxxxxxxxxxxxxx" }),
provideHttpClient(withInterceptors([authdogInterceptor])),
],
};Read auth state from the signals-based AuthdogService:
import { Component, inject } from "@angular/core";
import { AuthdogService } from "@authdog/angular";
@Component({/* β¦ */})
export class ProfileComponent {
readonly auth = inject(AuthdogService);
// auth.isLoading(), auth.token(), auth.user() β signals
// auth.signIn(), auth.signUp(), auth.signOut(), auth.fetchUser()
}import express from "express";
import { createAuthdog } from "@authdog/express";
const app = express();
const authdog = createAuthdog({ publicKey: process.env.PK_AUTHDOG! });
app.use(authdog.attachSession()); // resolves `req.authdog` for every request
app.get("/me", authdog.requireAuth, (req, res) => res.json(req.authdog!.user));
app.get("/logout", authdog.logout);import Fastify from "fastify";
import { authdogPlugin } from "@authdog/fastify";
const app = Fastify();
await app.register(authdogPlugin, { publicKey: process.env.AUTHDOG_PK! });
// `request.authdog` is now available everywhere; `app.authdog.requireAuth`
// is the server-side enforcement point.
app.get(
"/me",
{ preHandler: app.authdog.requireAuth },
async (req) => req.authdog!.user,
);
app.get("/logout", (req, reply) => app.authdog.logout(req, reply));import os
from fastapi import Depends, FastAPI, Request
from authdog.fastapi import Authdog
app = FastAPI()
authdog = Authdog(public_key=os.environ["PK_AUTHDOG"])
@app.get("/me")
async def me(user=Depends(authdog.require_auth)): # require_auth is the gate
return user
@app.get("/logout")
async def logout(request: Request):
return authdog.logout(request)The same Authdog surface ships for other Python frameworks β import the
matching module: authdog.flask (@authdog.require_auth decorator),
authdog.django (middleware + decorator), authdog.starlette (ASGI
middleware), or authdog.aiohttp (@web.middleware). All share one
framework-agnostic core.
ad, _ := authdog.New(authdog.Config{PublicKey: os.Getenv("PK_AUTHDOG")})
r := gin.Default()
r.Use(ad.AttachSession()) // resolves *authdog.Context per request
r.GET("/me", ad.RequireAuth(), func(c *gin.Context) {
c.JSON(http.StatusOK, authdog.FromGin(c).User)
})
r.GET("/logout", ad.Logout)let authdog = Authdog::new(&std::env::var("PK_AUTHDOG").unwrap()).unwrap();
let app = Router::new()
.route("/me", get(me).layer(middleware::from_fn(require_auth)))
.route("/logout", get(logout))
.layer(middleware::from_fn_with_state(authdog.clone(), attach_session))
.with_state(authdog);
async fn me(ctx: AuthContext) -> Json<Value> { Json(ctx.user.unwrap_or(Value::Null)) }The same surface ships as a dedicated crate per framework on top of the shared
authdog-core: authdog-axum, authdog-actix (extractors + middleware),
authdog-rocket (request guards + catcher), authdog-warp (composable
filters), and authdog-poem (extractors + middleware).
import com.authdog.Authdog
fun Application.module() {
val authdog = Authdog(System.getenv("PK_AUTHDOG"))
routing {
get("/me") {
val ctx = authdog.requireAuth(call) ?: return@get // requireAuth is the gate
call.respondText(ctx.user.toString())
}
get("/logout") { authdog.logout(call) }
}
}Wrap your app with the provider, backed by hardware-secure storage, and handle the login deep link:
import * as SecureStore from "expo-secure-store";
import {
AuthdogProvider,
createSecureStoreAdapter,
} from "@authdog/react-native";
export default function App() {
return (
<AuthdogProvider
publicKey={process.env.EXPO_PUBLIC_PK_AUTHDOG!}
storage={createSecureStoreAdapter(SecureStore)}
>
<RootNavigator />
</AuthdogProvider>
);
}import { useSignIn, useRedirectHandler } from "@authdog/react-native";
// signIn("myapp://callback") opens Universal Login; handleRedirect(url)
// validates the returned token and persists it via your secure storage.Use Chrome's Identity API for hosted authentication and persist the session in
chrome.storage.local:
import {
AuthdogProvider,
SignedIn,
SignedOut,
SignInButton,
} from "@authdog/chrome-extension";
<AuthdogProvider publicKey={process.env.PLASMO_PUBLIC_AUTHDOG_PUBLIC_KEY!}>
<SignedOut>
<SignInButton />
</SignedOut>
<SignedIn>Signed in</SignedIn>
</AuthdogProvider>;Each framework ships a runnable demo under examples/. Set PK_AUTHDOG (or the framework's public-key variable) and start one with moon:
| Example | Package showcased | Run |
|---|---|---|
examples/nextjs-app |
@authdog/nextjs-app + @authdog/react-elements |
moon run nextjs-app-sample:dev |
examples/remix |
@authdog/remix-node + @authdog/react-elements |
moon run remix-playground:dev |
examples/vue-app |
@authdog/vue (home, login, signup, profile, permissions) |
moon run vue-app:dev |
examples/astro |
@authdog/astro (SSR middleware, server getUser, logout) |
moon run astro-app:dev |
examples/sveltekit |
@authdog/sveltekit (SSR handle hook, server getUser, logout) |
moon run sveltekit-app:dev |
examples/gatsby |
@authdog/gatsby (client provider, Functions requireAuth, logout) |
moon run gatsby-app:dev |
examples/redwood |
@authdog/redwood (web provider, API requireAuth, logout) |
moon run redwood-app:type-check |
examples/angular |
@authdog/angular (interceptor, guard, signals) |
moon run angular-app:dev |
examples/express |
@authdog/express (attachSession, requireAuth, logout) |
moon run express-api:dev |
examples/fastify |
@authdog/fastify (plugin, requireAuth, logout) |
moon run fastify-api:dev |
examples/hono |
@authdog/hono (attachSession, requireAuth, logout) |
moon run hono-api:dev |
examples/koa |
@authdog/koa (attachSession, requireAuth, logout) |
moon run koa-api:dev |
examples/elysia |
@authdog/elysia (attachSession, requireAuth, logout) |
moon run elysia-api:dev |
examples/react-router |
@authdog/react-router (identity loader, protected page, logout) |
moon run react-router-app:dev |
examples/nuxt |
@authdog/nuxt (plugin, composables, server session) |
moon run nuxt-app:dev |
examples/solid |
@authdog/solid (provider, sign-in, profile, sign-out) |
moon run solid-app:dev |
examples/javascript |
@authdog/javascript (redirect callback, profile, sign-out) |
moon run javascript-app:dev |
examples/tauri |
@authdog/tauri (injectable opener, callback, sign-out) |
moon run tauri-app:dev |
examples/electron |
@authdog/electron (injectable opener, callback, sign-out) |
moon run electron-app:dev |
examples/react-native |
@authdog/react-native (Expo, deep-link sign-in, secure store) |
moon run react-native-app:dev |
This is a Bun + moon monorepo.
git clone https://github.com/authdog/web-sdk.git
cd web-sdk
bun installThe repo ships a Justfile for ergonomic shortcuts:
| Command | Description |
|---|---|
just build |
Build all publishable packages |
just dev-next |
Run the Next.js demo app |
just dev-remix |
Run the Remix demo app |
just ui |
Launch the Storybook component explorer |
just storybook |
Alias for just ui |
just ci |
Run the full CI pipeline locally |
Prefer raw scripts? bun run build, bun run dev, bun run test, bun run lint, and bun run check-types all work via moon.
web-sdk/
βββ examples/ # Runnable demo apps (Next.js, Remix, Vue, Nuxt, Solid, React Router, JavaScript, Tauri, Electron, Astro, SvelteKit, Gatsby, RedwoodJS, Angular, Express, Fastify, Hono, Koa, Elysia, React Native)
βββ packages/ # Published SDK packages + shared configs
β βββ react-elements/ # React UI components
β βββ nextjs-app/ # Next.js App Router SDK
β βββ remix/ # Remix SDK (@authdog/remix-node)
β βββ tanstack-start/ # TanStack Start SDK
β βββ vue/ # Vue SDK
β βββ astro/ # Astro SDK
β βββ sveltekit/ # SvelteKit SDK
β βββ gatsby/ # Gatsby SDK
β βββ redwood/ # RedwoodJS SDK
β βββ angular/ # Angular SDK
β βββ express/ # Express SDK
β βββ fastify/ # Fastify SDK
β βββ hono/ # Hono SDK
β βββ koa/ # Koa SDK
β βββ elysia/ # Elysia SDK
β βββ react-router/ # React Router 7 SDK
β βββ nuxt/ # Nuxt SDK
β βββ solid/ # SolidJS SDK
β βββ javascript/ # Framework-free browser client
β βββ tauri/ # Tauri desktop SDK
β βββ electron/ # Electron desktop SDK
β βββ react-native/ # React Native / Expo SDK
β βββ chrome-extension/ # Chrome Extension SDK
β βββ node-commons/ # Shared Node utilities
β βββ python/ # Python SDK (FastAPI, Flask, Django, Starlette, aiohttp) β authdog-fastapi
β βββ go/ # Go SDK (Gin)
β βββ rust/ # Rust workspace (axum, actix, rocket, warp, poem) β authdog-core + crates
β βββ kotlin/ # Kotlin SDK (Ktor) β authdog-ktor
β βββ java/ # Java SDK (Jakarta Servlet)
β βββ php/ # PHP SDK (PSR-15, Laravel)
β βββ ruby/ # Ruby SDK (Rack, Rails, Sinatra)
β βββ csharp/ # C# SDK (ASP.NET Core)
β βββ android/ # Android client
β βββ ios/ # iOS client
β βββ flutter/ # Flutter / Dart client
β βββ eslint/ # Shared ESLint config
β βββ typescript-config/# Shared tsconfig presets
βββ .moon/ # moon workspace & toolchain config
Versioning and changelogs are handled with Changesets:
bun run changeset # describe your change
bun run publish-packages # build + publish to npmContributions are welcome and appreciated! To get started:
- Fork the repository and create a feature branch.
- Make your changes and add a changeset (
bun run changeset). - Ensure
bun run cipasses. - Open a pull request describing your change.
Please report bugs and request features via GitHub Issues.
Found a vulnerability? Please do not open a public issue. See our Security Policy for responsible disclosure instructions.
MIT Β© Authdog