Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
version: latest

web:
name: Web package
name: Web packages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
Expand All @@ -38,12 +38,12 @@ jobs:
bun-version: latest
- name: Install
run: bun install --frozen-lockfile
- name: Build package
run: bun run --filter '@rxtech-lab/admin-generator-next' build
- name: Typecheck
run: bun run --filter '@rxtech-lab/admin-generator-next' typecheck
- name: Test
run: bun run --filter '@rxtech-lab/admin-generator-next' test
- name: Build packages
run: bun run build
- name: Typecheck packages
run: bun run --filter '@rxtech-lab/*' typecheck
- name: Test packages
run: bun run test
- name: Build example app (RSC / directive regression net)
run: bun run --filter 'web-example' build
env:
Expand All @@ -62,8 +62,8 @@ jobs:
bun-version: latest
- name: Install
run: bun install --frozen-lockfile
- name: Build package
run: bun run --filter '@rxtech-lab/admin-generator-next' build
- name: Build packages
run: bun run build
- name: Install Playwright browser
run: bunx playwright install --with-deps chromium
# Root test:e2e builds the app, then Playwright boots the Go API + Next app.
Expand Down
19 changes: 14 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,22 @@ jobs:
registry-url: "https://registry.npmjs.org"
package-manager-cache: false

- name: Upgrade npm for trusted publishing
run: npm install --global "npm@^11.5.1"

- name: Install
run: bun install --frozen-lockfile

- name: Build package
run: bun run --filter '@rxtech-lab/admin-generator-next' build
- name: Build packages
run: bun run build

- name: Typecheck packages
run: bun run --filter '@rxtech-lab/*' typecheck

- name: Test packages
run: bun run test

# Sets packages/admin-next/package.json to the release tag version, then
# publishes to npm through the package's trusted publisher configuration.
- name: Publish to NPM
# Sets every public package to the release tag version, then publishes
# through each package's npm trusted-publisher configuration.
- name: Publish packages to NPM
run: node scripts/publish.js ${{ github.event.release.tag_name }}
39 changes: 30 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ catch-all route — no per-resource frontend code.

- **Go module** — `github.com/rxtech-lab/admin-generator`
- **npm package** — `@rxtech-lab/admin-generator-next`
- **Auth.js package** — `@rxtech-lab/authjs-rxlab`

## Repository layout

Expand All @@ -26,6 +27,7 @@ catch-all route — no per-resource frontend code.
| `datasource/gormds/` | GORM adapter (`DataSource`) |
| `datasource/memory/` | In-memory adapter for tests/demos |
| `packages/admin-next/` | The npm package (React 19 / Next 15, RJSF forms, shadcn-style theme) |
| `packages/authjs-rxlab/` | Auth.js v5 configuration for RxLab OIDC sessions and refresh-token rotation |
| `examples/server/` | Runnable Go demo (SQLite, seeded Authors + Posts) |
| `examples/web/` | Runnable Next.js demo consuming the package |

Expand Down Expand Up @@ -143,10 +145,22 @@ adminhttp.New(reg, adminhttp.WithAuthenticator(auth))

Gate resources on app-scoped roles with `oidc.RequireRole("admin")`.

**Frontend** — use Auth.js's generic OIDC provider (`issuer: "https://auth.rxlab.app"`,
PKCE + state), store the access token on the session, and return it from
`getToken`. A client must be pre-registered in the rxlab-auth dashboard with your
redirect URI and allowed scopes.
**Frontend** — use `@rxtech-lab/authjs-rxlab` to configure Auth.js with the
RxLab OIDC provider, refresh-token rotation, session access tokens, and
app-scoped roles:

```ts
import { createRxLabAuth } from "@rxtech-lab/authjs-rxlab";

export const { handlers, signIn, signOut, auth } = createRxLabAuth({
issuer: process.env.AUTH_ISSUER!,
clientId: process.env.AUTH_CLIENT_ID!,
clientSecret: process.env.AUTH_CLIENT_SECRET!,
});
```

A client must be pre-registered in the rxlab-auth dashboard with the Auth.js
callback URI and the `openid email profile offline_access` scopes.

## Run the demo

Expand All @@ -164,18 +178,25 @@ cd examples/web && bun run dev

```bash
go test ./... # Go core
bun run --filter '@rxtech-lab/admin-generator-next' test # package tests
bun run --filter '@rxtech-lab/admin-generator-next' build # package build
bun run test # npm package tests
bun run build # npm package builds
bun run --filter '@rxtech-lab/*' typecheck # npm package types
```

## Publishing

- **npm** — add a changeset (`bun run changeset`); merging to `main` opens a
Version Packages PR, and merging that publishes `@rxtech-lab/admin-generator-next` with
provenance (needs `NPM_TOKEN`).
- **npm** — manually run the `Create Release` workflow. Semantic-release creates
a versioned GitHub Release from conventional commits; `release.yml` then
builds, tests, stamps, and publishes both npm packages through npm trusted
publishing with OIDC.
- **Go** — tag the repo: `git tag v0.1.0 && git push --tags`, then
`go get github.com/rxtech-lab/admin-generator@v0.1.0`.

Each npm package must trust `rxtech-lab/admin-generator` and `release.yml` in
its npm settings. npm requires a package to exist before trusted publishing can
be configured, so bootstrap a brand-new package once with maintainer
credentials, configure the trusted publisher, and use CI for later releases.

## License

MIT
31 changes: 31 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"examples/web"
],
"scripts": {
"build": "bun run --filter '@rxtech-lab/admin-generator-next' build",
"build": "bun run --filter '@rxtech-lab/*' build",
"typecheck": "bun run --filter '*' typecheck",
"test": "bun run --filter '@rxtech-lab/admin-generator-next' test",
"test": "bun run --filter '@rxtech-lab/*' test",
"test:e2e": "bun run --filter 'web-example' test:e2e",
"test:e2e:ui": "bun run --filter 'web-example' test:e2e:ui"
}
Expand Down
21 changes: 21 additions & 0 deletions packages/authjs-rxlab/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 RxTech Lab

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
78 changes: 78 additions & 0 deletions packages/authjs-rxlab/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# @rxtech-lab/authjs-rxlab

Auth.js v5 integration for the RxLab OIDC service. It configures the RxLab
provider, keeps the OAuth access token and app-specific roles on the session,
and rotates expired access tokens without shortening the Auth.js session.

## Install

```bash
bun add @rxtech-lab/authjs-rxlab next-auth@beta
```

## Usage

```ts
// lib/auth.ts
import { createRxLabAuth } from "@rxtech-lab/authjs-rxlab";

export const { handlers, signIn, signOut, auth } = createRxLabAuth({
issuer: process.env.AUTH_ISSUER!,
clientId: process.env.AUTH_CLIENT_ID!,
clientSecret: process.env.AUTH_CLIENT_SECRET!,
signInPage: "/login",
});
```

```ts
// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/lib/auth";

export const { GET, POST } = handlers;
```

Sign in with the provider ID `rxlab`:

```ts
await signIn("rxlab", { redirectTo: "/admin" });
```

The resulting session includes the access token and RxLab roles:

```ts
const session = await auth();
const bearer = session?.accessToken;
const isAdmin = session?.user?.roles?.includes("admin") ?? false;
```

## Environment

The application still owns Auth.js's standard `AUTH_SECRET` and public base URL
configuration. Pass the RxLab client values explicitly so missing configuration
fails at startup instead of during an OAuth callback.

```dotenv
AUTH_ISSUER=https://auth.rxlab.app
AUTH_CLIENT_ID=your-client-id
AUTH_CLIENT_SECRET=your-client-secret
AUTH_SECRET=replace-with-a-random-secret
```

The RxLab OAuth client must allow your Auth.js callback URL and the scopes
`openid email profile offline_access`.

## Refresh behavior

- Uses an encrypted Auth.js JWT session lasting 30 days by default.
- Stores OAuth expiry in `expiresAt`, separate from Auth.js's reserved `exp`.
- Refreshes access tokens 60 seconds before expiry.
- Persists a rotated refresh token, or keeps the previous token when the server
does not return a replacement.
- Exposes `RefreshTokenError` on `session.error` when re-authentication is
required.
- Logs only token presence, expiry, event, and HTTP status. Token values are
never passed to the optional logger.

All defaults can be adjusted through `RxLabAuthOptions`. Advanced applications
can call `createRxLabAuthConfig(options)` and inspect or extend the resulting
Auth.js configuration before passing it to `NextAuth`.
44 changes: 44 additions & 0 deletions packages/authjs-rxlab/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "@rxtech-lab/authjs-rxlab",
"version": "0.1.0",
"description": "Auth.js integration for RxLab OIDC with refresh-token rotation and typed sessions.",
"license": "MIT",
"type": "module",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/rxtech-lab/admin-generator.git",
"directory": "packages/authjs-rxlab"
},
"files": [
"dist",
"README.md",
"LICENSE"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"scripts": {
"build": "tsc -p tsconfig.json",
"dev": "tsc -p tsconfig.json --watch",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test": "vitest run",
"clean": "rm -rf dist"
},
"peerDependencies": {
"next-auth": ">=5.0.0-beta.31 <6"
},
"devDependencies": {
"@types/node": "^22.13.1",
"next": "^15.1.6",
"next-auth": "5.0.0-beta.31",
"react": "^19.0.0",
"typescript": "^5.7.3",
"vitest": "^3.0.5"
}
}
Loading