mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Lint & build / lint_and_build (latest) (push) Has been cancelled
Dev Environment Test With Custom Base Port / restart-dev-and-test-with-custom-base-port (push) Has been cancelled
Dev Environment Test / restart-dev-and-test (push) Has been cancelled
Run setup tests with custom base port / setup-tests-with-custom-base-port (push) Has been cancelled
Run setup tests / setup-tests (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **High Risk**
> Touches authentication and OAuth token/authorize flows and changes how
client requests are validated, so regressions could cause widespread
login/client-access failures. Also includes a data migration that alters
effective security posture for existing projects.
>
> **Overview**
> Adds a **project-level toggle**
(`project.requirePublishableClientKey`) to control whether client
requests/OAuth flows must include a publishable client key, including a
DB migration that backfills existing projects to require it.
>
> Backend auth now treats the publishable client key as *optional when
allowed*, introducing a public sentinel (`__stack_public_client__`) and
returning a new specific error
(`PUBLISHABLE_CLIENT_KEY_REQUIRED_FOR_PROJECT`) across smart request
auth + OAuth `authorize`/`callback`/`token` endpoints.
>
> Dashboard and SDKs update key generation/display and request
construction to handle missing publishable keys, expose an advanced
toggle on the Project Keys page, and extend internal config overrides to
support a new `project` level; E2E/tests and schema fuzzing are expanded
accordingly, and CI adds a forward-compat migration check job when
back-compat fails.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5d06c08613. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Project-level config to require publishable client keys; migration
applied to existing projects.
* **Improvements**
* Auth flows now support optional publishable client keys with explicit
validation and a sentinel for keyless OAuth.
* Dashboard/UI and SDKs handle publishable keys as optional and
conditionally show/generate them.
* Admin/client APIs extended to manage project-level overrides.
* **Bug Fixes**
* Key validation behavior aligned with project config.
* **Tests**
* Expanded E2E and unit tests covering optional/required publishable-key
scenarios.
* **Documentation**
* Spec and knowledge docs updated to describe the sentinel and config
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
90 lines
3.3 KiB
Swift
90 lines
3.3 KiB
Swift
import Foundation
|
|
#if canImport(FoundationNetworking)
|
|
import FoundationNetworking
|
|
#endif
|
|
@testable import StackAuth
|
|
|
|
/// Shared test configuration
|
|
/// Set environment variables to customize test behavior:
|
|
/// - NEXT_PUBLIC_STACK_PORT_PREFIX: Port prefix for backend (default: "81")
|
|
/// - STACK_SKIP_E2E_TESTS: Set to "true" to skip E2E tests
|
|
struct TestConfig {
|
|
static let portPrefix = ProcessInfo.processInfo.environment["NEXT_PUBLIC_STACK_PORT_PREFIX"] ?? "81"
|
|
static let baseUrl = "http://localhost:\(portPrefix)02"
|
|
static let skipE2E = ProcessInfo.processInfo.environment["STACK_SKIP_E2E_TESTS"] == "true"
|
|
|
|
// Test credentials - these should match the test project in the backend
|
|
// See apps/e2e/.env.development for the source of truth
|
|
static let projectId = "internal"
|
|
static let publishableClientKey = "this-publishable-client-key-is-for-local-development-only"
|
|
static let secretServerKey = "this-secret-server-key-is-for-local-development-only"
|
|
|
|
/// Check if backend is accessible
|
|
static func isBackendAvailable() async -> Bool {
|
|
guard !skipE2E else { return false }
|
|
|
|
guard let url = URL(string: "\(baseUrl)/api/v1/health") else { return false }
|
|
|
|
do {
|
|
let (_, response) = try await URLSession.shared.data(from: url)
|
|
if let httpResponse = response as? HTTPURLResponse {
|
|
return (200..<300).contains(httpResponse.statusCode)
|
|
}
|
|
return false
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Generate a unique test email
|
|
static func uniqueEmail() -> String {
|
|
"test-\(UUID().uuidString.lowercased())@example.com"
|
|
}
|
|
|
|
/// Generate a unique team name
|
|
static func uniqueTeamName() -> String {
|
|
"Test Team \(UUID().uuidString.prefix(8))"
|
|
}
|
|
|
|
/// Create a new client app instance for testing.
|
|
/// By default uses a fresh isolated MemoryTokenStore (not from the registry)
|
|
/// to avoid interference between parallel tests.
|
|
static func createClientApp(
|
|
tokenStore: TokenStoreInit? = nil,
|
|
publishableClientKey: String? = TestConfig.publishableClientKey
|
|
) -> StackClientApp {
|
|
// Default to a fresh isolated memory store, not the shared registry singleton
|
|
let store = tokenStore ?? .custom(MemoryTokenStore())
|
|
return StackClientApp(
|
|
projectId: projectId,
|
|
publishableClientKey: publishableClientKey,
|
|
baseUrl: baseUrl,
|
|
tokenStore: store,
|
|
noAutomaticPrefetch: true
|
|
)
|
|
}
|
|
|
|
/// Create a new server app instance for testing
|
|
static func createServerApp() -> StackServerApp {
|
|
StackServerApp(
|
|
projectId: projectId,
|
|
publishableClientKey: publishableClientKey,
|
|
secretServerKey: secretServerKey,
|
|
baseUrl: baseUrl
|
|
)
|
|
}
|
|
|
|
/// Standard test password that meets requirements
|
|
static let testPassword = "TestPassword123!"
|
|
|
|
/// Weak password that should be rejected
|
|
static let weakPassword = "123"
|
|
}
|
|
|
|
// MARK: - Convenience Aliases
|
|
|
|
let baseUrl = TestConfig.baseUrl
|
|
let testProjectId = TestConfig.projectId
|
|
let testPublishableClientKey = TestConfig.publishableClientKey
|
|
let testSecretServerKey = TestConfig.secretServerKey
|