mirror of
https://github.com/hanzoai/id.git
synced 2026-10-16 18:38:15 +00:00
On fix/restore-design: wip: pkce/favicon edits on restore-design (redundant; main has pkce)
This commit is contained in:
@@ -25,6 +25,13 @@ export function Login({
|
||||
const redirectUri = sp.get('redirect_uri') ?? undefined
|
||||
const state = sp.get('state') ?? undefined
|
||||
const clientIdOverride = sp.get('client_id') ?? undefined
|
||||
// OAuth-authorize passthrough: when iam.hanzo.ai/login/oauth/authorize 302s a
|
||||
// downstream app (console, chat, …) here, the original PKCE challenge + scope
|
||||
// ride the query string. Forward them so the code IAM mints is bound to the
|
||||
// app's verifier — hanzo.id is the login UI, IAM stays the OAuth backend.
|
||||
const codeChallenge = sp.get('code_challenge') ?? undefined
|
||||
const codeChallengeMethod = (sp.get('code_challenge_method') as 'S256' | 'plain' | null) ?? undefined
|
||||
const scope = sp.get('scope') ?? undefined
|
||||
const accent = brand.accentColor ?? '#ffffff'
|
||||
const marketing = marketingFor(tenant.orgId)
|
||||
const search = window.location.search
|
||||
@@ -44,6 +51,9 @@ export function Login({
|
||||
redirectUri={redirectUri}
|
||||
state={state}
|
||||
clientIdOverride={clientIdOverride ?? undefined}
|
||||
codeChallenge={codeChallenge}
|
||||
codeChallengeMethod={codeChallengeMethod}
|
||||
scope={scope}
|
||||
/>
|
||||
<p className="hanzo-id-footer-links">
|
||||
<a href="/forget">Forgot password?</a>
|
||||
|
||||
+13
-2
@@ -63,8 +63,15 @@ export function createAuthClient(opts: AuthClientOptions): AuthClient {
|
||||
url.searchParams.set('clientId', req.clientId)
|
||||
url.searchParams.set('responseType', 'code')
|
||||
if (req.redirectUri) url.searchParams.set('redirectUri', req.redirectUri)
|
||||
url.searchParams.set('scope', 'openid profile email')
|
||||
url.searchParams.set('scope', req.scope ?? 'openid profile email')
|
||||
if (req.state) url.searchParams.set('state', req.state)
|
||||
// PKCE passthrough: when this login completes a downstream app's OAuth
|
||||
// authorize (console, chat, …), bind the app's challenge to the minted code
|
||||
// so IAM enforces it at /oauth/token against the app's verifier.
|
||||
if (req.codeChallenge) {
|
||||
url.searchParams.set('code_challenge', req.codeChallenge)
|
||||
url.searchParams.set('code_challenge_method', req.codeChallengeMethod ?? 'S256')
|
||||
}
|
||||
url.searchParams.set('type', type)
|
||||
const res = await f(url.toString(), {
|
||||
method: 'POST',
|
||||
@@ -251,8 +258,12 @@ export function createAuthClient(opts: AuthClientOptions): AuthClient {
|
||||
url.searchParams.set('clientId', req.clientId)
|
||||
url.searchParams.set('responseType', 'code')
|
||||
if (req.redirectUri) url.searchParams.set('redirectUri', req.redirectUri)
|
||||
url.searchParams.set('scope', 'openid profile email')
|
||||
url.searchParams.set('scope', req.scope ?? 'openid profile email')
|
||||
if (req.state) url.searchParams.set('state', req.state)
|
||||
if (req.codeChallenge) {
|
||||
url.searchParams.set('code_challenge', req.codeChallenge)
|
||||
url.searchParams.set('code_challenge_method', req.codeChallengeMethod ?? 'S256')
|
||||
}
|
||||
url.searchParams.set('type', type)
|
||||
const isEmail = req.dest.includes('@')
|
||||
const res = await f(url.toString(), {
|
||||
|
||||
@@ -6,6 +6,13 @@ export interface LoginRequest {
|
||||
readonly organization: string
|
||||
readonly redirectUri?: string
|
||||
readonly state?: string
|
||||
/** PKCE challenge to bind to the minted code, when this login completes an
|
||||
* OAuth authorize on behalf of a downstream app (console, chat, …). The app
|
||||
* holds the verifier and presents it at /oauth/token. Forwarded verbatim. */
|
||||
readonly codeChallenge?: string
|
||||
readonly codeChallengeMethod?: 'S256' | 'plain'
|
||||
/** OAuth scope requested by the downstream app (defaults to openid profile email). */
|
||||
readonly scope?: string
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
@@ -86,6 +93,11 @@ export interface CodeLoginRequest {
|
||||
readonly organization: string
|
||||
readonly redirectUri?: string
|
||||
readonly state?: string
|
||||
/** PKCE challenge to bind to the minted code (downstream-app OAuth passthrough). */
|
||||
readonly codeChallenge?: string
|
||||
readonly codeChallengeMethod?: 'S256' | 'plain'
|
||||
/** OAuth scope requested by the downstream app (defaults to openid profile email). */
|
||||
readonly scope?: string
|
||||
}
|
||||
|
||||
export interface TokenResponse {
|
||||
|
||||
@@ -9,6 +9,11 @@ export interface LoginFormProps {
|
||||
readonly redirectUri?: string
|
||||
readonly state?: string
|
||||
readonly clientIdOverride?: string
|
||||
/** PKCE challenge from a downstream app's OAuth authorize, forwarded to IAM. */
|
||||
readonly codeChallenge?: string
|
||||
readonly codeChallengeMethod?: 'S256' | 'plain'
|
||||
/** OAuth scope from the downstream app's authorize request. */
|
||||
readonly scope?: string
|
||||
readonly onSuccess?: (res: LoginResponse) => void
|
||||
readonly onMfaRequired?: (res: LoginResponse) => void
|
||||
}
|
||||
@@ -65,6 +70,9 @@ export function LoginForm(props: LoginFormProps) {
|
||||
organization: client.tenant.orgId,
|
||||
redirectUri: props.redirectUri,
|
||||
state: props.state,
|
||||
codeChallenge: props.codeChallenge,
|
||||
codeChallengeMethod: props.codeChallengeMethod,
|
||||
scope: props.scope,
|
||||
})
|
||||
handleResult(res)
|
||||
} catch (err) {
|
||||
@@ -106,6 +114,9 @@ export function LoginForm(props: LoginFormProps) {
|
||||
organization: client.tenant.orgId,
|
||||
redirectUri: props.redirectUri,
|
||||
state: props.state,
|
||||
codeChallenge: props.codeChallenge,
|
||||
codeChallengeMethod: props.codeChallengeMethod,
|
||||
scope: props.scope,
|
||||
})
|
||||
handleResult(res)
|
||||
} catch (err) {
|
||||
@@ -125,6 +136,9 @@ export function LoginForm(props: LoginFormProps) {
|
||||
redirectUri={props.redirectUri}
|
||||
state={props.state}
|
||||
clientIdOverride={props.clientIdOverride}
|
||||
codeChallenge={props.codeChallenge}
|
||||
codeChallengeMethod={props.codeChallengeMethod}
|
||||
scope={props.scope}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ export interface ProviderButtonsProps {
|
||||
readonly redirectUri?: string
|
||||
readonly state?: string
|
||||
readonly clientIdOverride?: string
|
||||
/** PKCE challenge from a downstream app's OAuth authorize (social login passthrough). */
|
||||
readonly codeChallenge?: string
|
||||
readonly codeChallengeMethod?: 'S256' | 'plain'
|
||||
readonly scope?: string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,6 +64,9 @@ export function ProviderButtons(props: ProviderButtonsProps) {
|
||||
redirectUri,
|
||||
state: props.state ?? mode,
|
||||
provider: p.name,
|
||||
scope: props.scope,
|
||||
codeChallenge: props.codeChallenge,
|
||||
codeChallengeMethod: props.codeChallengeMethod,
|
||||
})
|
||||
return (
|
||||
<a
|
||||
|
||||
Reference in New Issue
Block a user