From fd5afe9dcee654b0621daa7ca5ed53d74a288231 Mon Sep 17 00:00:00 2001 From: Carmen Date: Mon, 18 May 2026 23:34:36 +0100 Subject: [PATCH 1/4] Add LinkOAuth to SettingsAction enum and xSettingsAction union Co-Authored-By: Claude Sonnet 4.6 --- packages/authgear-core/src/types.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/authgear-core/src/types.ts b/packages/authgear-core/src/types.ts index 55fe0371..2ed0232c 100644 --- a/packages/authgear-core/src/types.ts +++ b/packages/authgear-core/src/types.ts @@ -167,7 +167,8 @@ export interface _OIDCAuthenticationRequest { | "add_username" | "change_email" | "change_phone" - | "change_username"; + | "change_username" + | "link_oauth"; xSettingsActionQuery?: _SettingsActionQuery; authenticationFlowGroup?: string; clientID?: string; @@ -572,6 +573,10 @@ export enum SettingsAction { * Change username in Authgear settings page. */ ChangeUsername = "change_username", + /** + * Link an OAuth provider in Authgear settings page. + */ + LinkOAuth = "link_oauth", } /** From d02e4787e890ec816159ebd37fb9791fe6cfeabd Mon Sep 17 00:00:00 2001 From: Carmen Date: Mon, 18 May 2026 23:34:39 +0100 Subject: [PATCH 2/4] Add LinkOAuthOptions and oauthProviderAlias to web types Co-Authored-By: Claude Sonnet 4.6 --- packages/authgear-web/src/types.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/authgear-web/src/types.ts b/packages/authgear-web/src/types.ts index 8b868306..326557f5 100644 --- a/packages/authgear-web/src/types.ts +++ b/packages/authgear-web/src/types.ts @@ -185,11 +185,24 @@ export interface SettingsActionOptions { uiLocales?: string[]; } +/** + * Options for connecting an OAuth provider via settings action. + * @public + */ +export interface LinkOAuthOptions extends SettingsActionOptions { + /** + * The alias of the OAuth provider to link, + * as configured in Authgear Portal under Social / Enterprise Login. + */ + oauthProviderAlias: string; +} + /** * @internal */ export interface _InternalSettingsActionOptions extends SettingsActionOptions { qLoginID?: string; + oauthProviderAlias?: string; } /** From 3f98cf7c2021e5c0acfc10484a0c066341092654 Mon Sep 17 00:00:00 2001 From: Carmen Date: Mon, 18 May 2026 23:34:43 +0100 Subject: [PATCH 3/4] Add startLinkOAuth and finishLinkOAuth to WebContainer Co-Authored-By: Claude Sonnet 4.6 --- packages/authgear-web/src/container.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/authgear-web/src/container.ts b/packages/authgear-web/src/container.ts index fd644ad2..0d67cc24 100644 --- a/packages/authgear-web/src/container.ts +++ b/packages/authgear-web/src/container.ts @@ -31,6 +31,7 @@ import { ReauthenticateResult, SettingsActionOptions, _InternalSettingsActionOptions, + LinkOAuthOptions, } from "./types"; /** @@ -669,6 +670,26 @@ export class WebContainer { return this.finishSettingsAction(); } + /** + * Start settings action "link_oauth" by redirecting to the authorization endpoint. + * + * @public + */ + async startLinkOAuth(options: LinkOAuthOptions): Promise { + await this.startSettingsAction(SettingsAction.LinkOAuth, options); + } + + /** + * Finish settings action "link_oauth". + * + * It may reject with OAuthError. + * + * @public + */ + async finishLinkOAuth(): Promise { + return this.finishSettingsAction(); + } + /** * Finish promote anonymous user. * From de746fc6327893345cf363c9f28cb2c63acc4bc5 Mon Sep 17 00:00:00 2001 From: Carmen Date: Tue, 19 May 2026 08:14:08 +0100 Subject: [PATCH 4/4] [Example] Add linkOAuth demo to reactweb example app Co-Authored-By: Claude Sonnet 4.6 --- example/reactweb/src/App.tsx | 51 +++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/example/reactweb/src/App.tsx b/example/reactweb/src/App.tsx index 7a8e9b21..e90244df 100644 --- a/example/reactweb/src/App.tsx +++ b/example/reactweb/src/App.tsx @@ -6,6 +6,7 @@ import authgear, { Page, WebContainerDelegate, SessionState, + LinkOAuthOptions, } from "@authgear/web"; import "./App.css"; @@ -66,6 +67,8 @@ function getOAuthState(): OAuthState | undefined { return "change_phone"; case "change_username": return "change_username"; + case "link_oauth": + return "link_oauth"; } return undefined; } @@ -81,7 +84,8 @@ type OAuthState = | "add_username" | "change_email" | "change_phone" - | "change_username"; + | "change_username" + | "link_oauth"; function ShowError(props: { error: unknown }) { const { error } = props; @@ -123,6 +127,7 @@ function Root() { const [page, setPage] = useState(); const [authenticationFlowGroup, setAuthenticationflowGroup] = useState(""); + const [oauthProviderAlias, setOauthProviderAlias] = useState(""); const [error, setError] = useState(null); const [userInfo, setUserInfo] = useState(null); @@ -323,6 +328,25 @@ function Root() { [userInfo] ); + const onClickLinkOAuth = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + if (oauthProviderAlias === "") { + setError(new Error("OAuth Provider Alias is required")); + return; + } + authgear + .startLinkOAuth({ + redirectURI: makeRedirectURI(), + state: "link_oauth", + oauthProviderAlias, + }) + .catch((err) => setError(err)); + }, + [oauthProviderAlias] + ); + const onClickChangeUsername = useCallback( (e: React.MouseEvent) => { e.preventDefault(); @@ -664,6 +688,23 @@ function Root() { > Change Username + + @@ -801,6 +842,14 @@ function AuthRedirect() { (err) => setError(err) ); break; + case "link_oauth": + authgear.finishLinkOAuth().then( + (_) => { + navigate("/"); + }, + (err) => setError(err) + ); + break; default: throw new Error("unknown oauth state: " + oauthState); }