使用 SDK 关联和取消关联社交/企业登录提供方¶
让已登录用户可以在您自己的界面中,将社交或企业登录提供方关联到账号,之后也可以取消关联。本指南以 Google 为示例提供方,但同样的代码适用于您配置的任何 OAuth 提供方。
说明
目前这些 SDK 方法仅在 Web SDK(@authgear/web) 中提供。如果使用其他 SDK,请使用 用户设置 页面,用户可以在那里管理自己的社交/企业登录连接。
您将实现的功能¶
在您的应用中实现一个页面(例如账号详情页),实现以下功能:
- 读取已登录用户已关联的身份。
- 当提供方尚未关联时显示 关联 按钮,已关联时显示 取消关联 按钮。
- 通过跳转到 Google 的授权同意页完成关联,然后返回您的应用。
- 通过跳转到 Authany 托管的确认页面完成取消关联,然后返回您的应用。
这两个操作都通过浏览器跳转完成。用户离开您的应用,在 Google 或 Authany 的页面上完成相应步骤,然后返回到您指定的重定向 URI。您的回调代码需要调用 finish 方法来完成该操作。
前提条件¶
- 已在应用中安装并配置
@authgear/webSDK。参见 开始使用。 - 用户 已登录。关联和取消关联都需要已通过身份验证的会话。如果不存在有效会话,这两个方法都会抛出异常。
- 已在 Authany 管理台的 社交 / 企业登录 下配置好该 OAuth 提供方,并设置了别名。本指南使用别名
google。 - 传给每个方法的重定向 URI,需要在管理台中该应用的 重定向 URI 设置下注册。
示例假设您的应用在配置 SDK 时使用了 sessionType: "refresh_token"。
步骤 1:检查提供方是否已关联¶
调用 fetchUserInfo() 获取用户信息,然后检查 identities 数组。每个 OAuth 身份都有 type === IdentityType.OAuth,以及与您在管理台中配置的别名相匹配的 oauthProviderAlias。
import authgear, { IdentityType } from "@authgear/web";
const PROVIDER_ALIAS = "google";
async function isProviderLinked() {
const userInfo = await authgear.fetchUserInfo();
return (userInfo.identities ?? []).some(
(identity) =>
identity.type === IdentityType.OAuth &&
identity.oauthProviderAlias === PROVIDER_ALIAS
);
}
来自 UserInfo 端点 的原始 identities 声明如下所示:
{
"https://authgear.com/claims/user/identities": [
{
"type": "login_id",
"login_id_key": "email",
"login_id_type": "email"
},
{
"type": "oauth",
"oauth_provider_type": "google",
"oauth_provider_alias": "google"
}
]
}
步骤 2:显示关联或取消关联按钮¶
根据当前状态渲染对应的按钮。
import { useEffect, useState } from "react";
import authgear, { IdentityType } from "@authgear/web";
const PROVIDER_ALIAS = "google";
function ProviderLinkButton() {
const [linked, setLinked] = useState<boolean | null>(null);
useEffect(() => {
authgear.fetchUserInfo().then((userInfo) => {
const isLinked = (userInfo.identities ?? []).some(
(i) =>
i.type === IdentityType.OAuth &&
i.oauthProviderAlias === PROVIDER_ALIAS
);
setLinked(isLinked);
});
}, []);
if (linked === null) {
return <p>Loading…</p>;
}
return linked ? (
<button onClick={startUnlink}>Unlink Google</button>
) : (
<button onClick={startLink}>Link Google</button>
);
}
<div id="provider-container"></div>
<script type="module">
import authgear, { IdentityType } from "@authgear/web";
const PROVIDER_ALIAS = "google";
const container = document.getElementById("provider-container");
const userInfo = await authgear.fetchUserInfo();
const linked = (userInfo.identities ?? []).some(
(i) =>
i.type === IdentityType.OAuth &&
i.oauthProviderAlias === PROVIDER_ALIAS
);
const button = document.createElement("button");
if (linked) {
button.textContent = "Unlink Google";
button.onclick = startUnlink;
} else {
button.textContent = "Link Google";
button.onclick = startLink;
}
container.appendChild(button);
</script>
步骤 3:关联提供方¶
调用 startLinkOAuth()。SDK 会将浏览器跳转到 Google 的授权同意页,用户在此登录并批准关联。批准后,Google 会跳转回您的 redirectURI。
function startLink() {
authgear
.startLinkOAuth({
oauthProviderAlias: PROVIDER_ALIAS,
redirectURI: "https://myapp.com/oauth-callback",
state: "link_oauth",
})
.catch((err) => {
console.error(err);
});
}
| Option | Type | Required | Description |
|---|---|---|---|
oauthProviderAlias |
string | 是 | 在管理台中配置的提供方别名,例如 google。 |
redirectURI |
string | 是 | 流程结束后浏览器返回的地址。必须在管理台中注册。 |
state |
string | 否 | 返回给您回调的 OAuth state 值。您可以用它来区分关联、取消关联和登录回调。 |
步骤 4:取消关联提供方¶
调用 startUnlinkOAuth()。SDK 会将浏览器跳转到 Authany 托管的页面,页面上有取消关联按钮。用户在该页面确认后,Authany 会跳转回您的 redirectURI。
function startUnlink() {
authgear
.startUnlinkOAuth({
oauthProviderAlias: PROVIDER_ALIAS,
redirectURI: "https://myapp.com/oauth-callback",
state: "unlink_oauth",
})
.catch((err) => {
console.error(err);
});
}
选项与 startLinkOAuth() 相同。请使用不同的 state 值,以便您的回调能识别要完成的是哪个操作。
步骤 5:处理回调¶
当用户返回到您的 redirectURI 时,完成该操作。读取 state 查询参数,以决定调用 finishLinkOAuth() 还是 finishUnlinkOAuth(),然后将用户送回您的账号详情页。
import { useEffect } from "react";
import authgear from "@authgear/web";
function OAuthCallback() {
useEffect(() => {
async function finish() {
// configure() must run before any finish method.
await authgear.configure({
clientID: "your-client-id",
endpoint: "https://myapp.authanyid.com",
sessionType: "refresh_token",
});
const state = new URL(window.location.href).searchParams.get("state");
try {
if (state === "link_oauth") {
await authgear.finishLinkOAuth();
} else if (state === "unlink_oauth") {
await authgear.finishUnlinkOAuth();
}
// Both actions are done. Return to the account details page.
window.location.replace("/account");
} catch (err) {
console.error(err);
}
}
finish();
}, []);
return <p>Finishing…</p>;
}
<script type="module">
import authgear from "@authgear/web";
// configure() must run before any finish method.
await authgear.configure({
clientID: "your-client-id",
endpoint: "https://myapp.authanyid.com",
sessionType: "refresh_token",
});
const state = new URL(window.location.href).searchParams.get("state");
try {
if (state === "link_oauth") {
await authgear.finishLinkOAuth();
} else if (state === "unlink_oauth") {
await authgear.finishUnlinkOAuth();
}
// Both actions are done. Return to the account details page.
window.location.replace("/account");
} catch (err) {
console.error(err);
}
</script>
注意
如果用户取消操作或流程失败,finishLinkOAuth() 和 finishUnlinkOAuth() 会以 OAuthError 拒绝(reject)。请在跳转前捕获该错误并向用户展示提示信息。
相关页面¶
- UserInfo 端点参考:完整的
identities声明。