- Elixir 100%
- Shell <1%
Atlogin #
A Plug-based OpenID Connect provider backed by AT Protocol identities. Phoenix is not required.
Status: experimental. Atlogin is an early port of apenwarr/atlogin. It has not had an independent security review or OIDC conformance test.
Features #
- OIDC discovery, JWKS, WebFinger, authorization code, PKCE, and UserInfo
- AT Protocol login through Atex
- RS256 ID tokens and opaque access tokens
- Exact redirect URI matching
- Single-use, expiring callback state and authorization codes
- ETS storage for single-node deployments
Client provisioning, refresh tokens, logout, durable storage, and a browser UI are not included yet.
Installation #
def deps do
[
{:atlogin, "~> 0.1.0"}
]
end
Atlogin requires Elixir 1.18 or later.
Setup #
Start a store under your application's supervisor:
children = [
{Atlogin.Store.ETS,
name: MyApp.AtloginStore,
sweep_interval: 30_000,
max_entries: 100_000}
]
Create the provider configuration at runtime. Keep both signing keys stable across restarts.
defmodule MyApp.IdentityProvider do
alias Atlogin.{Client, ClientRegistry, Store}
def config do
Atlogin.new!(
issuer: "https://id.example.com",
store: {Store.ETS, MyApp.AtloginStore},
clients: {
ClientRegistry.Static,
%{
"my-client" => %Client{
id: "my-client",
secret: System.fetch_env!("OIDC_CLIENT_SECRET"),
redirect_uris: ["https://app.example.com/oidc/callback"]
}
}
},
signing_key: {__MODULE__, :oidc_signing_key, []},
signing_key_id: "oidc-2026-01",
atproto: {
Atlogin.ATProto.Atex,
[
base_url: "https://id.example.com",
private_key: {__MODULE__, :atproto_private_key, []},
key_id: "atproto-2026-01"
]
}
)
end
def oidc_signing_key do
System.fetch_env!("OIDC_PRIVATE_JWK")
|> Jason.decode!()
|> JOSE.JWK.from_map()
end
def atproto_private_key do
System.fetch_env!("ATPROTO_PRIVATE_JWK")
|> Jason.decode!()
|> JOSE.JWK.from_map()
end
end
Mount it in any Plug router:
defmodule MyApp.Router do
use Plug.Router
plug :match
plug :dispatch
forward "/",
to: Atlogin.Plug,
init_opts: [config: {MyApp.IdentityProvider, :config, []}]
end
For local development, allow_insecure_http: true permits an HTTP issuer on
localhost, 127.0.0.1, or ::1. HTTP callback URIs are allowed only for the
exact host 127.0.0.1. Use HTTPS in public deployments.
Routes #
GET /.well-known/openid-configurationGET /.well-known/jwks.jsonGET /.well-known/webfingerGET /authorizeGET /client-metadata.jsonGET /atproto/callbackPOST /tokenGETorPOST /userinfo
Storage and security #
Atlogin.Store.ETS is for tests and single-node deployments. It is in-memory;
active logins are lost on restart. Multi-node deployments need a shared
Atlogin.Store implementation.
Atlogin fixes the issuer in configuration, verifies AT Protocol DIDs, requires
exact registered redirect URIs, and consumes callback state and authorization
codes once. The email claim is a login alias, not proof of mailbox ownership;
email_verified is always false.
Development #
mix test