Repository navigation
Conversation
Contributor
There was a problem hiding this comment.
Copilot was unable to review this pull request because there is no eligible user to bill.
An enterprise owner can allow the organization to be billed for reviews like this by enabling Copilot code review for users without a license.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
alliscode
force-pushed
the
copilot/hub-identity-behavior
branch
from
October 28, 2026 18:25
aa35eb3 to
b4b9597
Compare
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical defects can prevent API startup, break existing database deployments, and allow the legacy admission policy to admit any GitHub account through a public installed repository.
9 open issues
Add a new migration for session generation columns · New Validate configuration before advancing the provider revision · New Allocate provider revisions when existing providers are disabled · New Require explicit collaboration for public repository admission · New Avoid unsupported TypeScript parameter properties in API startup · New Display authorization errors reported by failed callbacks · New Refresh session state and CSRF tokens before mutations · New Bound repository admission checks during denied sign-ins · New Cover successful callback cookie, CSRF, and redirect protections · New
Review effort: Max
| -- CreateTable | ||
| CREATE TABLE "identity_store_states" ( | ||
| "id" UUID NOT NULL, | ||
| "session_generation" INTEGER NOT NULL DEFAULT 0, |
Comment on lines
+57
to
+68
| const store = createIdentityStore(prisma) | ||
| const providerRevision = configuration | ||
| ? await ensureGitHubProviderRevision(store, configuration) | ||
| : null | ||
| const admissionPolicy = createAdmissionPolicy(environment, store) | ||
| const settings: AuthenticationSettings = { | ||
| origin: origin.origin, | ||
| secureCookies: origin.protocol === "https:", | ||
| configured: configuration !== null, | ||
| admissionPolicy: admissionPolicy.name, | ||
| trustedProxyHops: readTrustedProxyHops(environment), | ||
| } |
| key: "github", | ||
| type: "github", | ||
| enabled: true, | ||
| revision: (current?.revision ?? 0) + 1, |
| "The repository permission identity did not match.", | ||
| ) | ||
| } | ||
| return permission.permission !== "none" |
Comment on lines
+1
to
+9
| export class AuthenticationError extends Error { | ||
| constructor( | ||
| readonly status: number, | ||
| readonly code: string, | ||
| message: string, | ||
| ) { | ||
| super(message) | ||
| } | ||
| } |
|
|
||
| export function AuthenticationApplication() { | ||
| const [session, setSession] = useState<SessionResponse | null>(null) | ||
| const [error, setError] = useState<string | null>(null) |
Comment on lines
+39
to
+59
| headers: { "X-CSRF-Token": session.csrfToken }, | ||
| }) | ||
| if (!response.ok) throw new Error("Sign-in request failed") | ||
| const data = (await response.json()) as { authorizationUrl: string } | ||
| window.location.assign(data.authorizationUrl) | ||
| } catch { | ||
| setError("Hub could not start GitHub sign-in.") | ||
| setLoading(false) | ||
| } | ||
| } | ||
|
|
||
| async function signOut() { | ||
| if (!session) return | ||
| setLoading(true) | ||
| setError(null) | ||
| try { | ||
| const response = await fetch("/api/session", { | ||
| method: "DELETE", | ||
| credentials: "same-origin", | ||
| headers: { "X-CSRF-Token": session.csrfToken }, | ||
| }) |
Comment on lines
+47
to
+48
| for (const repository of response.repositories) { | ||
| if (await hasPermission(token, profile, repository.full_name)) return true |
| expect(runtime.service.revokeAll).toHaveBeenCalledWith(sessionToken) | ||
| }) | ||
|
|
||
| it("redirects callback failures generically and never logs OAuth secrets", async () => { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Why
Hub needs a production sign-in path that preserves the current GitHub-based experience without treating GitHub identity as permission to use Hub. GitHub proves who the user is; Hub applies a separate admission policy, and repository authorization remains a separate product boundary.
What changes
Hub now supports GitHub user authorization with OAuth state, a browser binding, and PKCE. Successful authorization creates a long-lived provider-neutral Hub session, while provider credentials and source-integration credentials remain independent.
The API sets protected session and CSRF cookies, checks the Hub origin for state-changing requests, omits authentication payloads from logs, and rate-limits authentication endpoints using the configured trusted-proxy boundary. Users can sign out, require fresh verification for sensitive actions, or revoke every session. Fresh verification expires after five minutes.
Admission is an explicit policy hook. The compatibility policy can admit a GitHub user under the configured rule, but authentication does not grant repository access and does not make repository permission checks the default admission mechanism.
Storybook screenshots
Hub/Pages/Identity/Sign in / Signed out — dark theme, 1280 × 720
Hub/Pages/Identity/Sign in / Signed in — dark theme, 1280 × 720
Hub/Pages/Identity/Sign in / Unconfigured — dark theme, 1280 × 720