- JavaScript 95.4%
- Nunjucks 4.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
`codeValidator` read the client an authorization code was issued to from `request.app.locals.client`, set during the authorization request. `app.locals` is shared by every request an Express application handles, so it holds whichever authorization request happened last on the server, not necessarily the one that issued the code being redeemed. Two consequences. A code exchange with no preceding authorization request found `client` undefined and failed at `client.id` with an unhandled TypeError and a 500, reachable unauthenticated. And the check that a code is redeemed by the client it was issued to could be satisfied by any client beginning its own authorization request first — a code issued to one client was redeemed by another, returning an access token for the profile URL and scope it carried. `consent.js` already signs `client_id` and `redirect_uri` into the code, and nothing read them. Verify the code first, compare the request against those claims, and reject a code missing either. PKCE keys off the challenge recorded in the code, so `app.locals` is no longer read here at all. `validateRedirect` is no longer called at redemption: the redirect was checked against client metadata during the authorization request, leaving only the match against the value the code was issued for. Seven existing tests signed codes carrying neither claim — codes this server cannot issue — and now sign what a real code carries. Two tests added, one per failure. Verified in the monorepo with this exact `lib/middleware/code.js` alongside the beta.35 `grant_type` change: 70 tests, 70 passing. The fork's own integration tests cannot run standalone (they need `@indiekit-test/*`), so that combined run is the evidence. Identical to the change proposed upstream in getindiekit/indiekit#893. Release: 1.0.0-beta.36 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGHR7MuyvBaDbAFfAGUxeT |
||
| assets | ||
| lib | ||
| locales | ||
| test | ||
| views | ||
| .gitignore | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| index.js | ||
| package.json | ||
| README.md | ||
@rmdes/indiekit-endpoint-auth
IndieAuth authentication and authorization endpoint for Indiekit. Grants and verifies access tokens and authenticates users.
This is a fork of @indiekit/endpoint-auth with custom authentication fixes (see below).
Installation
npm install @rmdes/indiekit-endpoint-auth
Or via npm overrides (recommended for replacing the default package):
{
"overrides": {
"@indiekit/endpoint-auth": "npm:@rmdes/indiekit-endpoint-auth@^1.0.0-beta.31"
}
}
Custom Fixes in This Fork
This fork includes the following improvements over the upstream package:
- Spec-Compliant redirect_uri Validation — Validates that
redirect_urihas the same scheme, host, and port asclient_idper IndieAuth specification (commit3e69d16) - Localhost Client Support — Handles
localhostclient IDs without attempting to fetch metadata (commit134c820) - Profile Scope Support — Adds support for IndieAuth profile scopes (commit
9d3351a) - Security Middleware Fix — Prevents double
next()calls in secret validation middleware (commitde7d8ca)
For full technical details, see CLAUDE.md in this repository.
Configuration
To customize the behavior of this plugin, add @rmdes/indiekit-endpoint-auth to your configuration:
import AuthorizationEndpoint from "@rmdes/indiekit-endpoint-auth";
export default {
plugins: [
new AuthorizationEndpoint({
mountPath: "/auth", // Default: "/auth"
}),
],
};
Environment Variables
Required:
SECRET- Strong random string used to sign and verify JWT tokensPASSWORD_SECRET- Hashed and salted password for user authentication. Generate at/auth/new-password
Options
| Option | Type | Default | Description |
|---|---|---|---|
mountPath |
string |
/auth |
Path to authorization endpoint. Must match your Indiekit instance URL path. |
Endpoints
Authorization Flow
GET /auth — Start the authorization flow. Shows either documentation (if no params) or the consent form
GET /auth/consent — Display the authentication/authorization consent form
POST /auth/consent — Submit credentials and grant authorization. Returns authorization code
POST /auth/token — Exchange authorization code for access token
POST /auth/introspect — Verify and inspect an access token
Utility
GET /auth/new-password — Display password secret generator UI
POST /auth/new-password — Generate a new hashed PASSWORD_SECRET value
GET /.well-known/oauth-authorization-server — OAuth authorization server metadata
GET /.well-known/change-password — Redirect to password secret generator
How It Works
- Authorization Request — Client app redirects user to
/auth?client_id=...&redirect_uri=...&state=... - Consent — User enters their password to grant authorization
- Authorization Code — Server issues a one-time authorization code
- Token Exchange — Client exchanges code for long-lived access token
- Token Introspection — Any service can verify tokens by POST'ing to
/auth/introspect
All tokens are JWTs signed with the SECRET environment variable.
Scopes
Supported Micropub scopes:
create— Create new postsdraft— Create draft postsupdate— Update existing postsdelete— Delete postsmedia— Upload media files
Supported Microsub scopes:
read— Read feedsfollow— Follow/subscribe to feedsmute,block— Manage blocked feedschannels— Create and manage feed channels
Unsupported scopes (shown as disabled in consent form):
email,profile— IndieAuth profile scopes
PKCE Support
If the client sends code_challenge and code_challenge_method, PKCE (Proof Key for Code Exchange) is required for token exchange. PKCE is optional but recommended for security, especially for mobile and single-page applications.
Client Information Discovery
The endpoint attempts to fetch client metadata in this order:
- JSON client metadata (preferred, per IndieAuth spec)
- h-app microformat (deprecated, backwards compatibility)
- Hostname fallback (from
client_idURL)
License
MIT - Original work by Paul Robert Lloyd, custom fixes by Ricardo Mendes.