全面定制Okta登录小部件:打造品牌化的安全登录体验
当你选择Okta作为你的身份与访问管理(IAM)提供商时,你可以获得的功能之一便是自定义Okta托管的登录小部件,这是我们推荐用于实现最高级别身份安全的方法。它是一个可自定义的JavaScript组件,提供了一个现成的登录界面,你可以立即将其用作Web应用程序的一部分。
Okta Identity Engine(OIE)利用身份验证策略来驱动身份验证挑战,而登录小部件支持多种身份验证因素,从基本的用户名/密码登录到更高级的场景,如多因素认证、生物识别、通行密钥、社交登录、账户注册和账户恢复等。在底层,它与Okta的API交互,因此你无需自己构建或管理复杂的身份验证逻辑。一切都为你处理好了!
使用Okta登录小部件的一个好处,特别是使用第三代标准版本时,是定制化可以通过设计令牌进行配置,这意味着你无需编写CSS来设计小部件元素的样式。
将Okta登录小部件风格与你的品牌相匹配
在本教程中,我们将为一个虚构的待办事项应用定制登录小部件。我们将进行以下更改:
- 替换字体选择
- 定义边框、错误和焦点颜色
- 从登录小部件中移除某些元素(如水平线)并添加自定义元素
- 将控件移至网站起始位置并添加背景面板
在没有任何更改的情况下,当你尝试登录Okta账户时,会看到如下界面:
在本教程结束时,你的登录界面将看起来像这样 🎉
我们将使用第三代登录小部件以及新的建议,通过设计令牌来自定义表单元素和样式。
目录
- 将Okta登录小部件风格与你的品牌相匹配
- 自定义Okta托管的登录页面
- 理解Okta托管的登录小部件默认代码
- 自定义Okta登录小部件内的UI元素
- 使用CSS自定义属性组织登录小部件定制
- 使用自定义颜色调色板扩展登录小部件主题
- 向登录小部件添加自定义HTML元素
- 覆盖Okta登录小部件元素样式
- 更改Okta托管的登录页面布局
- 自定义你的第三代Okta托管登录小部件
先决条件
要学习本教程,你需要:
- 一个启用了Identity Engine的Okta账户,例如集成者免费账户。我们使用的组织中的登录小部件版本是7.36。
- 你自己的域名
- 对HTML、CSS和JavaScript的基本了解
- 一个品牌设计方案。请自由发挥你的创造力!
让我们开始吧!
自定义你的Okta托管登录页面
在开始之前,你必须配置你的Okta组织以使用自定义域名。自定义域名启用了代码定制,允许我们不仅定制默认的Logo、背景、网站图标和两种颜色。以管理员身份登录并打开Okta管理控制台,导航到 定制 > 品牌 并选择 创建品牌 +。
按照自定义域名和电子邮件开发者文档在新品牌上设置你的自定义域名。
你也可以按照这篇文章操作,如果你更喜欢的话。
一旦你拥有一个带有自定义域名的工作品牌,请选择你的品牌进行配置。
首先,导航到 设置 并选择 使用第三代 以启用登录小部件第三代。保存你的选择。
⚠️ 注意
本文中的代码依赖于使用登录小部件第三代。它不适用于登录小部件第二代。
导航到 主题。你将看到一个看起来类似这样的默认品牌页面:
让我们开始使其更符合我们心中的主题。更改主色和辅色,然后用你喜欢的选项替换Logo和网站图标图像。
要更改颜色,请点击文本字段并输入每个颜色的十六进制代码。我们采用大胆多彩的风格,所以将使用 #ea3eda 作为主色,#ffa738 作为辅色,并为品牌上传Logo和网站图标图像。选择 保存。
现在,通过导航到品牌的登录URL查看你的登录页面。通过你的配置,登录小部件看起来比默认视图更有趣,但我们还可以让事情变得更令人兴奋。
让我们深入主要任务,自定义登录页面。在 主题 选项卡上:
- 在下拉菜单中选择 登录页面
- 选择 自定义 按钮
- 在 页面设计 选项卡上,选择 代码编辑器 切换开关以查看HTML页面
注意:只有当你配置了自定义域名时,才能启用代码编辑器。
理解Okta托管的登录小部件默认代码
如果你熟悉基本的HTML、CSS和JavaScript,登录代码看起来是标准的,尽管在某些领域有些不同寻常。我们应该检查两个主要的代码块:页面<body>标签的顶部和<script>标签中的登录配置。
第一个看起来像这样:
1
|
<div id="okta-login-container"></div>
|
第二个看起来像这样:
1
2
3
4
5
6
7
8
9
10
11
12
|
var config = OktaUtil.getSignInWidgetConfig();
// 渲染Okta登录小部件
var oktaSignIn = new OktaSignIn(config);
oktaSignIn.renderEl({ el: '#okta-login-container' },
OktaUtil.completeLogin,
function(error) {
// 记录配置小部件时发生的错误。
// 删除或替换为你自己的自定义错误处理程序。
console.log(error.message, error);
}
);
|
让我们仔细看看这段代码是如何工作的。在HTML中,有一个指定的父元素,OktaSignIn实例使用它来将登录小部件渲染为子节点。这意味着当页面加载时,你会在DOM中看到<div id="okta-login-container"></div>,其中包含登录小部件功能的HTML元素作为其子元素。登录小部件处理所有由策略定义的身份验证和用户注册过程,使我们能够完全专注于定制。
要创建登录小部件,我们需要传入配置。配置包括主题元素和标签消息等属性。renderEl()方法标识用于渲染登录小部件的HTML元素。我们传入#okta-login-container作为标识符。
#okta-login-container是一个CSS选择器。虽然任何正确的CSS选择器都有效,但我们建议你使用元素的ID。元素ID在HTML文档中必须是唯一的,因此这是最安全、最简单的方法。
自定义Okta登录小部件内的UI元素
现在我们对Okta登录小部件的工作原理有了基本的了解,让我们开始自定义代码。我们将从自定义登录小部件内的元素开始。要操作第三代Okta登录小部件的DOM元素,我们使用afterTransform方法。afterTransform方法允许我们为单个或所有表单删除或更新元素。
在代码编辑器视图中找到 编辑 按钮,它使代码编辑器可编辑,并表现得像一个轻量级IDE。
在<script>标签内的oktaSignIn.renderEl()方法下面,添加:
1
2
3
4
5
6
7
8
9
10
11
|
oktaSignIn.afterTransform('identify', ({ formBag }) => {
const title = formBag.uischema.elements.find(ele => ele.type === 'Title');
if (title) {
title.options.content = "登录并创建任务";
}
const help = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'help');
const unlock = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'unlock');
const divider = formBag.uischema.elements.find(ele => ele.type === 'Divider');
formBag.uischema.elements = formBag.uischema.elements.filter(ele => ![help, unlock, divider].includes(ele));
});
|
这个afterTransform钩子只在‘identify’表单之前运行。我们可以使用FormBag来查找和定位UI元素。afterTransform钩子是在渲染小部件之前操作登录小部件内DOM元素的一种更简化方法。例如,我们可以在渲染前按类型搜索元素并将其过滤出视图,这比在登录小部件渲染后操作DOM元素性能更高。在此代码片段中,我们过滤掉了诸如账户解锁元素和分隔符等元素。
让我们看看这看起来是什么样子。按 保存到草稿 和 发布。
导航到你的品牌的登录URL以查看你所做的更改。与默认状态相比,我们不再看到Logo下方的水平线或“帮助”链接。账户解锁元素也不再可用。
我们探索了如何自定义小部件元素。现在,让我们添加一些特色。
使用CSS自定义属性组织登录小部件定制
本质上,我们是在设计一个HTML文档的样式。这意味着我们操作登录小部件定制的方式与操作任何HTML页面相同,并且代码组织原则仍然适用。我们可以将自定义值定义为CSS自定义属性(也称为CSS变量)。
使用CSS变量定义样式可以使我们的代码保持DRY。设置可重用的样式值甚至超出了Okta托管的登录页面。如果你的组织公开托管带有品牌颜色定义为CSS自定义属性的样式表,你可以使用那里定义的颜色并链接你的样式表。
在进行代码编辑之前,确定你想要用于定制的字体。我们找到了要使用的标题和正文字体。
打开品牌的登录小部件代码编辑器,并选择 编辑 进行更改。
将字体导入HTML。你可以根据你的偏好使用<link>或@import。我们将<link>指令添加到了HTML的<head>中。
1
2
3
|
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:ital,wght@0,100..900;1,100..900&family=Poiret+One&display=swap" rel="stylesheet">
|
找到 <style nonce="{{nonceValue}}"> 标签。在该标签内,使用:root选择器定义你的属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
:root {
--color-gray: #4f4f4f;
--color-fuchsia: #ea3eda;
--color-orange: #ffa738;
--color-azul: #016fb9;
--color-cherry: #ea3e84;
--color-purple: #b13fff;
--color-black: #191919;
--color-white: #fefefe;
--color-bright-white: #fff;
--border-radius: 4px;
--font-header: 'Poiret One', sans-serif;
--font-body: 'Inter Tight', sans-serif;
}
|
请随意添加新属性或替换属性值以适应你的品牌。现在是添加你自己的品牌颜色和定制的好机会!
让我们使用设计令牌来配置带有我们变量的登录小部件。
找到 var config = OktaUtil.getSignInWidgetConfig();。在这行代码之后,使用你的CSS自定义属性设置设计令牌的值。你将使用var()函数来访问你的变量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
config.theme = {
tokens: {
BorderColorDisplay: 'var(--color-bright-white)',
PalettePrimaryMain: 'var(--color-fuchsia)',
PalettePrimaryDark: 'var(--color-purple)',
PalettePrimaryDarker: 'var(--color-purple)',
BorderRadiusTight: 'var(--border-radius)',
BorderRadiusMain: 'var(--border-radius)',
PalettePrimaryDark: 'var(--color-orange)',
FocusOutlineColorPrimary: 'var(--color-azul)',
TypographyFamilyBody: 'var(--font-body)',
TypographyFamilyHeading: 'var(--font-header)',
TypographyFamilyButton: 'var(--font-body)',
BorderColorDangerControl: 'var(--color-cherry)'
}
}
|
保存更改,发布页面,并查看你品牌的登录URI网站。耶!你看,没有边框轮廓,小部件和HTML元素的边框半径改变了,焦点颜色不同了,表单错误时元素轮廓的颜色也不同了。你可以检查HTML元素并查看计算后的样式。或者,如果你愿意,可以随意将CSS变量更新为更可见的内容。
当你检查你品牌的登录URL网站时,你会注意到字体没有正确加载,并且浏览器的调试控制台中有错误。这是因为你需要配置内容安全策略以允许从外部站点加载资源。CSP是一种安全措施,用于减轻跨站脚本攻击。你可以阅读安全头最佳实践概述以了解更多关于CSP的信息。
导航到你品牌的登录页面的 设置 选项卡。找到 内容安全策略 并按 编辑。添加外部资源的域名。在我们的示例中,我们只从Google Fonts加载资源,因此我们添加了以下两个域名:
*.googleapis.com
*.gstatic.com
按 保存到草稿 并按 发布 以查看你的更改。登录小部件现在显示你选择的字体!
使用自定义颜色调色板扩展登录小部件主题
在我们的示例中,我们有选择地添加了颜色。登录小部件设计系统遵循WCAG可访问性标准,并依赖于Material Design颜色调色板。
Okta根据你的主色生成符合可访问性标准和对比度要求的颜色。查看了解登录小部件颜色定制以了解更多关于颜色对比度以及Okta颜色生成的工作原理。你必须为配置提供可访问的颜色。
Material Design通过自定义颜色调色板来支持主题。所有可配置设计令牌的列表显示了所有可用选项,包括用于精确颜色控制的Hue*属性。考虑探索适合你品牌特定需求的颜色调色板定制选项。你可以使用Material调色板生成器,例如来自Google团队的此颜色选择器或允许你输入HEX颜色值的开源Material Design调色板生成器。
不要忘记注意可访问性。你可以使用Chrome浏览器中的Lighthouse和WebAIM对比度检查器运行可访问性审计。我们选择的主色不太符合对比度要求。😅
向登录小部件添加自定义HTML元素
之前,我们将HTML元素过滤出了登录小部件。我们还可以向登录小部件添加新的自定义HTML元素。我们将通过添加指向Okta开发者博客的链接来进行实验。找到afterTransform()方法。更新afterTransform()方法,使其看起来像这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
oktaSignIn.afterTransform('identify', ({formBag}) => {
const title = formBag.uischema.elements.find(ele => ele.type === 'Title');
if (title) {
title.options.content = "登录并创建任务";
}
const help = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'help');
const unlock = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'unlock');
const divider = formBag.uischema.elements.find(ele => ele.type === 'Divider');
formBag.uischema.elements = formBag.uischema.elements.filter(ele => ![help, unlock, divider].includes(ele));
const blogLink = {
type: 'Link',
contentType: 'footer',
options: {
href: 'https://developer.okta.com/blog',
label: '阅读我们的博客',
dataSe: 'blogCustomLink'
}
};
formBag.uischema.elements.push(blogLink);
});
|
我们创建了一个名为blogLink的新元素,并设置了诸如类型、内容所在位置以及与类型相关的选项等属性。我们还添加了一个dataSe属性,它将值blogCustomLink添加到HTML数据属性中。这样做使我们更容易选择元素进行定制或测试。
当你继续登录流程中的‘identify’表单之后,你将不再看到指向博客的链接。
覆盖Okta登录小部件元素样式
我们应该尽可能使用设计令牌进行定制。在特定样式需求没有设计令牌可用的情况下,你可以回退到手动定义样式。
让我们从添加的元素——博客链接开始。假设我们想以大写形式显示文本。出于可访问性考虑,使用大写定义标签值不是好做法。我们应该使用CSS来转换文本。
在样式定义中,找到#login-bg-image-id。在背景图像的样式之后,添加样式以定位blogCustomLink数据属性,并定义文本转换,如下所示:
1
2
3
|
a[data-se="blogCustomLink"] {
text-transform: uppercase;
}
|
保存并发布页面以查看你的更改。
现在,假设你想样式化一个Okta提供的HTML元素。尽可能使用设计令牌,并且进行样式更改时要谨慎,因为这样做会增加脆弱性和安全隐患。
这里有一个样式化Okta提供的HTML元素的糟糕示例,你不应该效仿,因为它会使文本难以辨认。假设你想将“下一步”按钮的背景改为渐变。🌈
检查你想要样式化的登录小部件元素。我们想样式化具有数据属性okta-sign-in-header的按钮。
在blogCustomLink样式之后,添加以下内容:
1
2
3
|
button[data-se="save"] {
background: linear-gradient(12deg, var(--color-fuchsia) 0%, var(--color-orange) 100%);
}
|
保存并发布网站。按钮背景现在是渐变的。
但是,样式化Okta提供的登录小部件元素时要谨慎。这种方法的危险有两个方面:
- Okta登录小部件经过可访问性审计,手动更改样式和行为可能会降低可访问性阈值
- Okta登录小部件已经国际化,手动更改围绕文本布局的样式可能会破坏本地化需求
- Okta无法保证数据属性或DOM元素保持不变,从而导致定制中断
在极少数情况下,如果你样式化了Okta提供的登录小部件元素,你可能需要固定登录小部件版本,以免你的定制突然失效。导航到 设置 选项卡,找到 登录小部件版本 部分。选择 编辑 并选择最新版本的小部件,因为此版本应与你的代码兼容。我们在本文中使用的是小部件版本7.36。
⚠️ 注意
当你固定小部件版本时,你将无法获得登录小部件的最新更新,除非手动更新版本。固定版本会阻止终端用户体验的演进和扩展。为了最安全的选项,请允许登录小部件自动更新,并避免使用CSS过度定制登录小部件。尽可能使用设计令牌。
更改Okta托管的登录页面布局
到目前为止,我们尚未编辑登录小部件自定义中定义的HTML节点。你可以更改默认的<div>容器的布局,以产生重大影响。更改display CSS属性以产生有影响力的更改,例如使用Flexbox或CSS Grid。我将在此示例中使用Flexbox。
找到背景图像容器和okta-login-container的div。将这些div元素替换为以下HTML片段:
1
2
3
4
5
|
<div id="login-bg-image-id" class="login-bg-image tb--background">
<div class="login-container-panel">
<div id="okta-login-container"></div>
</div>
</div>
|
我们将okta-login-container div移到了另一个父容器内部,并使其成为背景图像容器的子元素。
找到#login-bg-image样式。添加display: flex;属性。样式应如下所示:
1
2
3
4
|
#login-bg-image-id {
background-image: {{bgImageUrl}};
display: flex;
}
|
我们想样式化okta-login-container的父<div>,以设置背景颜色并在面板上居中登录小部件。为login-container-panel类添加新样式:
1
2
3
4
5
6
7
8
|
.login-container-panel {
background: var(--color-white);
display: flex;
justify-content: center;
align-items: center;
width: 40%;
min-width: 400px;
}
|
保存更改并查看登录页面。你觉得新布局怎么样?🎊
⚠️ 注意
Flexbox和CSS Grid是响应式的,但你仍可能需要添加处理响应式或媒体查询的属性以满足你的需求。
你的最终代码可能看起来像这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="noindex,nofollow" />
<!-- 从主题生成的样式 -->
<link href="{{themedStylesUrl}}" rel="stylesheet" type="text/css">
<!-- 来自主题的网站图标 -->
<link rel="shortcut icon" href="{{faviconUrl}}" type="image/x-icon">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:ital,wght@0,100..900;1,100..900&family=Poiret+One&display=swap" rel="stylesheet">
<title>{{pageTitle}}</title>
{{{SignInWidgetResources}}}
<style nonce="{{nonceValue}}">
:root {
--font-header: 'Poiret One', sans-serif;
--font-body: 'Inter Tight', sans-serif;
--color-gray: #4f4f4f;
--color-fuchsia: #ea3eda;
--color-orange: #ffa738;
--color-azul: #016fb9;
--color-cherry: #ea3e84;
--color-purple: #b13fff;
--color-black: #191919;
--color-white: #fefefe;
--color-bright-white: #fff;
--border-radius: 4px;
}
{{ #useSiwGen3 }}
html {
font-size: 87.5%;
}
{{ /useSiwGen3 }}
#login-bg-image-id {
background-image: {{bgImageUrl}};
display: flex;
}
.login-container-panel {
background: var(--color-white);
display: flex;
justify-content: center;
align-items: center;
width: 40%;
min-width: 400px;
}
a[data-se="blogCustomLink"] {
text-transform: uppercase;
}
</style>
</head>
<body>
<div id="login-bg-image-id" class="login-bg-image tb--background">
<div class="login-container-panel">
<div id="okta-login-container"></div>
</div>
</div>
<!--
"OktaUtil"定义了一个全局OktaUtil对象
包含用于完成Okta登录流程的方法。
-->
{{{OktaUtil}}}
<script type="text/javascript" nonce="{{nonceValue}}">
// "config"对象包含默认小部件配置
// 以及你在管理设置中定义的任何自定义覆盖。
const config = OktaUtil.getSignInWidgetConfig();
config.theme = {
tokens: {
BorderColorDisplay: 'var(--color-bright-white)',
PalettePrimaryMain: 'var(--color-fuchsia)',
PalettePrimaryDark: 'var(--color-purple)',
PalettePrimaryDarker: 'var(--color-purple)',
BorderRadiusTight: 'var(--border-radius)',
BorderRadiusMain: 'var(--border-radius)',
PalettePrimaryDark: 'var(--color-orange)',
FocusOutlineColorPrimary: 'var(--color-azul)',
TypographyFamilyBody: 'var(--font-body)',
TypographyFamilyHeading: 'var(--font-header)',
TypographyFamilyButton: 'var(--font-body)',
BorderColorDangerControl: 'var(--color-cherry)'
}
}
// 渲染Okta登录小部件
const oktaSignIn = new OktaSignIn(config);
oktaSignIn.renderEl({ el: '#okta-login-container' },
OktaUtil.completeLogin,
function (error) {
// 记录配置小部件时发生的错误。
// 删除或替换为你自己的自定义错误处理程序。
console.log(error.message, error);
}
);
oktaSignIn.afterTransform('identify', ({ formBag }) => {
const title = formBag.uischema.elements.find(ele => ele.type === 'Title');
if (title) {
title.options.content = "登录并创建任务";
}
const help = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'help');
const unlock = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'unlock');
const divider = formBag.uischema.elements.find(ele => ele.type === 'Divider');
formBag.uischema.elements = formBag.uischema.elements.filter(ele => ![help, unlock, divider].includes(ele));
const blogLink = {
type: 'Link',
contentType: 'footer',
options: {
href: 'https://developer.okta.com/blog',
label: '阅读我们的博客',
dataSe: 'blogCustomLink'
}
};
formBag.uischema.elements.push(blogLink);
});
</script>
</body>
</html>
|
你也可以在本文的GitHub仓库中找到代码。通过这些代码更改,你可以将其与应用程序连接以查看其端到端的工作方式。你需要更新你的Okta OpenID Connect应用程序以使用域名。在Okta管理控制台中,导航到 应用程序 > 应用程序 并找到你的自定义应用的Okta应用程序。导航到 登录 选项卡。你将看到一个名为 OpenID Connect ID令牌 的部分。选择 编辑 并选择你的品牌登录URL作为 颁发者 值的自定义URL。
你将在自定义应用的OIDC配置中使用与你的品牌自定义URL匹配的颁发者值以及Okta应用程序的客户端ID。如果你想尝试这个并且没有预构建的应用程序,你可以使用我们的一个示例,例如Okta React示例。
自定义你的第三代Okta托管登录小部件
我希望你喜欢为你的品牌自定义登录体验。使用Okta托管的登录小部件是为你的网站添加身份安全的最佳、最安全的方式。利用所有可用的配置选项,你可以拥有高度自定义的登录体验,使用自定义域名,而无需任何人知道你正在使用Okta。
如果你喜欢这篇文章,你很可能会发现以下链接有帮助:
请记得在Twitter上关注我们,并订阅我们的YouTube频道,以获取有趣且具有教育意义的内容。我们也希望听到你想看到的主题和可能有的问题。请在下方留下评论!下次见!