Keycloak JavaScript 适配器

可用于保护 Web 应用程序的客户端 JavaScript 库。

Keycloak 带有一个名为 keycloak-js 的客户端 JavaScript 库,可用于保护 Web 应用程序。适配器还内置支持 Cordova 应用程序。

安装

我们建议您从 NPM 安装 keycloak-js

npm install keycloak-js

Keycloak 服务器配置

关于使用客户端应用程序需要注意的一点是,客户端必须是公共客户端,因为没有安全的方法将客户端凭据存储在客户端应用程序中。 这种考虑使得确保您为客户端配置的重定向 URI 正确且尽可能具体非常重要。

要使用适配器,请在 Keycloak 管理控制台中为您的应用程序创建一个客户端。通过在“功能配置”页面上将客户端身份验证切换到关闭来使客户端公开。

您还需要配置有效重定向 URIWeb 来源。请尽可能具体,因为未能做到这一点可能会导致安全漏洞。

使用适配器

以下示例显示了如何初始化适配器。确保您将传递给Keycloak构造函数的选项替换为您已配置的客户端的选项。

import Keycloak from 'keycloak-js';

const keycloak = new Keycloak({
    url: "http://keycloak-server",
    realm: "my-realm",
    clientId: "my-app"
});

try {
    const authenticated = await keycloak.init();
    if (authenticated) {
        console.log('User is authenticated');
    } else {
        console.log('User is not authenticated');
    }
} catch (error) {
    console.error('Failed to initialize adapter:', error);
}

要进行身份验证,请调用login函数。 有两个选项可以使适配器自动进行身份验证。您可以将login-requiredcheck-sso传递给init()函数。

  • login-required在用户登录 Keycloak 时对客户端进行身份验证,或者如果用户未登录,则显示登录页面。

  • check-sso仅在用户已登录时对客户端进行身份验证。如果用户未登录,则浏览器将重定向回应用程序,并保持未经身份验证。

您可以配置静默check-sso选项。启用此功能后,您的浏览器不会完全重定向到 Keycloak 服务器,然后再重定向回您的应用程序,而是此操作将在隐藏的 iframe 中执行。因此,您的应用程序资源只会被浏览器加载和解析一次,即在应用程序初始化时,而不是在从 Keycloak 重定向回您的应用程序后再次解析。这种方法在 SPA(单页面应用程序)的情况下特别有用。

要启用静默check-sso,请在 init 方法中提供silentCheckSsoRedirectUri属性。确保此 URI 是应用程序中的有效端点;它必须在 Keycloak 管理控制台中配置为客户端的有效重定向。

await keycloak.init({
    onLoad: 'check-sso',
    silentCheckSsoRedirectUri: `${location.origin}/silent-check-sso.html`
});

在成功检查您的身份验证状态并从 Keycloak 服务器检索令牌后,将在 iframe 中加载静默检查 sso 重定向 uri 处的页面。它没有其他任务,只是将接收到的令牌发送到主应用程序,并且应该只像这样

<!doctype html>
<html>
<body>
    <script>
        parent.postMessage(location.href, location.origin);
    </script>
</body>
</html>

请记住,此页面必须由您的应用程序在silentCheckSsoRedirectUri中指定的位置提供服务,并且不是适配器的一部分。

某些现代浏览器限制了静默check-sso功能。请参阅具有跟踪保护的现代浏览器部分

要启用login-required,请将onLoad设置为login-required并传递到 init 方法

await keycloak.init({
    onLoad: 'login-required'
});

用户身份验证后,应用程序可以通过在Authorization标头中包含承载令牌来向 Keycloak 保护的 RESTful 服务发出请求。例如

async function fetchUsers() {
    const response = await fetch('/api/users', {
        headers: {
            accept: 'application/json',
            authorization: `Bearer ${keycloak.token}`
        }
    });

    return response.json();
}

需要注意的是,默认情况下,访问令牌的有效期很短,因此您可能需要在发送请求之前刷新访问令牌。您可以通过调用updateToken()方法刷新此令牌。此方法返回一个 Promise,这使得在成功刷新令牌的情况下才调用服务并向用户显示错误变得容易,如果没有刷新则显示错误。例如

try {
    await keycloak.updateToken(30);
} catch (error) {
    console.error('Failed to refresh token:', error);
}

const users = await fetchUsers();

访问令牌和刷新令牌都存储在内存中,并且不会持久保存到任何类型的存储中。因此,这些令牌永远不应该被持久保存,以防止劫持攻击。

会话状态 iframe

默认情况下,适配器会创建一个隐藏的 iframe,用于检测是否发生了单点注销。此 iframe 不需要任何网络流量。相反,状态是通过查看特殊的 status cookie 来检索的。可以通过在传递给init()方法的选项中设置checkLoginIframe: false来禁用此功能。

您不应该依赖于直接查看此 cookie。它的格式可能会发生变化,并且它还与 Keycloak 服务器的 URL 相关联,而不是您的应用程序。

某些现代浏览器限制了会话状态 iframe 功能。请参阅具有跟踪保护的现代浏览器部分

隐式和混合流程

默认情况下,适配器使用授权码流程。

使用此流程,Keycloak 服务器会将授权码(而不是身份验证令牌)返回到应用程序。浏览器重定向回应用程序后,JavaScript 适配器会将code交换为访问令牌和刷新令牌。

Keycloak 还支持隐式流程,在该流程中,访问令牌会在 Keycloak 成功身份验证后立即发送。与标准流程相比,此流程的性能可能更好,因为不存在额外的请求来将代码交换为令牌,但它在访问令牌过期时会产生影响。

但是,在 URL 片段中发送访问令牌可能会导致安全漏洞。例如,令牌可能通过 Web 服务器日志或浏览器历史记录泄露。

要启用隐式流程,请在 Keycloak 管理控制台中为客户端启用隐式流程已启用标志。您还需要将参数flow与值implicit一起传递给init方法

await keycloak.init({
    flow: 'implicit'
})

请注意,只提供了访问令牌,没有刷新令牌。这种情况意味着,一旦访问令牌过期,应用程序就必须再次重定向到 Keycloak 以获取新的访问令牌。

Keycloak 还支持混合流程。

此流程要求客户端在管理控制台中同时启用标准流程隐式流程。然后,Keycloak 服务器会将代码和令牌都发送到您的应用程序。访问令牌可以立即使用,而代码可以交换为访问令牌和刷新令牌。与隐式流程类似,混合流程的性能良好,因为访问令牌可以立即获得。但是,令牌仍然发送到 URL,并且前面提到的安全漏洞可能仍然适用。

混合流程的一个优点是,刷新令牌可以提供给应用程序。

对于混合流程,您需要将参数flow与值hybrid一起传递给init方法

await keycloak.init({
    flow: 'hybrid'
});

使用 Cordova 的混合应用程序

Keycloak 支持使用Apache Cordova开发的混合移动应用程序。适配器为此提供了两种模式:cordovacordova-native

默认值为cordova,如果未明确配置适配器类型且window.cordova存在,则适配器会自动选择它。登录时,它会打开一个InApp 浏览器,允许用户与 Keycloak 交互,然后通过重定向到http://127.0.0.1返回到应用程序。由于这种行为,您将此 URL 作为有效重定向 URI 列入管理控制台的客户端配置部分的“白名单”。

虽然这种模式易于设置,但它也有一些缺点

  • InApp 浏览器是嵌入在应用程序中的浏览器,而不是手机的默认浏览器。因此,它将具有不同的设置,并且存储的凭据将不可用。

  • InApp 浏览器也可能速度较慢,尤其是在渲染更复杂的主题时。

  • 在使用这种模式之前,需要注意安全问题,例如,应用程序有可能访问用户的凭据,因为它完全控制渲染登录页面的浏览器,因此不要允许在您不信任的应用程序中使用它。

另一种模式是cordova-native,它采用不同的方法。它使用系统的浏览器打开登录页面。用户身份验证后,浏览器使用特殊 URL 重定向回应用程序。从那里,Keycloak 适配器可以通过从 URL 读取代码或令牌来完成登录。

您可以通过将适配器类型cordova-native传递给init()方法来激活本机模式

await keycloak.init({
    adapter: 'cordova-native'
});

此适配器需要两个额外的插件

链接到应用程序的技术细节在每个平台上都不同,需要特殊的设置。有关更多说明,请参阅deeplinks 插件文档中的 Android 和 iOS 部分。

存在用于打开应用程序的不同类型的链接

虽然前者更容易设置,并且通常运行得更可靠,但后者提供了额外的安全性,因为它们是唯一的,并且只有域的所有者才能注册它们。自定义 URL 在 iOS 上已被弃用。为了获得最佳可靠性,我们建议您使用通用链接,并结合使用使用自定义 URL 链接的回退站点。

此外,我们建议您采取以下步骤来提高与适配器的兼容性

  • iOS 上的通用链接似乎在response-mode设置为query时运行得更可靠

  • 要防止 Android 在重定向时打开您的应用程序的新实例,请将以下代码段添加到config.xml

<preference name="AndroidLaunchMode" value="singleTask" />

自定义适配器

在某些情况下,您可能需要在默认情况下不支持的环境中运行适配器,例如 Capacitor。要在这些环境中使用 JavasScript 客户端,您可以传递自定义适配器。例如,第三方库可以提供这样的适配器,使其能够可靠地运行适配器

import Keycloak from 'keycloak-js';
import KeycloakCapacitorAdapter from 'keycloak-capacitor-adapter';

const keycloak = new Keycloak({
    url: "http://keycloak-server",
    realm: "my-realm",
    clientId: "my-app"
});

await keycloak.init({
    adapter: KeycloakCapacitorAdapter,
});

此特定包不存在,但它提供了一个很好的示例,说明如何将这样的适配器传递给客户端。

也可以创建自己的适配器,为此,您必须实现KeycloakAdapter接口中描述的方法。例如,以下 TypeScript 代码确保所有方法都已正确实现

import Keycloak, { KeycloakAdapter } from 'keycloak-js';

// Implement the 'KeycloakAdapter' interface so that all required methods are guaranteed to be present.
const MyCustomAdapter: KeycloakAdapter = {
    async login(options) {
        // Write your own implementation here.
    }

    // The other methods go here...
};

const keycloak = new Keycloak({
    url: "http://keycloak-server",
    realm: "my-realm",
    clientId: "my-app"
});

await keycloak.init({
    adapter: MyCustomAdapter,
});

当然,您也可以在没有 TypeScript 的情况下执行此操作,方法是省略类型信息,但确保正确实现接口将完全取决于您。

具有跟踪保护的现代浏览器

在某些浏览器的最新版本中,应用了各种 Cookie 策略来防止第三方跟踪用户,例如 Chrome 中的 SameSite 策略或完全阻止第三方 Cookie。随着时间的推移,这些策略可能会变得更加严格,并被其他浏览器采用。最终,浏览器可能完全不支持和阻止第三方环境中的 Cookie。因此,受影响的适配器功能最终可能会被弃用。

适配器依赖于第三方 Cookie 来实现会话状态 iframe、静默check-sso,以及部分常规(非静默)check-sso。这些功能的功能有限,或者根据浏览器对 Cookie 的限制程度而完全禁用。适配器会尝试检测此设置并相应地做出反应。

具有“SameSite=Lax by Default”策略的浏览器

如果 Keycloak 端和应用程序端都配置了 SSL/TLS 连接,则所有功能都受支持。例如,Chrome 从版本 84 开始受到影响。

具有被阻止的第三方 Cookie 的浏览器

会话状态 iframe 不受支持,如果适配器检测到这种浏览器行为,则会自动禁用。这意味着适配器无法使用会话 Cookie 来检测单点注销,而必须完全依赖于令牌。因此,当用户在另一个窗口中注销时,使用适配器的应用程序不会注销,直到应用程序尝试刷新访问令牌为止。因此,请考虑将访问令牌生存期设置为相对较短的时间,以便尽快检测到注销。有关更多详细信息,请参阅 会话和令牌超时

静默check-sso 不受支持,默认情况下将回退到常规(非静默)check-sso。可以通过在传递给init方法的选项中设置silentCheckSsoFallback: false来更改此行为。在这种情况下,如果检测到浏览器行为限制,check-sso将完全禁用。

常规check-sso也会受到影响。由于会话状态 iframe 不受支持,因此在初始化适配器时必须进行额外的重定向到 Keycloak,以检查用户的登录状态。此检查不同于使用 iframe 来判断用户是否登录时的标准行为,并且仅在用户注销时才会执行重定向。

例如,从版本 13.1 开始的 Safari 就是受影响的浏览器。

API 参考

构造函数

// Recommended way to initialize the adapter.
new Keycloak({
    url: "http://keycloak-server",
    realm: "my-realm",
    clientId: "my-app"
});

// Alternatively a string to the path of the `keycloak.json` file.
// Has some performance implications, as it will load the keycloak.json file from the server.
// This version might also change in the future and is therefore not recommended.
new Keycloak("http://keycloak-server/keycloak.json");

属性

authenticated

如果用户已验证,则为true,否则为false

token

可以在请求中的Authorization标头中发送到服务的 base64 编码令牌。

tokenParsed

作为 JavaScript 对象解析的令牌。

subject

用户 ID。

idToken

base64 编码的 ID 令牌。

idTokenParsed

作为 JavaScript 对象解析的 ID 令牌。

realmAccess

与令牌关联的 realm 角色。

resourceAccess

与令牌关联的资源角色。

refreshToken

可以用来检索新令牌的 base64 编码刷新令牌。

refreshTokenParsed

作为 JavaScript 对象解析的刷新令牌。

timeSkew

浏览器时间与 Keycloak 服务器时间之间的估计时间差(以秒为单位)。此值只是一个估计值,但在确定令牌是否已过期时足够精确。

responseMode

在 init 中传递的响应模式(默认值为 fragment)。

flow

在 init 中传递的流程。

adapter

允许您覆盖库将如何处理重定向和其他与浏览器相关的功能。可用选项

  • "default" - 库使用浏览器 API 进行重定向(这是默认设置)

  • "cordova" - 库将尝试使用 InAppBrowser cordova 插件加载 keycloak 登录/注册页面(当库在 cordova 生态系统中工作时会自动使用)

  • "cordova-native" - 库尝试使用电话的系统浏览器通过 BrowserTabs cordova 插件打开登录和注册页面。这需要额外的设置才能重定向回应用程序(请参阅 使用 Cordova 的混合应用程序)。

  • "custom" - 允许您实现自定义适配器(仅供高级用例使用)

responseType

发送到 Keycloak 的包含登录请求的响应类型。这是根据初始化期间使用的 flow 值确定的,但可以通过设置此值来覆盖。

方法

init(options)

用于初始化适配器。

Options 是一个对象,其中

  • useNonce - 向身份验证响应添加密码 nonce 以验证身份验证响应是否与请求匹配(默认值为true)。

  • onLoad - 指定在加载时要执行的操作。支持的值是login-requiredcheck-sso

  • silentCheckSsoRedirectUri - 如果 onLoad 设置为 'check-sso',则设置静默身份验证检查的重定向 URI。

  • silentCheckSsoFallback - 当浏览器不支持静默check-sso时启用回退到常规check-sso(默认值为true)。

  • token - 设置令牌的初始值。

  • refreshToken - 设置刷新令牌的初始值。

  • idToken - 设置 ID 令牌的初始值(仅与令牌或刷新令牌一起使用)。

  • scope - 将默认范围参数设置为 Keycloak 登录端点。使用范围的空格分隔列表。这些通常引用在特定客户端上定义的 客户端范围。请注意,适配器将始终将范围openid添加到范围列表中。例如,如果您输入范围选项address phone,则对 Keycloak 的请求将包含范围参数scope=openid address phone。请注意,如果login()选项显式指定范围,则此处指定的默认范围将被覆盖。

  • timeSkew - 设置本地时间与 Keycloak 服务器时间之间的初始时间差(以秒为单位)(仅与令牌或刷新令牌一起使用)。

  • checkLoginIframe - 设置为启用/禁用监控登录状态(默认值为true)。

  • checkLoginIframeInterval - 设置检查登录状态的间隔(默认值为 5 秒)。

  • responseMode - 设置发送到 Keycloak 服务器的 OpenID Connect 响应模式,用于登录请求。有效值为queryfragment。默认值为fragment,这意味着在成功身份验证后,Keycloak 将重定向到 JavaScript 应用程序,并将 OpenID Connect 参数添加到 URL 片段中。这通常更安全,建议使用query

  • flow - 设置 OpenID Connect 流程。有效值为standardimplicithybrid

  • enableLogging - 启用从 Keycloak 到控制台的日志消息(默认值为false)。

  • pkceMethod - 用于 Proof Key Code Exchange (PKCE) 的方法。配置此值将启用 PKCE 机制。可用选项

    • "S256" - 基于 SHA256 的 PKCE 方法(默认设置)

    • false - PKCE 被禁用。

  • acrValues - 生成acr_values参数,该参数引用身份验证上下文类引用,并允许客户端声明所需的身份验证级别要求,例如身份验证机制。请参阅 OpenID Connect MODRNA 身份验证配置文件 1.0 中的第 4 节。acr_values 请求值和身份验证级别

  • messageReceiveTimeout - 设置等待来自 Keycloak 服务器的消息响应的超时时间(以毫秒为单位)。例如,在等待第三方 Cookie 检查期间的消息时,会使用此设置。默认值为 10000。

  • locale - 当 onLoad 为 'login-required' 时,设置ui_locales查询参数,以符合 OIDC 1.0 规范中第 3.1.2.1 节

返回一个承诺,该承诺在初始化完成后解析。

login(options)

重定向到登录表单,返回一个承诺。

Options 是一个可选对象,其中

  • redirectUri - 指定登录后要重定向到的 URI。

  • prompt - 此参数允许在 Keycloak 服务器端稍微自定义登录流程。例如,在值login的情况下强制显示登录屏幕。或者,在客户端具有Consent Required的情况下,强制显示同意屏幕以获取值consent。最后,可以使用值none来确保不向用户显示登录屏幕,这对于在用户之前已通过身份验证的情况下仅检查 SSO 非常有用(这与上面描述的具有值check-ssoonLoad检查相关)。

  • maxAge - 仅在用户已通过身份验证时使用。指定用户身份验证发生以来的最长时间。如果用户已通过身份验证的时间长于maxAge,则会忽略 SSO,用户将需要重新身份验证。

  • loginHint - 用于预填充登录表单中的用户名/电子邮件字段。

  • scope - 使用不同的值覆盖init中配置的范围,以用于此特定登录。

  • idpHint - 用于告知 Keycloak 跳过显示登录页面并自动重定向到指定的身份提供者。有关更多信息,请参阅 身份提供者文档

  • acr - 包含有关acr声明的信息,该声明将在claims参数中发送到 Keycloak 服务器。典型用法是用于逐步身份验证。使用示例{ values: ["silver", "gold"], essential: true }。有关更多详细信息,请参阅 OpenID Connect 规范和 逐步身份验证文档

  • acrValues - 生成acr_values参数,该参数引用身份验证上下文类引用,并允许客户端声明所需的身份验证级别要求,例如身份验证机制。请参阅 OpenID Connect MODRNA 身份验证配置文件 1.0 中的第 4 节。acr_values 请求值和身份验证级别

  • action - 如果值为register,则用户将重定向到注册页面。有关更多详细信息,请参阅 客户端请求的注册部分。如果值为UPDATE_PASSWORD或其他支持的必需操作,则用户将被重定向到重置密码页面或其他必需的操作页面。但是,如果用户未通过身份验证,则用户将被发送到登录页面并在身份验证后重定向。有关更多详细信息,请参阅 应用程序启动的操作部分

  • locale - 设置ui_locales查询参数,以符合 OIDC 1.0 规范中第 3.1.2.1 节

  • cordovaOptions - 指定传递给 Cordova in-app-browser 的参数(如果适用)。选项hiddenlocation不受这些参数的影响。所有可用选项都在 http://cordova.net.cn/docs/en/latest/reference/cordova-plugin-inappbrowser/ 中定义。使用示例:{ zoom: "no", hardwareback: "yes" };

createLoginUrl(options)

返回一个包含登录表单 URL 的承诺。

Options 是一个可选对象,它支持与函数login相同的选项。

logout(options)

重定向到注销。

Options 是一个对象,其中

  • redirectUri - 指定注销后要重定向到的 URI。

createLogoutUrl(options)

返回注销用户的 URL。

Options 是一个对象,其中

  • redirectUri - 指定注销后要重定向到的 URI。

register(options)

重定向到注册表单。登录的快捷方式,选项 action = 'register'

Options 与登录方法相同,但'action'设置为'register'

createRegisterUrl(options)

返回一个包含注册页面 URL 的承诺。createLoginUrl 的快捷方式,选项 action = 'register'

Options 与 createLoginUrl 方法相同,但'action'设置为'register'

accountManagement()

重定向到帐户控制台。

createAccountUrl(options)

返回帐户控制台的 URL。

Options 是一个对象,其中

  • redirectUri - 指定重定向回应用程序时要重定向到的 URI。

hasRealmRole(role)

如果令牌具有给定的领域角色,则返回 true。

hasResourceRole(role, resource)

如果令牌对资源具有给定的角色,则返回 true(资源是可选的,如果未指定,则使用 clientId)。

loadUserProfile()

加载用户的个人资料。

返回一个解析为个人资料的 Promise。

例如

try {
    const profile = await keycloak.loadUserProfile();
    console.log('Retrieved user profile:', profile);
} catch (error) {
    console.error('Failed to load user profile:', error);
}

isTokenExpired(minValidity)

如果令牌在过期前剩余时间少于 minValidity 秒,则返回 true(minValidity 是可选的,如果未指定,则使用 0)。

updateToken(minValidity)

如果令牌在 minValidity 秒内过期(minValidity 是可选的,如果未指定,则使用 5),则令牌将被刷新。如果将 -1 作为 minValidity 传递,则令牌将强制刷新。如果启用了会话状态 iframe,则还会检查会话状态。

返回一个解析为布尔值的 Promise,指示令牌是否已刷新。

例如

try {
    const refreshed = await keycloak.updateToken(5);
    console.log(refreshed ? 'Token was refreshed' : 'Token is still valid');
} catch (error) {
    console.error('Failed to refresh the token:', error);
}

clearToken()

清除身份验证状态,包括令牌。如果应用程序检测到会话已过期,这可能很有用,例如如果更新令牌失败。

调用此方法将导致 onAuthLogout 回调监听器被调用。

回调事件

适配器支持为某些事件设置回调监听器。请记住,这些必须在调用 init() 方法之前设置。

例如

keycloak.onAuthSuccess = () => console.log('Authenticated!');

可用的事件是

  • onReady(authenticated) - 当适配器初始化时调用。

  • onAuthSuccess - 当用户成功身份验证时调用。

  • onAuthError - 如果身份验证期间发生错误,则调用。

  • onAuthRefreshSuccess - 当令牌刷新时调用。

  • onAuthRefreshError - 如果尝试刷新令牌时发生错误,则调用。

  • onAuthLogout - 如果用户注销(仅在启用会话状态 iframe 或在 Cordova 模式下调用)。

  • onTokenExpired - 当访问令牌过期时调用。如果刷新令牌可用,则可以使用 updateToken 刷新令牌,或者在不可用时(即使用隐式流),您可以重定向到登录屏幕以获取新的访问令牌。

在此页面上