ai-passport-signin
v0.1.1
Published
Sign in with AI Passport buttons and an OpenID Connect server helper.
Readme
ai-passport-signin
Add an AI Passport sign-in button to a browser page, React app, or server. The browser entry has no dependencies. The server helper uses Node built-ins.
Install
npm install ai-passport-signinBrowser button
Load the browser entry as a module. The custom element renders a real link inside its shadow root.
<ai-passport-button
href="/sign-in/ai-passport"
theme="light"
label="signin">
</ai-passport-button>
<script type="module">
import "ai-passport-signin";
</script>Set theme to light or dark. Set label to signin or continue.
Use full-bleed when the button should fill its container.
buttonHTML() returns a server-rendered HTML fragment. It needs no browser
runtime.
import { buttonHTML } from "ai-passport-signin";
const html = buttonHTML({
href: "/sign-in/ai-passport",
theme: "dark",
label: "continue",
});React
The React export uses the same custom element and requires React 18 or later.
import React from "react";
import { AIPassportButton } from "ai-passport-signin/react";
export function SignIn() {
return React.createElement(AIPassportButton, {
href: "/sign-in/ai-passport",
theme: "light",
label: "signin",
});
}Server helper
Use the server export in routes that start and finish sign-in. Keep the
returned state object in the user's server-side session. Do not send it to
the browser outside the authorization request.
Use an HTTPS issuer. The helper accepts HTTP only for loopback development issuers.
import { createPassportSignIn } from "ai-passport-signin/server";
const signIn = createPassportSignIn({
issuer: "https://passport.ego.ist",
clientId: "https://notes.example/ai-passport-client.json",
redirectUri: "https://notes.example/auth/ai-passport/callback",
scopes: ["openid", "profile", "email"],
});
// In the route that starts sign-in
const started = await signIn.begin();
session.aiPassportState = started.state;
return redirect(started.url);
// In the callback route
const result = await signIn.complete({
code: request.query.code,
state: request.query.state,
iss: request.query.iss,
storedState: session.aiPassportState,
});
delete session.aiPassportState;begin() reads OpenID Connect discovery, creates PKCE S256 values, and adds
the RFC 8707 MCP resource when you request memory. complete() checks the
RFC 9207 iss response parameter, exchanges the code, verifies the RS256 ID
token against JWKS, and checks issuer, audience, expiry, issued-at time, and
nonce.
The returned object has claims, tokens, and passport. passport is the
top-level ID token claim when the issuer sends it. passport.memory_access
only says the token may ask to read memory. Category passes still govern each
memory read.
Register your client
Use a Client Identifier Metadata Document (CIMD) first. Set clientId to the
HTTPS URL where you host the document. The URL must include a path. It cannot
include user information, a query, a fragment, or a non-default port.
Host this JSON at https://<your-host>/ai-passport-client.json.
import { clientMetadataDocument } from "ai-passport-signin/server";
export default clientMetadataDocument({
clientId: "https://notes.example/ai-passport-client.json",
clientName: "Acme Notes",
redirectUris: ["https://notes.example/auth/ai-passport/callback"],
});The document contains the exact client_id, a non-empty client_name, its
redirect URIs, authorization-code and refresh-token grants, code response
type, and public client authentication with none.
For automatic admission, every redirect host must equal the document host or be its strict subdomain. Sibling hosts, HTTP redirects, loopback hosts, IP literals, and mixed host trees use the manual admission path.
Registration is self-serve. Production authorization requires client admission. With CIMD, hosts consistent with the document host can be admitted automatically where auto-admission is enabled. Otherwise admission is a manual review step described in the docs at https://ego.ist/docs/sign-in.
For an older integration, use Dynamic Client Registration (DCR). DCR remains compatible but is deprecated for new integrations.
import { registerClient } from "ai-passport-signin/server";
const registration = await registerClient({
issuer: "https://passport.ego.ist",
clientName: "Acme Notes",
redirectUris: ["https://notes.example/auth/ai-passport/callback"],
});Store registration.client_id as durable configuration. DCR registration does
not grant production authorization.
Button rules
Use exactly Sign in with AI Passport or Continue with AI Passport. The
visible label is the accessible name.
Use the light variant on light surfaces. It uses a #11100E background and
white text. Use the dark variant on dark surfaces. It uses a white background,
near-black text, and a subtle border.
Both variants have a 44 pixel minimum height, 220 pixel minimum width, 16 pixel horizontal padding, 8 pixel radius, the official 22 pixel mark, and a 12 pixel gap before the label. Do not use the mark alone as a sign-in control.
API
| Export | Use |
| --- | --- |
| ai-passport-signin | buttonHTML(), button labels, and SVG assets |
| ai-passport-signin/react | AIPassportButton React wrapper |
| ai-passport-signin/server | OIDC sign-in and registration helpers |
createPassportSignIn({ issuer, clientId, redirectUri, scopes, fetch })
creates a helper. Supply fetch only for tests or a controlled transport.
clientMetadataDocument({ clientId, clientName, redirectUris }) returns the
CIMD JSON document.
registerClient({ issuer, clientName, redirectUris, fetch }) uses the
deprecated DCR fallback.
