Skip to content
Open
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
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ AF_STACK_S3_REGION=us-east-1
# Enterprise SSO/SAML (#26): BackAI accepts OIDC here. For SAML
# customers, put Authentik in front as the self-hosted SAML-to-OIDC
# bridge, or use WorkOS as the managed broker. Register:
# <BETTER_AUTH_URL>/api/auth/oauth2/callback/enterprise-sso
# <BETTER_AUTH_URL>/api/auth/callback/enterprise-sso
# as the redirect URI in the broker.
# AF_STACK_SSO_LABEL=Enterprise SSO
# AF_STACK_SSO_ISSUER=https://auth.example.com/application/o/af-stack/
Expand Down
5 changes: 2 additions & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,9 @@ updates:
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
# 1.7 removed genericOAuthClient and signIn.oauth2. Stay on 1.6
# until the dashboard/customer-app auth clients are migrated.
# 1.8+ is the next breaking train after the 1.7 client migration.
- dependency-name: "better-auth"
versions: [">=1.7.0"]
versions: [">=1.8.0"]
labels:
- type:chore
- area:dashboard
Expand Down
12 changes: 12 additions & 0 deletions apps/customer-app/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ const nextConfig: NextConfig = {
// /api/v1/* is proxied to the runtime via the dynamic Route Handler at
// src/app/api/v1/[...path]/route.ts. RUNTIME_URL is resolved at request
// time so docker-compose env vars take effect without a rebuild.
//
// better-auth 1.7 moved generic-OAuth callbacks from
// /api/auth/oauth2/callback/:id to /api/auth/callback/:id. Keep the old
// path working so already-registered IdP redirect URIs still land.
async rewrites() {
return [
{
source: "/api/auth/oauth2/callback/:id",
destination: "/api/auth/callback/:id",
},
]
},
}

export default nextConfig
2 changes: 1 addition & 1 deletion apps/customer-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"@hookform/resolvers": "^5.9.2",
"@types/pg": "^8.23.1",
"bcryptjs": "^2.4.3",
"better-auth": "1.6.33",
"better-auth": "1.7.15",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^1.59.0",
Expand Down
4 changes: 2 additions & 2 deletions apps/customer-app/src/app/(auth)/sign-in/sign-in-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ function SignInInner({ sso }: SignInFormProps) {
const handleSSO = async () => {
setSubmitting(true)
try {
const result = await signIn.oauth2({
providerId: SSO_PROVIDER_ID,
const result = await signIn.social({
provider: SSO_PROVIDER_ID,
callbackURL: next,
})
if (result.error) {
Expand Down
8 changes: 4 additions & 4 deletions apps/customer-app/src/lib/auth-client.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: Apache-2.0

// Client-side auth helpers — used in React Server / Client Components.
//
// better-auth 1.7 folded generic OAuth into the social-provider path, so
// the client no longer needs genericOAuthClient(). SSO uses signIn.social.

import { createAuthClient } from "better-auth/react"
import { genericOAuthClient } from "better-auth/client/plugins"

export const authClient = createAuthClient({
plugins: [genericOAuthClient()],
})
export const authClient = createAuthClient()

export const { signIn, signUp, signOut, useSession } = authClient
9 changes: 2 additions & 7 deletions apps/customer-app/src/lib/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import { provisionTenant, lookupCustomerContext } from "@/lib/provisioning"
import { getCustomerSSOConfig } from "@/lib/sso"

function makeAuth() {
const databaseUrl =
process.env.DATABASE_URL ?? process.env.AF_STACK_DATABASE_URL
const databaseUrl = process.env.DATABASE_URL ?? process.env.AF_STACK_DATABASE_URL
if (!databaseUrl) {
return null
}
Expand Down Expand Up @@ -71,13 +70,11 @@ function makeAuth() {
config: [
{
providerId: sso.providerId,
issuer: sso.issuer,
discoveryUrl: sso.discoveryUrl,
clientId: process.env.AF_STACK_SSO_CLIENT_ID!,
clientSecret: process.env.AF_STACK_SSO_CLIENT_SECRET!,
scopes: sso.scopes,
pkce: true,
requireIssuerValidation: true,
overrideUserInfo: true,
},
],
Expand Down Expand Up @@ -128,9 +125,7 @@ export const auth =
{},
{
get() {
throw new Error(
"DATABASE_URL or AF_STACK_DATABASE_URL must be set for better-auth.",
)
throw new Error("DATABASE_URL or AF_STACK_DATABASE_URL must be set for better-auth.")
},
},
) as ReturnType<typeof betterAuth>)
Expand Down
14 changes: 13 additions & 1 deletion apps/dashboard/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ const nextConfig: NextConfig = {
// /api/v1/* is proxied to the runtime via the dynamic Route Handler at
// src/app/api/v1/[...path]/route.ts (so RUNTIME_URL is resolved at
// request time, not baked at build time).
//
// better-auth 1.7 moved generic-OAuth callbacks from
// /api/auth/oauth2/callback/:id to /api/auth/callback/:id. Keep the old
// path working so already-registered IdP redirect URIs still land.
async rewrites() {
return [
{
source: "/api/auth/oauth2/callback/:id",
destination: "/api/auth/callback/:id",
},
]
},
}

export default nextConfig
export default nextConfig
2 changes: 1 addition & 1 deletion apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@hookform/resolvers": "^5.9.2",
"@tanstack/react-table": "^8.21.3",
"@types/pg": "^8.23.1",
"better-auth": "1.6.33",
"better-auth": "1.7.15",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
Expand Down
8 changes: 4 additions & 4 deletions apps/dashboard/src/app/(auth)/login/login-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ function LoginFormInner({ sso }: LoginFormProps) {
const handleSSO = async () => {
setSubmitting(true)
try {
const result = await signIn.oauth2({
providerId: SSO_PROVIDER_ID,
const result = await signIn.social({
provider: SSO_PROVIDER_ID,
callbackURL: next,
})
if (result.error) {
Expand All @@ -114,8 +114,8 @@ function LoginFormInner({ sso }: LoginFormProps) {
<div className="border-destructive/30 bg-destructive/5 text-destructive flex gap-2 rounded-md border p-3 text-sm">
<AlertCircle className="mt-0.5 size-4 shrink-0" />
<p>
This account is not an operator. Sign in with the first operator account
created during setup.
This account is not an operator. Sign in with the first operator account created
during setup.
</p>
</div>
) : null}
Expand Down
7 changes: 5 additions & 2 deletions apps/dashboard/src/lib/auth-client.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// SPDX-License-Identifier: Apache-2.0

// Client-side auth helpers. Use these in React Server / Client Components.
//
// better-auth 1.7 folded generic OAuth into the social-provider path, so
// the client no longer needs genericOAuthClient(). SSO uses signIn.social.

import { createAuthClient } from "better-auth/react"
import { genericOAuthClient, magicLinkClient } from "better-auth/client/plugins"
import { magicLinkClient } from "better-auth/client/plugins"

export const authClient = createAuthClient({
plugins: [genericOAuthClient(), magicLinkClient()],
plugins: [magicLinkClient()],
})

export const { signIn, signUp, signOut, useSession } = authClient
2 changes: 0 additions & 2 deletions apps/dashboard/src/lib/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ function makeAuth() {
config: [
{
providerId: sso.providerId,
issuer: sso.issuer,
discoveryUrl: sso.discoveryUrl,
clientId:
process.env.AF_STACK_SSO_CLIENT_ID ?? process.env.DASHBOARD_SSO_CLIENT_ID!,
Expand All @@ -170,7 +169,6 @@ function makeAuth() {
process.env.DASHBOARD_SSO_CLIENT_SECRET!,
scopes: sso.scopes,
pkce: true,
requireIssuerValidation: true,
overrideUserInfo: true,
},
],
Expand Down
8 changes: 6 additions & 2 deletions docs/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,14 @@ AF_STACK_SSO_SCOPES="openid email profile"
Register these redirect URIs in Authentik, WorkOS, or your OIDC broker:

```text
https://admin.example.com/api/auth/oauth2/callback/enterprise-sso
https://app.example.com/api/auth/oauth2/callback/enterprise-sso
https://admin.example.com/api/auth/callback/enterprise-sso
https://app.example.com/api/auth/callback/enterprise-sso
```

better-auth 1.7 moved these off `/api/auth/oauth2/callback/:id`. The apps
still rewrite the old path so already-registered IdP URIs keep working;
register the new URIs on new brokers.

The provider must return an email claim. New SSO users are mirrored into
`suite_users` by the same better-auth hook as email/password users. They
do not become dashboard operators unless they are the first bootstrap
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"nanoid@<3.3.18": "3.3.18",
"postcss@<=8.5.22": ">=8.5.26",
"browserslist@<=4.28.6": ">=4.28.8",
"better-auth": "1.6.33"
"better-auth": "1.7.15"
},
"auditConfig": {
"ignoreGhsas": [
Expand Down
Loading
Loading