Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
d8206434bc | |||
c71c2a8710 | |||
e55b881017 | |||
ab906ec417 | |||
0b1ff529f7 | |||
85a6835748 | |||
259271bc0f | |||
b7bc0f178b | |||
688455d0aa |
48
.github/workflows/ci.yaml
vendored
48
.github/workflows/ci.yaml
vendored
@ -13,10 +13,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ !github.event.created && github.repository != 'garronej/ts-ci' }}
|
if: ${{ !github.event.created && github.repository != 'garronej/ts-ci' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v2.1.3
|
- uses: actions/setup-node@v3
|
||||||
- uses: bahmutov/npm-install@v1
|
- uses: bahmutov/npm-install@v1
|
||||||
- name: If this step fails run 'npm run lint' and 'npm run format' then commit again.
|
- name: If this step fails run 'yarn format' then commit again.
|
||||||
run: |
|
run: |
|
||||||
PACKAGE_MANAGER=npm
|
PACKAGE_MANAGER=npm
|
||||||
if [ -f "./yarn.lock" ]; then
|
if [ -f "./yarn.lock" ]; then
|
||||||
@ -24,30 +24,31 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
$PACKAGE_MANAGER run format:check
|
$PACKAGE_MANAGER run format:check
|
||||||
test:
|
test:
|
||||||
runs-on: macos-10.15
|
runs-on: ${{ matrix.os }}
|
||||||
needs: test_lint
|
needs: test_lint
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node: [ '15', '14' ]
|
node: [ '14', '15' ,'16', '17' ]
|
||||||
name: Test with Node v${{ matrix.node }}
|
os: [ windows-latest, ubuntu-latest ]
|
||||||
|
name: Test with Node v${{ matrix.node }} on ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Tell if project is using npm or yarn
|
- name: Tell if project is using npm or yarn
|
||||||
id: step1
|
id: step1
|
||||||
uses: garronej/ts-ci@v1.1.7
|
uses: garronej/ts-ci@v2.0.2
|
||||||
with:
|
with:
|
||||||
action_name: tell_if_project_uses_npm_or_yarn
|
action_name: tell_if_project_uses_npm_or_yarn
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v2.1.3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- uses: bahmutov/npm-install@v1
|
- uses: bahmutov/npm-install@v1
|
||||||
- if: steps.step1.outputs.npm_or_yarn == 'yarn'
|
- if: steps.step1.outputs.npm_or_yarn == 'yarn'
|
||||||
run: |
|
run: |
|
||||||
|
yarn build
|
||||||
yarn test
|
yarn test
|
||||||
- if: steps.step1.outputs.npm_or_yarn == 'npm'
|
- if: steps.step1.outputs.npm_or_yarn == 'npm'
|
||||||
run: |
|
run: |
|
||||||
|
npm run build
|
||||||
npm test
|
npm test
|
||||||
check_if_version_upgraded:
|
check_if_version_upgraded:
|
||||||
name: Check if version upgrade
|
name: Check if version upgrade
|
||||||
@ -63,9 +64,9 @@ jobs:
|
|||||||
from_version: ${{ steps.step1.outputs.from_version }}
|
from_version: ${{ steps.step1.outputs.from_version }}
|
||||||
to_version: ${{ steps.step1.outputs.to_version }}
|
to_version: ${{ steps.step1.outputs.to_version }}
|
||||||
is_upgraded_version: ${{ steps.step1.outputs.is_upgraded_version }}
|
is_upgraded_version: ${{ steps.step1.outputs.is_upgraded_version }}
|
||||||
is_release_beta: ${{steps.step1.outputs.is_release_beta }}
|
is_pre_release: ${{steps.step1.outputs.is_pre_release }}
|
||||||
steps:
|
steps:
|
||||||
- uses: garronej/ts-ci@v1.1.7
|
- uses: garronej/ts-ci@v2.0.2
|
||||||
id: step1
|
id: step1
|
||||||
with:
|
with:
|
||||||
action_name: is_package_json_version_upgraded
|
action_name: is_package_json_version_upgraded
|
||||||
@ -73,13 +74,13 @@ jobs:
|
|||||||
|
|
||||||
create_github_release:
|
create_github_release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# We create a release only if the version have been upgraded and we are on a default branch
|
# We create a release only if the version have been upgraded and we are on the main branch
|
||||||
# PR on the default branch can release beta but not real release
|
# or if we are on a branch of the repo that has an PR open on main.
|
||||||
if: |
|
if: |
|
||||||
needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true' &&
|
needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true' &&
|
||||||
(
|
(
|
||||||
github.event_name == 'push' ||
|
github.event_name == 'push' ||
|
||||||
needs.check_if_version_upgraded.outputs.is_release_beta == 'true'
|
needs.check_if_version_upgraded.outputs.is_pre_release == 'true'
|
||||||
)
|
)
|
||||||
needs:
|
needs:
|
||||||
- check_if_version_upgraded
|
- check_if_version_upgraded
|
||||||
@ -91,7 +92,7 @@ jobs:
|
|||||||
target_commitish: ${{ github.head_ref || github.ref }}
|
target_commitish: ${{ github.head_ref || github.ref }}
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: ${{ needs.check_if_version_upgraded.outputs.is_release_beta == 'true' }}
|
prerelease: ${{ needs.check_if_version_upgraded.outputs.is_pre_release == 'true' }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@ -101,12 +102,11 @@ jobs:
|
|||||||
- create_github_release
|
- create_github_release
|
||||||
- check_if_version_upgraded
|
- check_if_version_upgraded
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.ref }}
|
ref: ${{ github.ref }}
|
||||||
- uses: actions/setup-node@v2.1.3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '15'
|
|
||||||
registry-url: https://registry.npmjs.org/
|
registry-url: https://registry.npmjs.org/
|
||||||
- uses: bahmutov/npm-install@v1
|
- uses: bahmutov/npm-install@v1
|
||||||
- run: |
|
- run: |
|
||||||
@ -115,7 +115,7 @@ jobs:
|
|||||||
PACKAGE_MANAGER=yarn
|
PACKAGE_MANAGER=yarn
|
||||||
fi
|
fi
|
||||||
$PACKAGE_MANAGER run build
|
$PACKAGE_MANAGER run build
|
||||||
- run: npx -y -p denoify@1.0.2 enable_short_npm_import_path
|
- run: npx -y -p denoify@1.2.2 enable_short_npm_import_path
|
||||||
env:
|
env:
|
||||||
DRY_RUN: "0"
|
DRY_RUN: "0"
|
||||||
- name: Publishing on NPM
|
- name: Publishing on NPM
|
||||||
@ -129,11 +129,11 @@ jobs:
|
|||||||
false
|
false
|
||||||
fi
|
fi
|
||||||
EXTRA_ARGS=""
|
EXTRA_ARGS=""
|
||||||
if [ "$IS_BETA" = "true" ]; then
|
if [ "$IS_PRE_RELEASE" = "true" ]; then
|
||||||
EXTRA_ARGS="--tag beta"
|
EXTRA_ARGS="--tag next"
|
||||||
fi
|
fi
|
||||||
npm publish $EXTRA_ARGS
|
npm publish $EXTRA_ARGS
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||||
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
|
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
|
||||||
IS_BETA: ${{ needs.check_if_version_upgraded.outputs.is_release_beta }}
|
IS_PRE_RELEASE: ${{ needs.check_if_version_upgraded.outputs.is_pre_release }}
|
@ -49,6 +49,12 @@
|
|||||||
|
|
||||||
# Changelog highlights
|
# Changelog highlights
|
||||||
|
|
||||||
|
## 6.8.0
|
||||||
|
|
||||||
|
- It is now possible to pass a custom `<Template />` component as a prop to `<KcApp />` and every
|
||||||
|
individual page (`<Login />`, `<RegisterUserProfile />`, ...) it enables to customize only the header and footer for
|
||||||
|
example without having to switch to a full-component level customization. [See issue](https://github.com/InseeFrLab/keycloakify/issues/191).
|
||||||
|
|
||||||
## 6.7.0
|
## 6.7.0
|
||||||
|
|
||||||
- Add support for `webauthn-authenticate.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/185).
|
- Add support for `webauthn-authenticate.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/185).
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloakify",
|
||||||
"version": "6.7.2",
|
"version": "6.8.3",
|
||||||
"description": "Keycloak theme generator for Reacts app",
|
"description": "Keycloak theme generator for Reacts app",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -78,12 +78,12 @@
|
|||||||
"@octokit/rest": "^18.12.0",
|
"@octokit/rest": "^18.12.0",
|
||||||
"cheerio": "^1.0.0-rc.5",
|
"cheerio": "^1.0.0-rc.5",
|
||||||
"cli-select": "^1.1.2",
|
"cli-select": "^1.1.2",
|
||||||
"evt": "^2.4.5",
|
"evt": "^2.4.6",
|
||||||
"memoizee": "^0.4.15",
|
"memoizee": "^0.4.15",
|
||||||
"minimal-polyfills": "^2.2.2",
|
"minimal-polyfills": "^2.2.2",
|
||||||
"minimist": "^1.2.6",
|
"minimist": "^1.2.6",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"powerhooks": "^0.20.22",
|
"powerhooks": "^0.20.23",
|
||||||
"react-markdown": "^5.0.3",
|
"react-markdown": "^5.0.3",
|
||||||
"rfc4648": "^1.5.2",
|
"rfc4648": "^1.5.2",
|
||||||
"scripting-tools": "^0.19.13",
|
"scripting-tools": "^0.19.13",
|
||||||
|
@ -32,60 +32,64 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
|||||||
"printIfExists": function (fieldName, x) {
|
"printIfExists": function (fieldName, x) {
|
||||||
<#if !messagesPerField?? >
|
<#if !messagesPerField?? >
|
||||||
return undefined;
|
return undefined;
|
||||||
|
<#else>
|
||||||
|
<#list fieldNames as fieldName>
|
||||||
|
if(fieldName === "${fieldName}" ){
|
||||||
|
<#attempt>
|
||||||
|
return "${messagesPerField.printIfExists(fieldName,'1')}" ? x : undefined;
|
||||||
|
<#recover>
|
||||||
|
</#attempt>
|
||||||
|
}
|
||||||
|
</#list>
|
||||||
|
throw new Error("There is no " + fieldName + " field");
|
||||||
</#if>
|
</#if>
|
||||||
<#list fieldNames as fieldName>
|
|
||||||
if(fieldName === "${fieldName}" ){
|
|
||||||
<#attempt>
|
|
||||||
return "${messagesPerField.printIfExists(fieldName,'1')}" ? x : undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
}
|
|
||||||
</#list>
|
|
||||||
throw new Error("There is no " + fieldName + " field");
|
|
||||||
},
|
},
|
||||||
"existsError": function (fieldName) {
|
"existsError": function (fieldName) {
|
||||||
<#if !messagesPerField?? >
|
<#if !messagesPerField?? >
|
||||||
return false;
|
return false;
|
||||||
|
<#else>
|
||||||
|
<#list fieldNames as fieldName>
|
||||||
|
if(fieldName === "${fieldName}" ){
|
||||||
|
<#attempt>
|
||||||
|
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
|
||||||
|
<#recover>
|
||||||
|
</#attempt>
|
||||||
|
}
|
||||||
|
</#list>
|
||||||
|
throw new Error("There is no " + fieldName + " field");
|
||||||
</#if>
|
</#if>
|
||||||
<#list fieldNames as fieldName>
|
|
||||||
if(fieldName === "${fieldName}" ){
|
|
||||||
<#attempt>
|
|
||||||
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
}
|
|
||||||
</#list>
|
|
||||||
throw new Error("There is no " + fieldName + " field");
|
|
||||||
},
|
},
|
||||||
"get": function (fieldName) {
|
"get": function (fieldName) {
|
||||||
<#if !messagesPerField?? >
|
<#if !messagesPerField?? >
|
||||||
return '';
|
return '';
|
||||||
|
<#else>
|
||||||
|
<#list fieldNames as fieldName>
|
||||||
|
if(fieldName === "${fieldName}" ){
|
||||||
|
<#attempt>
|
||||||
|
<#if messagesPerField.existsError('${fieldName}')>
|
||||||
|
return "${messagesPerField.get('${fieldName}')?no_esc}";
|
||||||
|
</#if>
|
||||||
|
<#recover>
|
||||||
|
</#attempt>
|
||||||
|
}
|
||||||
|
</#list>
|
||||||
|
throw new Error("There is no " + fieldName + " field");
|
||||||
</#if>
|
</#if>
|
||||||
<#list fieldNames as fieldName>
|
|
||||||
if(fieldName === "${fieldName}" ){
|
|
||||||
<#attempt>
|
|
||||||
<#if messagesPerField.existsError('${fieldName}')>
|
|
||||||
return "${messagesPerField.get('${fieldName}')?no_esc}";
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
}
|
|
||||||
</#list>
|
|
||||||
throw new Error("There is no " + fieldName + " field");
|
|
||||||
},
|
},
|
||||||
"exists": function (fieldName) {
|
"exists": function (fieldName) {
|
||||||
<#if !messagesPerField?? >
|
<#if !messagesPerField?? >
|
||||||
return false;
|
return false;
|
||||||
|
<#else>
|
||||||
|
<#list fieldNames as fieldName>
|
||||||
|
if(fieldName === "${fieldName}" ){
|
||||||
|
<#attempt>
|
||||||
|
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
|
||||||
|
<#recover>
|
||||||
|
</#attempt>
|
||||||
|
}
|
||||||
|
</#list>
|
||||||
|
throw new Error("There is no " + fieldName + " field");
|
||||||
</#if>
|
</#if>
|
||||||
<#list fieldNames as fieldName>
|
|
||||||
if(fieldName === "${fieldName}" ){
|
|
||||||
<#attempt>
|
|
||||||
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
}
|
|
||||||
</#list>
|
|
||||||
throw new Error("There is no " + fieldName + " field");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,40 +1,43 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Error = memo(
|
export type ErrorProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.Error;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.Error; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { message, client } = kcContext;
|
|
||||||
|
|
||||||
const { msg } = i18n;
|
const Error = memo((props: ErrorProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
return (
|
const { message, client } = kcContext;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
const { msg } = i18n;
|
||||||
displayMessage={false}
|
|
||||||
headerNode={msg("errorTitle")}
|
return (
|
||||||
formNode={
|
<Template
|
||||||
<div id="kc-error-message">
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<p className="instruction">{message.summary}</p>
|
displayMessage={false}
|
||||||
{client !== undefined && client.baseUrl !== undefined && (
|
headerNode={msg("errorTitle")}
|
||||||
<p>
|
formNode={
|
||||||
<a id="backToApplication" href={client.baseUrl}>
|
<div id="kc-error-message">
|
||||||
{msg("backToApplication")}
|
<p className="instruction">{message.summary}</p>
|
||||||
</a>
|
{client !== undefined && client.baseUrl !== undefined && (
|
||||||
</p>
|
<p>
|
||||||
)}
|
<a id="backToApplication" href={client.baseUrl}>
|
||||||
</div>
|
{msg("backToApplication")}
|
||||||
}
|
</a>
|
||||||
/>
|
</p>
|
||||||
);
|
)}
|
||||||
}
|
</div>
|
||||||
);
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default Error;
|
export default Error;
|
||||||
|
@ -1,57 +1,60 @@
|
|||||||
import React, { useState, memo } from "react";
|
import React, { useState, memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
import { UserProfileFormFields } from "./shared/UserProfileCommons";
|
import { UserProfileFormFields } from "./shared/UserProfileCommons";
|
||||||
|
|
||||||
const IdpReviewUserProfile = memo(
|
export type IdpReviewUserProfileProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.IdpReviewUserProfile;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.IdpReviewUserProfile; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { cx } = useCssAndCx();
|
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const IdpReviewUserProfile = memo((props: IdpReviewUserProfileProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { url } = kcContext;
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
return (
|
const { url } = kcContext;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
|
||||||
headerNode={msg("loginIdpReviewProfileTitle")}
|
|
||||||
formNode={
|
|
||||||
<form id="kc-idp-review-profile-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
|
||||||
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...props} />
|
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)} />
|
return (
|
||||||
</div>
|
<Template
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<input
|
headerNode={msg("loginIdpReviewProfileTitle")}
|
||||||
className={cx(
|
formNode={
|
||||||
props.kcButtonClass,
|
<form id="kc-idp-review-profile-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
props.kcButtonPrimaryClass,
|
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...kcProps} />
|
||||||
props.kcButtonBlockClass,
|
|
||||||
props.kcButtonLargeClass
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
)}
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
type="submit"
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)} />
|
||||||
value={msgStr("doSubmit")}
|
|
||||||
disabled={!isFomSubmittable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
}
|
<input
|
||||||
/>
|
className={cx(
|
||||||
);
|
kcProps.kcButtonClass,
|
||||||
}
|
kcProps.kcButtonPrimaryClass,
|
||||||
);
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doSubmit")}
|
||||||
|
disabled={!isFomSubmittable}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default IdpReviewUserProfile;
|
export default IdpReviewUserProfile;
|
||||||
|
@ -1,57 +1,60 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import { assert } from "../tools/assert";
|
import { assert } from "../tools/assert";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Info = memo(
|
export type InfoProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.Info;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.Info; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { msgStr, msg } = i18n;
|
|
||||||
|
|
||||||
assert(kcContext.message !== undefined);
|
const Info = memo((props: InfoProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { messageHeader, message, requiredActions, skipLink, pageRedirectUri, actionUri, client } = kcContext;
|
const { msgStr, msg } = i18n;
|
||||||
|
|
||||||
return (
|
assert(kcContext.message !== undefined);
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
|
||||||
displayMessage={false}
|
|
||||||
headerNode={messageHeader !== undefined ? <>{messageHeader}</> : <>{message.summary}</>}
|
|
||||||
formNode={
|
|
||||||
<div id="kc-info-message">
|
|
||||||
<p className="instruction">
|
|
||||||
{message.summary}
|
|
||||||
|
|
||||||
{requiredActions !== undefined && (
|
const { messageHeader, message, requiredActions, skipLink, pageRedirectUri, actionUri, client } = kcContext;
|
||||||
<b>{requiredActions.map(requiredAction => msgStr(`requiredAction.${requiredAction}` as const)).join(",")}</b>
|
|
||||||
)}
|
return (
|
||||||
</p>
|
<Template
|
||||||
{!skipLink && pageRedirectUri !== undefined ? (
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<p>
|
displayMessage={false}
|
||||||
<a href={pageRedirectUri}>{msg("backToApplication")}</a>
|
headerNode={messageHeader !== undefined ? <>{messageHeader}</> : <>{message.summary}</>}
|
||||||
</p>
|
formNode={
|
||||||
) : actionUri !== undefined ? (
|
<div id="kc-info-message">
|
||||||
<p>
|
<p className="instruction">
|
||||||
<a href={actionUri}>{msg("proceedWithAction")}</a>
|
{message.summary}
|
||||||
</p>
|
|
||||||
) : (
|
{requiredActions !== undefined && (
|
||||||
client.baseUrl !== undefined && (
|
<b>{requiredActions.map(requiredAction => msgStr(`requiredAction.${requiredAction}` as const)).join(",")}</b>
|
||||||
<p>
|
|
||||||
<a href={client.baseUrl}>{msg("backToApplication")}</a>
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</p>
|
||||||
}
|
{!skipLink && pageRedirectUri !== undefined ? (
|
||||||
/>
|
<p>
|
||||||
);
|
<a href={pageRedirectUri}>{msg("backToApplication")}</a>
|
||||||
}
|
</p>
|
||||||
);
|
) : actionUri !== undefined ? (
|
||||||
|
<p>
|
||||||
|
<a href={actionUri}>{msg("proceedWithAction")}</a>
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
client.baseUrl !== undefined && (
|
||||||
|
<p>
|
||||||
|
<a href={client.baseUrl}>{msg("backToApplication")}</a>
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default Info;
|
export default Info;
|
||||||
|
@ -3,6 +3,8 @@ import type { KcContextBase } from "../getKcContext/KcContextBase";
|
|||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import { __unsafe_useI18n as useI18n } from "../i18n";
|
import { __unsafe_useI18n as useI18n } from "../i18n";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
|
|
||||||
const Login = lazy(() => import("./Login"));
|
const Login = lazy(() => import("./Login"));
|
||||||
const Register = lazy(() => import("./Register"));
|
const Register = lazy(() => import("./Register"));
|
||||||
@ -26,79 +28,82 @@ const LogoutConfirm = lazy(() => import("./LogoutConfirm"));
|
|||||||
const UpdateUserProfile = lazy(() => import("./UpdateUserProfile"));
|
const UpdateUserProfile = lazy(() => import("./UpdateUserProfile"));
|
||||||
const IdpReviewUserProfile = lazy(() => import("./IdpReviewUserProfile"));
|
const IdpReviewUserProfile = lazy(() => import("./IdpReviewUserProfile"));
|
||||||
|
|
||||||
const KcApp = memo(
|
export type KcAppProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase;
|
||||||
kcContext,
|
i18n?: I18n;
|
||||||
i18n: userProvidedI18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
...kcProps
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
}: { kcContext: KcContextBase; i18n?: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
};
|
||||||
const i18n = (function useClosure() {
|
|
||||||
const i18n = useI18n({
|
|
||||||
kcContext,
|
|
||||||
"extraMessages": {},
|
|
||||||
"doSkip": userProvidedI18n !== undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
return userProvidedI18n ?? i18n;
|
const KcApp = memo((props_: KcAppProps) => {
|
||||||
})();
|
const { kcContext, i18n: userProvidedI18n, Template = DefaultTemplate, ...kcProps } = props_;
|
||||||
|
|
||||||
if (i18n === null) {
|
const i18n = (function useClosure() {
|
||||||
return null;
|
const i18n = useI18n({
|
||||||
}
|
kcContext,
|
||||||
|
"extraMessages": {},
|
||||||
|
"doSkip": userProvidedI18n !== undefined
|
||||||
|
});
|
||||||
|
|
||||||
const props = { i18n, ...kcProps };
|
return userProvidedI18n ?? i18n;
|
||||||
|
})();
|
||||||
|
|
||||||
return (
|
if (i18n === null) {
|
||||||
<Suspense>
|
return null;
|
||||||
{(() => {
|
|
||||||
switch (kcContext.pageId) {
|
|
||||||
case "login.ftl":
|
|
||||||
return <Login {...{ kcContext, ...props }} />;
|
|
||||||
case "register.ftl":
|
|
||||||
return <Register {...{ kcContext, ...props }} />;
|
|
||||||
case "register-user-profile.ftl":
|
|
||||||
return <RegisterUserProfile {...{ kcContext, ...props }} />;
|
|
||||||
case "info.ftl":
|
|
||||||
return <Info {...{ kcContext, ...props }} />;
|
|
||||||
case "error.ftl":
|
|
||||||
return <Error {...{ kcContext, ...props }} />;
|
|
||||||
case "login-reset-password.ftl":
|
|
||||||
return <LoginResetPassword {...{ kcContext, ...props }} />;
|
|
||||||
case "login-verify-email.ftl":
|
|
||||||
return <LoginVerifyEmail {...{ kcContext, ...props }} />;
|
|
||||||
case "terms.ftl":
|
|
||||||
return <Terms {...{ kcContext, ...props }} />;
|
|
||||||
case "login-otp.ftl":
|
|
||||||
return <LoginOtp {...{ kcContext, ...props }} />;
|
|
||||||
case "login-username.ftl":
|
|
||||||
return <LoginUsername {...{ kcContext, ...props }} />;
|
|
||||||
case "login-password.ftl":
|
|
||||||
return <LoginPassword {...{ kcContext, ...props }} />;
|
|
||||||
case "webauthn-authenticate.ftl":
|
|
||||||
return <WebauthnAuthenticate {...{ kcContext, ...props }} />;
|
|
||||||
case "login-update-password.ftl":
|
|
||||||
return <LoginUpdatePassword {...{ kcContext, ...props }} />;
|
|
||||||
case "login-update-profile.ftl":
|
|
||||||
return <LoginUpdateProfile {...{ kcContext, ...props }} />;
|
|
||||||
case "login-idp-link-confirm.ftl":
|
|
||||||
return <LoginIdpLinkConfirm {...{ kcContext, ...props }} />;
|
|
||||||
case "login-idp-link-email.ftl":
|
|
||||||
return <LoginIdpLinkEmail {...{ kcContext, ...props }} />;
|
|
||||||
case "login-page-expired.ftl":
|
|
||||||
return <LoginPageExpired {...{ kcContext, ...props }} />;
|
|
||||||
case "login-config-totp.ftl":
|
|
||||||
return <LoginConfigTotp {...{ kcContext, ...props }} />;
|
|
||||||
case "logout-confirm.ftl":
|
|
||||||
return <LogoutConfirm {...{ kcContext, ...props }} />;
|
|
||||||
case "update-user-profile.ftl":
|
|
||||||
return <UpdateUserProfile {...{ kcContext, ...props }} />;
|
|
||||||
case "idp-review-user-profile.ftl":
|
|
||||||
return <IdpReviewUserProfile {...{ kcContext, ...props }} />;
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
const commonProps = { i18n, Template, ...kcProps };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Suspense>
|
||||||
|
{(() => {
|
||||||
|
switch (kcContext.pageId) {
|
||||||
|
case "login.ftl":
|
||||||
|
return <Login {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "register.ftl":
|
||||||
|
return <Register {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "register-user-profile.ftl":
|
||||||
|
return <RegisterUserProfile {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "info.ftl":
|
||||||
|
return <Info {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "error.ftl":
|
||||||
|
return <Error {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-reset-password.ftl":
|
||||||
|
return <LoginResetPassword {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-verify-email.ftl":
|
||||||
|
return <LoginVerifyEmail {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "terms.ftl":
|
||||||
|
return <Terms {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-otp.ftl":
|
||||||
|
return <LoginOtp {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-username.ftl":
|
||||||
|
return <LoginUsername {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-password.ftl":
|
||||||
|
return <LoginPassword {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "webauthn-authenticate.ftl":
|
||||||
|
return <WebauthnAuthenticate {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-update-password.ftl":
|
||||||
|
return <LoginUpdatePassword {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-update-profile.ftl":
|
||||||
|
return <LoginUpdateProfile {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-idp-link-confirm.ftl":
|
||||||
|
return <LoginIdpLinkConfirm {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-idp-link-email.ftl":
|
||||||
|
return <LoginIdpLinkEmail {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-page-expired.ftl":
|
||||||
|
return <LoginPageExpired {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-config-totp.ftl":
|
||||||
|
return <LoginConfigTotp {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "logout-confirm.ftl":
|
||||||
|
return <LogoutConfirm {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "update-user-profile.ftl":
|
||||||
|
return <UpdateUserProfile {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "idp-review-user-profile.ftl":
|
||||||
|
return <IdpReviewUserProfile {...{ kcContext, ...commonProps }} />;
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default KcApp;
|
export default KcApp;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState, memo } from "react";
|
import React, { useState, memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
@ -7,197 +8,199 @@ import { useConstCallback } from "powerhooks/useConstCallback";
|
|||||||
import type { FormEventHandler } from "react";
|
import type { FormEventHandler } from "react";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Login = memo(
|
export type LoginProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.Login;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.Login; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
|
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const Login = memo((props: LoginProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
|
||||||
|
|
||||||
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
|
const { cx } = useCssAndCx();
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
setIsLoginButtonDisabled(true);
|
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
|
||||||
|
|
||||||
const formElement = e.target as HTMLFormElement;
|
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
//NOTE: Even if we login with email Keycloak expect username and password in
|
setIsLoginButtonDisabled(true);
|
||||||
//the POST request.
|
|
||||||
formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
|
|
||||||
|
|
||||||
formElement.submit();
|
const formElement = e.target as HTMLFormElement;
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
//NOTE: Even if we login with email Keycloak expect username and password in
|
||||||
<Template
|
//the POST request.
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
|
||||||
displayInfo={social.displayInfo}
|
|
||||||
displayWide={realm.password && social.providers !== undefined}
|
|
||||||
headerNode={msg("doLogIn")}
|
|
||||||
formNode={
|
|
||||||
<div id="kc-form" className={cx(realm.password && social.providers !== undefined && props.kcContentWrapperClass)}>
|
|
||||||
<div
|
|
||||||
id="kc-form-wrapper"
|
|
||||||
className={cx(
|
|
||||||
realm.password && social.providers && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass]
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{realm.password && (
|
|
||||||
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
|
||||||
{(() => {
|
|
||||||
const label = !realm.loginWithEmailAllowed
|
|
||||||
? "username"
|
|
||||||
: realm.registrationEmailAsUsername
|
|
||||||
? "email"
|
|
||||||
: "usernameOrEmail";
|
|
||||||
|
|
||||||
const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
|
formElement.submit();
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Template
|
||||||
<label htmlFor={autoCompleteHelper} className={cx(props.kcLabelClass)}>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
{msg(label)}
|
displayInfo={social.displayInfo}
|
||||||
</label>
|
displayWide={realm.password && social.providers !== undefined}
|
||||||
|
headerNode={msg("doLogIn")}
|
||||||
|
formNode={
|
||||||
|
<div id="kc-form" className={cx(realm.password && social.providers !== undefined && kcProps.kcContentWrapperClass)}>
|
||||||
|
<div
|
||||||
|
id="kc-form-wrapper"
|
||||||
|
className={cx(
|
||||||
|
realm.password && social.providers && [kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass]
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{realm.password && (
|
||||||
|
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
{(() => {
|
||||||
|
const label = !realm.loginWithEmailAllowed
|
||||||
|
? "username"
|
||||||
|
: realm.registrationEmailAsUsername
|
||||||
|
? "email"
|
||||||
|
: "usernameOrEmail";
|
||||||
|
|
||||||
|
const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label htmlFor={autoCompleteHelper} className={cx(kcProps.kcLabelClass)}>
|
||||||
|
{msg(label)}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
tabIndex={1}
|
||||||
|
id={autoCompleteHelper}
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
//NOTE: This is used by Google Chrome auto fill so we use it to tell
|
||||||
|
//the browser how to pre fill the form but before submit we put it back
|
||||||
|
//to username because it is what keycloak expects.
|
||||||
|
name={autoCompleteHelper}
|
||||||
|
defaultValue={login.username ?? ""}
|
||||||
|
type="text"
|
||||||
|
{...(usernameEditDisabled
|
||||||
|
? { "disabled": true }
|
||||||
|
: {
|
||||||
|
"autoFocus": true,
|
||||||
|
"autoComplete": "off"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
<label htmlFor="password" className={cx(kcProps.kcLabelClass)}>
|
||||||
|
{msg("password")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
tabIndex={2}
|
||||||
|
id="password"
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
|
||||||
|
<div id="kc-form-options">
|
||||||
|
{realm.rememberMe && !usernameEditDisabled && (
|
||||||
|
<div className="checkbox">
|
||||||
|
<label>
|
||||||
<input
|
<input
|
||||||
tabIndex={1}
|
tabIndex={3}
|
||||||
id={autoCompleteHelper}
|
id="rememberMe"
|
||||||
className={cx(props.kcInputClass)}
|
name="rememberMe"
|
||||||
//NOTE: This is used by Google Chrome auto fill so we use it to tell
|
type="checkbox"
|
||||||
//the browser how to pre fill the form but before submit we put it back
|
{...(login.rememberMe
|
||||||
//to username because it is what keycloak expects.
|
? {
|
||||||
name={autoCompleteHelper}
|
"checked": true
|
||||||
defaultValue={login.username ?? ""}
|
}
|
||||||
type="text"
|
: {})}
|
||||||
{...(usernameEditDisabled
|
|
||||||
? { "disabled": true }
|
|
||||||
: {
|
|
||||||
"autoFocus": true,
|
|
||||||
"autoComplete": "off"
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</>
|
{msg("rememberMe")}
|
||||||
);
|
</label>
|
||||||
})()}
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
<label htmlFor="password" className={cx(props.kcLabelClass)}>
|
{realm.resetPasswordAllowed && (
|
||||||
{msg("password")}
|
<span>
|
||||||
</label>
|
<a tabIndex={5} href={url.loginResetCredentialsUrl}>
|
||||||
<input
|
{msg("doForgotPassword")}
|
||||||
tabIndex={2}
|
</a>
|
||||||
id="password"
|
</span>
|
||||||
className={cx(props.kcInputClass)}
|
)}
|
||||||
name="password"
|
|
||||||
type="password"
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcFormGroupClass, props.kcFormSettingClass)}>
|
</div>
|
||||||
<div id="kc-form-options">
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
|
||||||
{realm.rememberMe && !usernameEditDisabled && (
|
<input
|
||||||
<div className="checkbox">
|
type="hidden"
|
||||||
<label>
|
id="id-hidden-input"
|
||||||
<input
|
name="credentialId"
|
||||||
tabIndex={3}
|
{...(auth?.selectedCredential !== undefined
|
||||||
id="rememberMe"
|
? {
|
||||||
name="rememberMe"
|
"value": auth.selectedCredential
|
||||||
type="checkbox"
|
}
|
||||||
{...(login.rememberMe
|
: {})}
|
||||||
? {
|
/>
|
||||||
"checked": true
|
<input
|
||||||
}
|
tabIndex={4}
|
||||||
: {})}
|
className={cx(
|
||||||
/>
|
kcProps.kcButtonClass,
|
||||||
{msg("rememberMe")}
|
kcProps.kcButtonPrimaryClass,
|
||||||
</label>
|
kcProps.kcButtonBlockClass,
|
||||||
</div>
|
kcProps.kcButtonLargeClass
|
||||||
)}
|
)}
|
||||||
</div>
|
name="login"
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
id="kc-login"
|
||||||
{realm.resetPasswordAllowed && (
|
type="submit"
|
||||||
<span>
|
value={msgStr("doLogIn")}
|
||||||
<a tabIndex={5} href={url.loginResetCredentialsUrl}>
|
disabled={isLoginButtonDisabled}
|
||||||
{msg("doForgotPassword")}
|
/>
|
||||||
</a>
|
</div>
|
||||||
</span>
|
</form>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormGroupClass)}>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
id="id-hidden-input"
|
|
||||||
name="credentialId"
|
|
||||||
{...(auth?.selectedCredential !== undefined
|
|
||||||
? {
|
|
||||||
"value": auth.selectedCredential
|
|
||||||
}
|
|
||||||
: {})}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
tabIndex={4}
|
|
||||||
className={cx(
|
|
||||||
props.kcButtonClass,
|
|
||||||
props.kcButtonPrimaryClass,
|
|
||||||
props.kcButtonBlockClass,
|
|
||||||
props.kcButtonLargeClass
|
|
||||||
)}
|
|
||||||
name="login"
|
|
||||||
id="kc-login"
|
|
||||||
type="submit"
|
|
||||||
value={msgStr("doLogIn")}
|
|
||||||
disabled={isLoginButtonDisabled}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{realm.password && social.providers !== undefined && (
|
|
||||||
<div id="kc-social-providers" className={cx(props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass)}>
|
|
||||||
<ul
|
|
||||||
className={cx(
|
|
||||||
props.kcFormSocialAccountListClass,
|
|
||||||
social.providers.length > 4 && props.kcFormSocialAccountDoubleListClass
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{social.providers.map(p => (
|
|
||||||
<li key={p.providerId} className={cx(props.kcFormSocialAccountListLinkClass)}>
|
|
||||||
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
|
|
||||||
<span>{p.displayName}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
{realm.password && social.providers !== undefined && (
|
||||||
infoNode={
|
<div id="kc-social-providers" className={cx(kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass)}>
|
||||||
realm.password &&
|
<ul
|
||||||
realm.registrationAllowed &&
|
className={cx(
|
||||||
!registrationDisabled && (
|
kcProps.kcFormSocialAccountListClass,
|
||||||
<div id="kc-registration">
|
social.providers.length > 4 && kcProps.kcFormSocialAccountDoubleListClass
|
||||||
<span>
|
)}
|
||||||
{msg("noAccount")}
|
>
|
||||||
<a tabIndex={6} href={url.registrationUrl}>
|
{social.providers.map(p => (
|
||||||
{msg("doRegister")}
|
<li key={p.providerId} className={cx(kcProps.kcFormSocialAccountListLinkClass)}>
|
||||||
</a>
|
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
|
||||||
</span>
|
<span>{p.displayName}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
}
|
</div>
|
||||||
/>
|
}
|
||||||
);
|
infoNode={
|
||||||
}
|
realm.password &&
|
||||||
);
|
realm.registrationAllowed &&
|
||||||
|
!registrationDisabled && (
|
||||||
|
<div id="kc-registration">
|
||||||
|
<span>
|
||||||
|
{msg("noAccount")}
|
||||||
|
<a tabIndex={6} href={url.registrationUrl}>
|
||||||
|
{msg("doRegister")}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default Login;
|
export default Login;
|
||||||
|
@ -1,192 +1,195 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginConfigTotp = memo(
|
export type LoginConfigTotpProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LoginConfigTotp;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LoginConfigTotp; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { url, isAppInitiatedAction, totp, mode, messagesPerField } = kcContext;
|
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const LoginConfigTotp = memo((props: LoginConfigTotpProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { url, isAppInitiatedAction, totp, mode, messagesPerField } = kcContext;
|
||||||
|
|
||||||
const algToKeyUriAlg: Record<KcContextBase.LoginConfigTotp["totp"]["policy"]["algorithm"], string> = {
|
const { cx } = useCssAndCx();
|
||||||
HmacSHA1: "SHA1",
|
|
||||||
HmacSHA256: "SHA256",
|
|
||||||
HmacSHA512: "SHA512"
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const { msg, msgStr } = i18n;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
|
||||||
headerNode={msg("loginTotpTitle")}
|
|
||||||
formNode={
|
|
||||||
<>
|
|
||||||
<ol id="kc-totp-settings">
|
|
||||||
<li>
|
|
||||||
<p>{msg("loginTotpStep1")}</p>
|
|
||||||
|
|
||||||
<ul id="kc-totp-supported-apps">
|
const algToKeyUriAlg: Record<KcContextBase.LoginConfigTotp["totp"]["policy"]["algorithm"], string> = {
|
||||||
{totp.policy.supportedApplications.map(app => (
|
"HmacSHA1": "SHA1",
|
||||||
<li>{app}</li>
|
"HmacSHA256": "SHA256",
|
||||||
))}
|
"HmacSHA512": "SHA512"
|
||||||
</ul>
|
};
|
||||||
</li>
|
|
||||||
|
|
||||||
{mode && mode == "manual" ? (
|
return (
|
||||||
<>
|
<Template
|
||||||
<li>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<p>{msg("loginTotpManualStep2")}</p>
|
headerNode={msg("loginTotpTitle")}
|
||||||
<p>
|
formNode={
|
||||||
<span id="kc-totp-secret-key">{totp.totpSecretEncoded}</span>
|
<>
|
||||||
</p>
|
<ol id="kc-totp-settings">
|
||||||
<p>
|
<li>
|
||||||
<a href={totp.qrUrl} id="mode-barcode">
|
<p>{msg("loginTotpStep1")}</p>
|
||||||
{msg("loginTotpScanBarcode")}
|
|
||||||
</a>
|
<ul id="kc-totp-supported-apps">
|
||||||
</p>
|
{totp.policy.supportedApplications.map(app => (
|
||||||
</li>
|
<li>{app}</li>
|
||||||
<li>
|
))}
|
||||||
<p>{msg("loginTotpManualStep3")}</p>
|
</ul>
|
||||||
<p>
|
</li>
|
||||||
<ul>
|
|
||||||
<li id="kc-totp-type">
|
{mode && mode == "manual" ? (
|
||||||
{msg("loginTotpType")}: {msg(`loginTotp.${totp.policy.type}`)}
|
<>
|
||||||
</li>
|
|
||||||
<li id="kc-totp-algorithm">
|
|
||||||
{msg("loginTotpAlgorithm")}: {algToKeyUriAlg?.[totp.policy.algorithm] ?? totp.policy.algorithm}
|
|
||||||
</li>
|
|
||||||
<li id="kc-totp-digits">
|
|
||||||
{msg("loginTotpDigits")}: {totp.policy.digits}
|
|
||||||
</li>
|
|
||||||
{totp.policy.type === "totp" ? (
|
|
||||||
<li id="kc-totp-period">
|
|
||||||
{msg("loginTotpInterval")}: {totp.policy.period}
|
|
||||||
</li>
|
|
||||||
) : (
|
|
||||||
<li id="kc-totp-counter">
|
|
||||||
{msg("loginTotpCounter")}: {totp.policy.initialCounter}
|
|
||||||
</li>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<li>
|
<li>
|
||||||
<p>{msg("loginTotpStep2")}</p>
|
<p>{msg("loginTotpManualStep2")}</p>
|
||||||
<img id="kc-totp-secret-qr-code" src={`data:image/png;base64, ${totp.totpSecretQrCode}`} alt="Figure: Barcode" />
|
|
||||||
<br />
|
|
||||||
<p>
|
<p>
|
||||||
<a href={totp.manualUrl} id="mode-manual">
|
<span id="kc-totp-secret-key">{totp.totpSecretEncoded}</span>
|
||||||
{msg("loginTotpUnableToScan")}
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href={totp.qrUrl} id="mode-barcode">
|
||||||
|
{msg("loginTotpScanBarcode")}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
)}
|
<li>
|
||||||
|
<p>{msg("loginTotpManualStep3")}</p>
|
||||||
|
<p>
|
||||||
|
<ul>
|
||||||
|
<li id="kc-totp-type">
|
||||||
|
{msg("loginTotpType")}: {msg(`loginTotp.${totp.policy.type}`)}
|
||||||
|
</li>
|
||||||
|
<li id="kc-totp-algorithm">
|
||||||
|
{msg("loginTotpAlgorithm")}: {algToKeyUriAlg?.[totp.policy.algorithm] ?? totp.policy.algorithm}
|
||||||
|
</li>
|
||||||
|
<li id="kc-totp-digits">
|
||||||
|
{msg("loginTotpDigits")}: {totp.policy.digits}
|
||||||
|
</li>
|
||||||
|
{totp.policy.type === "totp" ? (
|
||||||
|
<li id="kc-totp-period">
|
||||||
|
{msg("loginTotpInterval")}: {totp.policy.period}
|
||||||
|
</li>
|
||||||
|
) : (
|
||||||
|
<li id="kc-totp-counter">
|
||||||
|
{msg("loginTotpCounter")}: {totp.policy.initialCounter}
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
<li>
|
<li>
|
||||||
<p>{msg("loginTotpStep3")}</p>
|
<p>{msg("loginTotpStep2")}</p>
|
||||||
<p>{msg("loginTotpStep3DeviceName")}</p>
|
<img id="kc-totp-secret-qr-code" src={`data:image/png;base64, ${totp.totpSecretQrCode}`} alt="Figure: Barcode" />
|
||||||
|
<br />
|
||||||
|
<p>
|
||||||
|
<a href={totp.manualUrl} id="mode-manual">
|
||||||
|
{msg("loginTotpUnableToScan")}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
)}
|
||||||
|
<li>
|
||||||
|
<p>{msg("loginTotpStep3")}</p>
|
||||||
|
<p>{msg("loginTotpStep3DeviceName")}</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
<form action={url.loginAction} className={cx(props.kcFormClass)} id="kc-totp-settings-form" method="post">
|
<form action={url.loginAction} className={cx(kcProps.kcFormClass)} id="kc-totp-settings-form" method="post">
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<label htmlFor="totp" className={cx(props.kcLabelClass)}>
|
<label htmlFor="totp" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("authenticatorCode")}
|
{msg("authenticatorCode")}
|
||||||
</label>{" "}
|
</label>{" "}
|
||||||
<span className="required">*</span>
|
<span className="required">*</span>
|
||||||
</div>
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="totp"
|
|
||||||
name="totp"
|
|
||||||
autoComplete="off"
|
|
||||||
className={cx(props.kcInputClass)}
|
|
||||||
aria-invalid={messagesPerField.existsError("totp")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{messagesPerField.existsError("totp") && (
|
|
||||||
<span id="input-error-otp-code" className={cx(props.kcInputErrorMessageClass)} aria-live="polite">
|
|
||||||
{messagesPerField.get("totp")}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<input type="hidden" id="totpSecret" name="totpSecret" value={totp.totpSecret} />
|
|
||||||
{mode && <input type="hidden" id="mode" value={mode} />}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="totp"
|
||||||
|
name="totp"
|
||||||
|
autoComplete="off"
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
aria-invalid={messagesPerField.existsError("totp")}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
{messagesPerField.existsError("totp") && (
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<span id="input-error-otp-code" className={cx(kcProps.kcInputErrorMessageClass)} aria-live="polite">
|
||||||
<label htmlFor="userLabel" className={cx(props.kcLabelClass)}>
|
{messagesPerField.get("totp")}
|
||||||
{msg("loginTotpDeviceName")}
|
</span>
|
||||||
</label>{" "}
|
)}
|
||||||
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
|
|
||||||
</div>
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="userLabel"
|
|
||||||
name="userLabel"
|
|
||||||
autoComplete="off"
|
|
||||||
className={cx(props.kcInputClass)}
|
|
||||||
aria-invalid={messagesPerField.existsError("userLabel")}
|
|
||||||
/>
|
|
||||||
{messagesPerField.existsError("userLabel") && (
|
|
||||||
<span id="input-error-otp-label" className={cx(props.kcInputErrorMessageClass)} aria-live="polite">
|
|
||||||
{messagesPerField.get("userLabel")}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<input type="hidden" id="totpSecret" name="totpSecret" value={totp.totpSecret} />
|
||||||
|
{mode && <input type="hidden" id="mode" value={mode} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
{isAppInitiatedAction ? (
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<label htmlFor="userLabel" className={cx(kcProps.kcLabelClass)}>
|
||||||
type="submit"
|
{msg("loginTotpDeviceName")}
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
</label>{" "}
|
||||||
id="saveTOTPBtn"
|
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
|
||||||
value={msgStr("doSubmit")}
|
</div>
|
||||||
/>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<button
|
<input
|
||||||
type="submit"
|
type="text"
|
||||||
className={cx(
|
id="userLabel"
|
||||||
props.kcButtonClass,
|
name="userLabel"
|
||||||
props.kcButtonDefaultClass,
|
autoComplete="off"
|
||||||
props.kcButtonLargeClass,
|
className={cx(kcProps.kcInputClass)}
|
||||||
props.kcButtonLargeClass
|
aria-invalid={messagesPerField.existsError("userLabel")}
|
||||||
)}
|
/>
|
||||||
id="cancelTOTPBtn"
|
{messagesPerField.existsError("userLabel") && (
|
||||||
name="cancel-aia"
|
<span id="input-error-otp-label" className={cx(kcProps.kcInputErrorMessageClass)} aria-live="polite">
|
||||||
value="true"
|
{messagesPerField.get("userLabel")}
|
||||||
>
|
</span>
|
||||||
${msg("doCancel")}
|
)}
|
||||||
</button>
|
</div>
|
||||||
</>
|
</div>
|
||||||
) : (
|
|
||||||
|
{isAppInitiatedAction ? (
|
||||||
|
<>
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
|
||||||
id="saveTOTPBtn"
|
id="saveTOTPBtn"
|
||||||
value={msgStr("doSubmit")}
|
value={msgStr("doSubmit")}
|
||||||
/>
|
/>
|
||||||
)}
|
<button
|
||||||
</form>
|
type="submit"
|
||||||
</>
|
className={cx(
|
||||||
}
|
kcProps.kcButtonClass,
|
||||||
/>
|
kcProps.kcButtonDefaultClass,
|
||||||
);
|
kcProps.kcButtonLargeClass,
|
||||||
}
|
kcProps.kcButtonLargeClass
|
||||||
);
|
)}
|
||||||
|
id="cancelTOTPBtn"
|
||||||
|
name="cancel-aia"
|
||||||
|
value="true"
|
||||||
|
>
|
||||||
|
${msg("doCancel")}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
|
||||||
|
id="saveTOTPBtn"
|
||||||
|
value={msgStr("doSubmit")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default LoginConfigTotp;
|
export default LoginConfigTotp;
|
||||||
|
@ -1,54 +1,67 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginIdpLinkConfirm = memo(
|
export type LoginIdpLinkConfirmProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LoginIdpLinkConfirm;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LoginIdpLinkConfirm; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { url, idpAlias } = kcContext;
|
|
||||||
|
|
||||||
const { msg } = i18n;
|
const LoginIdpLinkConfirm = memo((props: LoginIdpLinkConfirmProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { url, idpAlias } = kcContext;
|
||||||
|
|
||||||
return (
|
const { msg } = i18n;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
const { cx } = useCssAndCx();
|
||||||
headerNode={msg("confirmLinkIdpTitle")}
|
|
||||||
formNode={
|
return (
|
||||||
<form id="kc-register-form" action={url.loginAction} method="post">
|
<Template
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<button
|
headerNode={msg("confirmLinkIdpTitle")}
|
||||||
type="submit"
|
formNode={
|
||||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
<form id="kc-register-form" action={url.loginAction} method="post">
|
||||||
name="submitAction"
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
id="updateProfile"
|
<button
|
||||||
value="updateProfile"
|
type="submit"
|
||||||
>
|
className={cx(
|
||||||
{msg("confirmLinkIdpReviewProfile")}
|
kcProps.kcButtonClass,
|
||||||
</button>
|
kcProps.kcButtonDefaultClass,
|
||||||
<button
|
kcProps.kcButtonBlockClass,
|
||||||
type="submit"
|
kcProps.kcButtonLargeClass
|
||||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
)}
|
||||||
name="submitAction"
|
name="submitAction"
|
||||||
id="linkAccount"
|
id="updateProfile"
|
||||||
value="linkAccount"
|
value="updateProfile"
|
||||||
>
|
>
|
||||||
{msg("confirmLinkIdpContinue", idpAlias)}
|
{msg("confirmLinkIdpReviewProfile")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
<button
|
||||||
</form>
|
type="submit"
|
||||||
}
|
className={cx(
|
||||||
/>
|
kcProps.kcButtonClass,
|
||||||
);
|
kcProps.kcButtonDefaultClass,
|
||||||
}
|
kcProps.kcButtonBlockClass,
|
||||||
);
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
name="submitAction"
|
||||||
|
id="linkAccount"
|
||||||
|
value="linkAccount"
|
||||||
|
>
|
||||||
|
{msg("confirmLinkIdpContinue", idpAlias)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default LoginIdpLinkConfirm;
|
export default LoginIdpLinkConfirm;
|
||||||
|
@ -1,40 +1,43 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginIdpLinkEmail = memo(
|
export type LoginIdpLinkEmailProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LoginIdpLinkEmail;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LoginIdpLinkEmail; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { url, realm, brokerContext, idpAlias } = kcContext;
|
|
||||||
|
|
||||||
const { msg } = i18n;
|
const LoginIdpLinkEmail = memo((props: LoginIdpLinkEmailProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
return (
|
const { url, realm, brokerContext, idpAlias } = kcContext;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
const { msg } = i18n;
|
||||||
headerNode={msg("emailLinkIdpTitle", idpAlias)}
|
|
||||||
formNode={
|
return (
|
||||||
<>
|
<Template
|
||||||
<p id="instruction1" className="instruction">
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
{msg("emailLinkIdp1", idpAlias, brokerContext.username, realm.displayName)}
|
headerNode={msg("emailLinkIdpTitle", idpAlias)}
|
||||||
</p>
|
formNode={
|
||||||
<p id="instruction2" className="instruction">
|
<>
|
||||||
{msg("emailLinkIdp2")} <a href={url.loginAction}>{msg("doClickHere")}</a> {msg("emailLinkIdp3")}
|
<p id="instruction1" className="instruction">
|
||||||
</p>
|
{msg("emailLinkIdp1", idpAlias, brokerContext.username, realm.displayName)}
|
||||||
<p id="instruction3" className="instruction">
|
</p>
|
||||||
{msg("emailLinkIdp4")} <a href={url.loginAction}>{msg("doClickHere")}</a> {msg("emailLinkIdp5")}
|
<p id="instruction2" className="instruction">
|
||||||
</p>
|
{msg("emailLinkIdp2")} <a href={url.loginAction}>{msg("doClickHere")}</a> {msg("emailLinkIdp3")}
|
||||||
</>
|
</p>
|
||||||
}
|
<p id="instruction3" className="instruction">
|
||||||
/>
|
{msg("emailLinkIdp4")} <a href={url.loginAction}>{msg("doClickHere")}</a> {msg("emailLinkIdp5")}
|
||||||
);
|
</p>
|
||||||
}
|
</>
|
||||||
);
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default LoginIdpLinkEmail;
|
export default LoginIdpLinkEmail;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, memo } from "react";
|
import React, { useEffect, memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { headInsert } from "../tools/headInsert";
|
import { headInsert } from "../tools/headInsert";
|
||||||
@ -7,95 +8,97 @@ import { pathJoin } from "../../bin/tools/pathJoin";
|
|||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginOtp = memo(
|
export type LoginOtpProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LoginOtp;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LoginOtp; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { otpLogin, url } = kcContext;
|
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const LoginOtp = memo((props: LoginOtpProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { otpLogin, url } = kcContext;
|
||||||
|
|
||||||
useEffect(() => {
|
const { cx } = useCssAndCx();
|
||||||
let isCleanedUp = false;
|
|
||||||
|
|
||||||
headInsert({
|
const { msg, msgStr } = i18n;
|
||||||
"type": "javascript",
|
|
||||||
"src": pathJoin(kcContext.url.resourcesCommonPath, "node_modules/jquery/dist/jquery.min.js")
|
|
||||||
}).then(() => {
|
|
||||||
if (isCleanedUp) return;
|
|
||||||
|
|
||||||
evaluateInlineScript();
|
useEffect(() => {
|
||||||
});
|
let isCleanedUp = false;
|
||||||
|
|
||||||
return () => {
|
headInsert({
|
||||||
isCleanedUp = true;
|
"type": "javascript",
|
||||||
};
|
"src": pathJoin(kcContext.url.resourcesCommonPath, "node_modules/jquery/dist/jquery.min.js")
|
||||||
}, []);
|
}).then(() => {
|
||||||
|
if (isCleanedUp) return;
|
||||||
|
|
||||||
return (
|
evaluateInlineScript();
|
||||||
<Template
|
});
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
|
||||||
headerNode={msg("doLogIn")}
|
return () => {
|
||||||
formNode={
|
isCleanedUp = true;
|
||||||
<form id="kc-otp-login-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
};
|
||||||
{otpLogin.userOtpCredentials.length > 1 && (
|
}, []);
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
return (
|
||||||
{otpLogin.userOtpCredentials.map(otpCredential => (
|
<Template
|
||||||
<div key={otpCredential.id} className={cx(props.kcSelectOTPListClass)}>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<input type="hidden" value="${otpCredential.id}" />
|
headerNode={msg("doLogIn")}
|
||||||
<div className={cx(props.kcSelectOTPListItemClass)}>
|
formNode={
|
||||||
<span className={cx(props.kcAuthenticatorOtpCircleClass)} />
|
<form id="kc-otp-login-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
<h2 className={cx(props.kcSelectOTPItemHeadingClass)}>{otpCredential.userLabel}</h2>
|
{otpLogin.userOtpCredentials.length > 1 && (
|
||||||
</div>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
|
{otpLogin.userOtpCredentials.map(otpCredential => (
|
||||||
|
<div key={otpCredential.id} className={cx(kcProps.kcSelectOTPListClass)}>
|
||||||
|
<input type="hidden" value="${otpCredential.id}" />
|
||||||
|
<div className={cx(kcProps.kcSelectOTPListItemClass)}>
|
||||||
|
<span className={cx(kcProps.kcAuthenticatorOtpCircleClass)} />
|
||||||
|
<h2 className={cx(kcProps.kcSelectOTPItemHeadingClass)}>{otpCredential.userLabel}</h2>
|
||||||
</div>
|
</div>
|
||||||
))}
|
</div>
|
||||||
</div>
|
))}
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
|
||||||
<label htmlFor="otp" className={cx(props.kcLabelClass)}>
|
|
||||||
{msg("loginOtpOneTime")}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
<input id="otp" name="otp" autoComplete="off" type="text" className={cx(props.kcInputClass)} autoFocus />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)} />
|
<label htmlFor="otp" className={cx(kcProps.kcLabelClass)}>
|
||||||
</div>
|
{msg("loginOtpOneTime")}
|
||||||
|
</label>
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
|
||||||
<input
|
|
||||||
className={cx(
|
|
||||||
props.kcButtonClass,
|
|
||||||
props.kcButtonPrimaryClass,
|
|
||||||
props.kcButtonBlockClass,
|
|
||||||
props.kcButtonLargeClass
|
|
||||||
)}
|
|
||||||
name="login"
|
|
||||||
id="kc-login"
|
|
||||||
type="submit"
|
|
||||||
value={msgStr("doLogIn")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
}
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
/>
|
<input id="otp" name="otp" autoComplete="off" type="text" className={cx(kcProps.kcInputClass)} autoFocus />
|
||||||
);
|
</div>
|
||||||
}
|
</div>
|
||||||
);
|
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
|
<input
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
name="login"
|
||||||
|
id="kc-login"
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doLogIn")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
declare const $: any;
|
declare const $: any;
|
||||||
|
|
||||||
|
@ -1,44 +1,47 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginPageExpired = memo(
|
export type LoginPageExpired = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LoginPageExpired;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LoginPageExpired; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { url } = kcContext;
|
|
||||||
|
|
||||||
const { msg } = i18n;
|
const LoginPageExpired = memo((props: LoginPageExpired) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
return (
|
const { url } = kcContext;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
const { msg } = i18n;
|
||||||
displayMessage={false}
|
|
||||||
headerNode={msg("pageExpiredTitle")}
|
return (
|
||||||
formNode={
|
<Template
|
||||||
<>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<p id="instruction1" className="instruction">
|
displayMessage={false}
|
||||||
{msg("pageExpiredMsg1")}
|
headerNode={msg("pageExpiredTitle")}
|
||||||
<a id="loginRestartLink" href={url.loginRestartFlowUrl}>
|
formNode={
|
||||||
{msg("doClickHere")}
|
<>
|
||||||
</a>{" "}
|
<p id="instruction1" className="instruction">
|
||||||
.<br />
|
{msg("pageExpiredMsg1")}
|
||||||
{msg("pageExpiredMsg2")}{" "}
|
<a id="loginRestartLink" href={url.loginRestartFlowUrl}>
|
||||||
<a id="loginContinueLink" href={url.loginAction}>
|
{msg("doClickHere")}
|
||||||
{msg("doClickHere")}
|
</a>{" "}
|
||||||
</a>{" "}
|
.<br />
|
||||||
.
|
{msg("pageExpiredMsg2")}{" "}
|
||||||
</p>
|
<a id="loginContinueLink" href={url.loginAction}>
|
||||||
</>
|
{msg("doClickHere")}
|
||||||
}
|
</a>{" "}
|
||||||
/>
|
.
|
||||||
);
|
</p>
|
||||||
}
|
</>
|
||||||
);
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default LoginPageExpired;
|
export default LoginPageExpired;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState, memo } from "react";
|
import React, { useState, memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
@ -7,90 +8,92 @@ import { useConstCallback } from "powerhooks/useConstCallback";
|
|||||||
import type { FormEventHandler } from "react";
|
import type { FormEventHandler } from "react";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginPassword = memo(
|
export type LoginPasswordProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LoginPassword;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LoginPassword; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { realm, url, login } = kcContext;
|
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const LoginPassword = memo((props: LoginPasswordProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { realm, url, login } = kcContext;
|
||||||
|
|
||||||
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
|
const { cx } = useCssAndCx();
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
setIsLoginButtonDisabled(true);
|
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
|
||||||
|
|
||||||
const formElement = e.target as HTMLFormElement;
|
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
formElement.submit();
|
setIsLoginButtonDisabled(true);
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
const formElement = e.target as HTMLFormElement;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
formElement.submit();
|
||||||
headerNode={msg("doLogIn")}
|
});
|
||||||
formNode={
|
|
||||||
<div id="kc-form">
|
return (
|
||||||
<div id="kc-form-wrapper">
|
<Template
|
||||||
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
headerNode={msg("doLogIn")}
|
||||||
<hr />
|
formNode={
|
||||||
<label htmlFor="password" className={cx(props.kcLabelClass)}>
|
<div id="kc-form">
|
||||||
{msg("password")}
|
<div id="kc-form-wrapper">
|
||||||
</label>
|
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
||||||
<input
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
tabIndex={2}
|
<hr />
|
||||||
id="password"
|
<label htmlFor="password" className={cx(kcProps.kcLabelClass)}>
|
||||||
className={cx(props.kcInputClass)}
|
{msg("password")}
|
||||||
name="password"
|
</label>
|
||||||
type="password"
|
<input
|
||||||
autoFocus={true}
|
tabIndex={2}
|
||||||
autoComplete="on"
|
id="password"
|
||||||
defaultValue={login.password ?? ""}
|
className={cx(kcProps.kcInputClass)}
|
||||||
/>
|
name="password"
|
||||||
|
type="password"
|
||||||
|
autoFocus={true}
|
||||||
|
autoComplete="on"
|
||||||
|
defaultValue={login.password ?? ""}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
|
||||||
|
<div id="kc-form-options" />
|
||||||
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
|
{realm.resetPasswordAllowed && (
|
||||||
|
<span>
|
||||||
|
<a tabIndex={5} href={url.loginResetCredentialsUrl}>
|
||||||
|
{msg("doForgotPassword")}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcFormGroupClass, props.kcFormSettingClass)}>
|
</div>
|
||||||
<div id="kc-form-options" />
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
<input
|
||||||
{realm.resetPasswordAllowed && (
|
tabIndex={4}
|
||||||
<span>
|
className={cx(
|
||||||
<a tabIndex={5} href={url.loginResetCredentialsUrl}>
|
kcProps.kcButtonClass,
|
||||||
{msg("doForgotPassword")}
|
kcProps.kcButtonPrimaryClass,
|
||||||
</a>
|
kcProps.kcButtonBlockClass,
|
||||||
</span>
|
kcProps.kcButtonLargeClass
|
||||||
)}
|
)}
|
||||||
</div>
|
name="login"
|
||||||
</div>
|
id="kc-login"
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormGroupClass)}>
|
type="submit"
|
||||||
<input
|
value={msgStr("doLogIn")}
|
||||||
tabIndex={4}
|
disabled={isLoginButtonDisabled}
|
||||||
className={cx(
|
/>
|
||||||
props.kcButtonClass,
|
</div>
|
||||||
props.kcButtonPrimaryClass,
|
</form>
|
||||||
props.kcButtonBlockClass,
|
|
||||||
props.kcButtonLargeClass
|
|
||||||
)}
|
|
||||||
name="login"
|
|
||||||
id="kc-login"
|
|
||||||
type="submit"
|
|
||||||
value={msgStr("doLogIn")}
|
|
||||||
disabled={isLoginButtonDisabled}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
/>
|
}
|
||||||
);
|
/>
|
||||||
}
|
);
|
||||||
);
|
});
|
||||||
|
|
||||||
export default LoginPassword;
|
export default LoginPassword;
|
||||||
|
@ -1,79 +1,82 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginResetPassword = memo(
|
export type LoginResetPasswordProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LoginResetPassword;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LoginResetPassword; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { url, realm, auth } = kcContext;
|
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const LoginResetPassword = memo((props: LoginResetPasswordProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { url, realm, auth } = kcContext;
|
||||||
|
|
||||||
return (
|
const { msg, msgStr } = i18n;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
const { cx } = useCssAndCx();
|
||||||
displayMessage={false}
|
|
||||||
headerNode={msg("emailForgotTitle")}
|
return (
|
||||||
formNode={
|
<Template
|
||||||
<form id="kc-reset-password-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
displayMessage={false}
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
headerNode={msg("emailForgotTitle")}
|
||||||
<label htmlFor="username" className={cx(props.kcLabelClass)}>
|
formNode={
|
||||||
{!realm.loginWithEmailAllowed
|
<form id="kc-reset-password-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
? msg("username")
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
: !realm.registrationEmailAsUsername
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
? msg("usernameOrEmail")
|
<label htmlFor="username" className={cx(kcProps.kcLabelClass)}>
|
||||||
: msg("email")}
|
{!realm.loginWithEmailAllowed
|
||||||
</label>
|
? msg("username")
|
||||||
</div>
|
: !realm.registrationEmailAsUsername
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
? msg("usernameOrEmail")
|
||||||
<input
|
: msg("email")}
|
||||||
type="text"
|
</label>
|
||||||
id="username"
|
</div>
|
||||||
name="username"
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
className={cx(props.kcInputClass)}
|
<input
|
||||||
autoFocus
|
type="text"
|
||||||
defaultValue={auth !== undefined && auth.showUsername ? auth.attemptedUsername : undefined}
|
id="username"
|
||||||
/>
|
name="username"
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
autoFocus
|
||||||
|
defaultValue={auth !== undefined && auth.showUsername ? auth.attemptedUsername : undefined}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
|
||||||
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
|
<span>
|
||||||
|
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcFormGroupClass, props.kcFormSettingClass)}>
|
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
|
||||||
<span>
|
|
||||||
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
<input
|
<input
|
||||||
className={cx(
|
className={cx(
|
||||||
props.kcButtonClass,
|
kcProps.kcButtonClass,
|
||||||
props.kcButtonPrimaryClass,
|
kcProps.kcButtonPrimaryClass,
|
||||||
props.kcButtonBlockClass,
|
kcProps.kcButtonBlockClass,
|
||||||
props.kcButtonLargeClass
|
kcProps.kcButtonLargeClass
|
||||||
)}
|
)}
|
||||||
type="submit"
|
type="submit"
|
||||||
value={msgStr("doSubmit")}
|
value={msgStr("doSubmit")}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
}
|
</form>
|
||||||
infoNode={msg("emailInstruction")}
|
}
|
||||||
/>
|
infoNode={msg("emailInstruction")}
|
||||||
);
|
/>
|
||||||
}
|
);
|
||||||
);
|
});
|
||||||
|
|
||||||
export default LoginResetPassword;
|
export default LoginResetPassword;
|
||||||
|
@ -1,125 +1,128 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginUpdatePassword = memo(
|
export type LoginUpdatePasswordProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LoginUpdatePassword;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LoginUpdatePassword; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { cx } = useCssAndCx();
|
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const LoginUpdatePassword = memo((props: LoginUpdatePasswordProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { url, messagesPerField, isAppInitiatedAction, username } = kcContext;
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
return (
|
const { msg, msgStr } = i18n;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
|
||||||
headerNode={msg("updatePasswordTitle")}
|
|
||||||
formNode={
|
|
||||||
<form id="kc-passwd-update-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="username"
|
|
||||||
name="username"
|
|
||||||
value={username}
|
|
||||||
readOnly={true}
|
|
||||||
autoComplete="username"
|
|
||||||
style={{ display: "none" }}
|
|
||||||
/>
|
|
||||||
<input type="password" id="password" name="password" autoComplete="current-password" style={{ display: "none" }} />
|
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("password", props.kcFormGroupErrorClass))}>
|
const { url, messagesPerField, isAppInitiatedAction, username } = kcContext;
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
|
||||||
<label htmlFor="password-new" className={cx(props.kcLabelClass)}>
|
return (
|
||||||
{msg("passwordNew")}
|
<Template
|
||||||
</label>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
</div>
|
headerNode={msg("updatePasswordTitle")}
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
formNode={
|
||||||
<input
|
<form id="kc-passwd-update-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
type="password"
|
<input
|
||||||
id="password-new"
|
type="text"
|
||||||
name="password-new"
|
id="username"
|
||||||
autoFocus
|
name="username"
|
||||||
autoComplete="new-password"
|
value={username}
|
||||||
className={cx(props.kcInputClass)}
|
readOnly={true}
|
||||||
/>
|
autoComplete="username"
|
||||||
</div>
|
style={{ display: "none" }}
|
||||||
|
/>
|
||||||
|
<input type="password" id="password" name="password" autoComplete="current-password" style={{ display: "none" }} />
|
||||||
|
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("password", kcProps.kcFormGroupErrorClass))}>
|
||||||
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor="password-new" className={cx(kcProps.kcLabelClass)}>
|
||||||
|
{msg("passwordNew")}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("password-confirm", props.kcFormGroupErrorClass))}>
|
<input
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
type="password"
|
||||||
<label htmlFor="password-confirm" className={cx(props.kcLabelClass)}>
|
id="password-new"
|
||||||
{msg("passwordConfirm")}
|
name="password-new"
|
||||||
</label>
|
autoFocus
|
||||||
</div>
|
autoComplete="new-password"
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
className={cx(kcProps.kcInputClass)}
|
||||||
<input
|
/>
|
||||||
type="password"
|
|
||||||
id="password-confirm"
|
|
||||||
name="password-confirm"
|
|
||||||
autoComplete="new-password"
|
|
||||||
className={cx(props.kcInputClass)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("password-confirm", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
<label htmlFor="password-confirm" className={cx(kcProps.kcLabelClass)}>
|
||||||
{isAppInitiatedAction && (
|
{msg("passwordConfirm")}
|
||||||
<div className="checkbox">
|
</label>
|
||||||
<label>
|
</div>
|
||||||
<input type="checkbox" id="logout-sessions" name="logout-sessions" value="on" checked />
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
{msgStr("logoutOtherSessions")}
|
<input
|
||||||
</label>
|
type="password"
|
||||||
</div>
|
id="password-confirm"
|
||||||
)}
|
name="password-confirm"
|
||||||
</div>
|
autoComplete="new-password"
|
||||||
</div>
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
{isAppInitiatedAction ? (
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
<>
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
<input
|
{isAppInitiatedAction && (
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
<div className="checkbox">
|
||||||
type="submit"
|
<label>
|
||||||
defaultValue={msgStr("doSubmit")}
|
<input type="checkbox" id="logout-sessions" name="logout-sessions" value="on" checked />
|
||||||
/>
|
{msgStr("logoutOtherSessions")}
|
||||||
<button
|
</label>
|
||||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
|
</div>
|
||||||
type="submit"
|
|
||||||
name="cancel-aia"
|
|
||||||
value="true"
|
|
||||||
>
|
|
||||||
{msg("doCancel")}
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<input
|
|
||||||
className={cx(
|
|
||||||
props.kcButtonClass,
|
|
||||||
props.kcButtonPrimaryClass,
|
|
||||||
props.kcButtonBlockClass,
|
|
||||||
props.kcButtonLargeClass
|
|
||||||
)}
|
|
||||||
type="submit"
|
|
||||||
defaultValue={msgStr("doSubmit")}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
}
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
/>
|
{isAppInitiatedAction ? (
|
||||||
);
|
<>
|
||||||
}
|
<input
|
||||||
);
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
|
||||||
|
type="submit"
|
||||||
|
defaultValue={msgStr("doSubmit")}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
|
||||||
|
type="submit"
|
||||||
|
name="cancel-aia"
|
||||||
|
value="true"
|
||||||
|
>
|
||||||
|
{msg("doCancel")}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
type="submit"
|
||||||
|
defaultValue={msgStr("doSubmit")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default LoginUpdatePassword;
|
export default LoginUpdatePassword;
|
||||||
|
@ -1,134 +1,137 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginUpdateProfile = memo(
|
export type LoginUpdateProfile = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LoginUpdateProfile;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LoginUpdateProfile; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { cx } = useCssAndCx();
|
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const LoginUpdateProfile = memo((props: LoginUpdateProfile) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { url, user, messagesPerField, isAppInitiatedAction } = kcContext;
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
return (
|
const { msg, msgStr } = i18n;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
|
||||||
headerNode={msg("loginProfileTitle")}
|
|
||||||
formNode={
|
|
||||||
<form id="kc-update-profile-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
|
||||||
{user.editUsernameAllowed && (
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("username", props.kcFormGroupErrorClass))}>
|
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
|
||||||
<label htmlFor="username" className={cx(props.kcLabelClass)}>
|
|
||||||
{msg("username")}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="username"
|
|
||||||
name="username"
|
|
||||||
defaultValue={user.username ?? ""}
|
|
||||||
className={cx(props.kcInputClass)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("email", props.kcFormGroupErrorClass))}>
|
const { url, user, messagesPerField, isAppInitiatedAction } = kcContext;
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
|
||||||
<label htmlFor="email" className={cx(props.kcLabelClass)}>
|
return (
|
||||||
{msg("email")}
|
<Template
|
||||||
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
|
headerNode={msg("loginProfileTitle")}
|
||||||
|
formNode={
|
||||||
|
<form id="kc-update-profile-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
|
{user.editUsernameAllowed && (
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("username", kcProps.kcFormGroupErrorClass))}>
|
||||||
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor="username" className={cx(kcProps.kcLabelClass)}>
|
||||||
|
{msg("username")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input type="text" id="email" name="email" defaultValue={user.email ?? ""} className={cx(props.kcInputClass)} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("firstName", props.kcFormGroupErrorClass))}>
|
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
|
||||||
<label htmlFor="firstName" className={cx(props.kcLabelClass)}>
|
|
||||||
{msg("firstName")}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="firstName"
|
id="username"
|
||||||
name="firstName"
|
name="username"
|
||||||
defaultValue={user.firstName ?? ""}
|
defaultValue={user.username ?? ""}
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("lastName", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("email", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="lastName" className={cx(props.kcLabelClass)}>
|
<label htmlFor="email" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("lastName")}
|
{msg("email")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input type="text" id="email" name="email" defaultValue={user.email ?? ""} className={cx(kcProps.kcInputClass)} />
|
||||||
type="text"
|
</div>
|
||||||
id="lastName"
|
</div>
|
||||||
name="lastName"
|
|
||||||
defaultValue={user.lastName ?? ""}
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("firstName", kcProps.kcFormGroupErrorClass))}>
|
||||||
className={cx(props.kcInputClass)}
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
/>
|
<label htmlFor="firstName" className={cx(kcProps.kcLabelClass)}>
|
||||||
</div>
|
{msg("firstName")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="firstName"
|
||||||
|
name="firstName"
|
||||||
|
defaultValue={user.firstName ?? ""}
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("lastName", kcProps.kcFormGroupErrorClass))}>
|
||||||
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor="lastName" className={cx(kcProps.kcLabelClass)}>
|
||||||
|
{msg("lastName")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="lastName"
|
||||||
|
name="lastName"
|
||||||
|
defaultValue={user.lastName ?? ""}
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
{isAppInitiatedAction ? (
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)} />
|
<>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
|
||||||
{isAppInitiatedAction ? (
|
|
||||||
<>
|
|
||||||
<input
|
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
|
||||||
type="submit"
|
|
||||||
defaultValue={msgStr("doSubmit")}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
|
|
||||||
type="submit"
|
|
||||||
name="cancel-aia"
|
|
||||||
value="true"
|
|
||||||
>
|
|
||||||
{msg("doCancel")}
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<input
|
<input
|
||||||
className={cx(
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
|
||||||
props.kcButtonClass,
|
|
||||||
props.kcButtonPrimaryClass,
|
|
||||||
props.kcButtonBlockClass,
|
|
||||||
props.kcButtonLargeClass
|
|
||||||
)}
|
|
||||||
type="submit"
|
type="submit"
|
||||||
defaultValue={msgStr("doSubmit")}
|
defaultValue={msgStr("doSubmit")}
|
||||||
/>
|
/>
|
||||||
)}
|
<button
|
||||||
</div>
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
|
||||||
|
type="submit"
|
||||||
|
name="cancel-aia"
|
||||||
|
value="true"
|
||||||
|
>
|
||||||
|
{msg("doCancel")}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
type="submit"
|
||||||
|
defaultValue={msgStr("doSubmit")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
}
|
</form>
|
||||||
/>
|
}
|
||||||
);
|
/>
|
||||||
}
|
);
|
||||||
);
|
});
|
||||||
|
|
||||||
export default LoginUpdateProfile;
|
export default LoginUpdateProfile;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState, memo } from "react";
|
import React, { useState, memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
@ -7,162 +8,164 @@ import { useConstCallback } from "powerhooks/useConstCallback";
|
|||||||
import type { FormEventHandler } from "react";
|
import type { FormEventHandler } from "react";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginUsername = memo(
|
export type LoginUsernameProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LoginUsername;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LoginUsername; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { social, realm, url, usernameHidden, login, registrationDisabled } = kcContext;
|
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const LoginUsername = memo((props: LoginUsernameProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { social, realm, url, usernameHidden, login, registrationDisabled } = kcContext;
|
||||||
|
|
||||||
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
|
const { cx } = useCssAndCx();
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
setIsLoginButtonDisabled(true);
|
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
|
||||||
|
|
||||||
const formElement = e.target as HTMLFormElement;
|
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
//NOTE: Even if we login with email Keycloak expect username and password in
|
setIsLoginButtonDisabled(true);
|
||||||
//the POST request.
|
|
||||||
formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
|
|
||||||
|
|
||||||
formElement.submit();
|
const formElement = e.target as HTMLFormElement;
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
//NOTE: Even if we login with email Keycloak expect username and password in
|
||||||
<Template
|
//the POST request.
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
|
||||||
displayInfo={social.displayInfo}
|
|
||||||
displayWide={realm.password && social.providers !== undefined}
|
|
||||||
headerNode={msg("doLogIn")}
|
|
||||||
formNode={
|
|
||||||
<div id="kc-form" className={cx(realm.password && social.providers !== undefined && props.kcContentWrapperClass)}>
|
|
||||||
<div
|
|
||||||
id="kc-form-wrapper"
|
|
||||||
className={cx(
|
|
||||||
realm.password && social.providers && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass]
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{realm.password && (
|
|
||||||
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
|
||||||
{!usernameHidden &&
|
|
||||||
(() => {
|
|
||||||
const label = !realm.loginWithEmailAllowed
|
|
||||||
? "username"
|
|
||||||
: realm.registrationEmailAsUsername
|
|
||||||
? "email"
|
|
||||||
: "usernameOrEmail";
|
|
||||||
|
|
||||||
const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
|
formElement.submit();
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Template
|
||||||
<label htmlFor={autoCompleteHelper} className={cx(props.kcLabelClass)}>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
{msg(label)}
|
displayInfo={social.displayInfo}
|
||||||
</label>
|
displayWide={realm.password && social.providers !== undefined}
|
||||||
<input
|
headerNode={msg("doLogIn")}
|
||||||
tabIndex={1}
|
formNode={
|
||||||
id={autoCompleteHelper}
|
<div id="kc-form" className={cx(realm.password && social.providers !== undefined && kcProps.kcContentWrapperClass)}>
|
||||||
className={cx(props.kcInputClass)}
|
<div
|
||||||
//NOTE: This is used by Google Chrome auto fill so we use it to tell
|
id="kc-form-wrapper"
|
||||||
//the browser how to pre fill the form but before submit we put it back
|
className={cx(
|
||||||
//to username because it is what keycloak expects.
|
realm.password && social.providers && [kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass]
|
||||||
name={autoCompleteHelper}
|
)}
|
||||||
defaultValue={login.username ?? ""}
|
>
|
||||||
type="text"
|
{realm.password && (
|
||||||
autoFocus={true}
|
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
||||||
autoComplete="off"
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
/>
|
{!usernameHidden &&
|
||||||
</>
|
(() => {
|
||||||
);
|
const label = !realm.loginWithEmailAllowed
|
||||||
})()}
|
? "username"
|
||||||
</div>
|
: realm.registrationEmailAsUsername
|
||||||
<div className={cx(props.kcFormGroupClass, props.kcFormSettingClass)}>
|
? "email"
|
||||||
<div id="kc-form-options">
|
: "usernameOrEmail";
|
||||||
{realm.rememberMe && !usernameHidden && (
|
|
||||||
<div className="checkbox">
|
const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
|
||||||
<label>
|
|
||||||
<input
|
return (
|
||||||
tabIndex={3}
|
<>
|
||||||
id="rememberMe"
|
<label htmlFor={autoCompleteHelper} className={cx(kcProps.kcLabelClass)}>
|
||||||
name="rememberMe"
|
{msg(label)}
|
||||||
type="checkbox"
|
|
||||||
{...(login.rememberMe
|
|
||||||
? {
|
|
||||||
"checked": true
|
|
||||||
}
|
|
||||||
: {})}
|
|
||||||
/>
|
|
||||||
{msg("rememberMe")}
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
<input
|
||||||
)}
|
tabIndex={1}
|
||||||
</div>
|
id={autoCompleteHelper}
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
//NOTE: This is used by Google Chrome auto fill so we use it to tell
|
||||||
|
//the browser how to pre fill the form but before submit we put it back
|
||||||
|
//to username because it is what keycloak expects.
|
||||||
|
name={autoCompleteHelper}
|
||||||
|
defaultValue={login.username ?? ""}
|
||||||
|
type="text"
|
||||||
|
autoFocus={true}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
|
||||||
|
<div id="kc-form-options">
|
||||||
|
{realm.rememberMe && !usernameHidden && (
|
||||||
|
<div className="checkbox">
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
tabIndex={3}
|
||||||
|
id="rememberMe"
|
||||||
|
name="rememberMe"
|
||||||
|
type="checkbox"
|
||||||
|
{...(login.rememberMe
|
||||||
|
? {
|
||||||
|
"checked": true
|
||||||
|
}
|
||||||
|
: {})}
|
||||||
|
/>
|
||||||
|
{msg("rememberMe")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormGroupClass)}>
|
</div>
|
||||||
<input
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
|
||||||
tabIndex={4}
|
<input
|
||||||
className={cx(
|
tabIndex={4}
|
||||||
props.kcButtonClass,
|
className={cx(
|
||||||
props.kcButtonPrimaryClass,
|
kcProps.kcButtonClass,
|
||||||
props.kcButtonBlockClass,
|
kcProps.kcButtonPrimaryClass,
|
||||||
props.kcButtonLargeClass
|
kcProps.kcButtonBlockClass,
|
||||||
)}
|
kcProps.kcButtonLargeClass
|
||||||
name="login"
|
)}
|
||||||
id="kc-login"
|
name="login"
|
||||||
type="submit"
|
id="kc-login"
|
||||||
value={msgStr("doLogIn")}
|
type="submit"
|
||||||
disabled={isLoginButtonDisabled}
|
value={msgStr("doLogIn")}
|
||||||
/>
|
disabled={isLoginButtonDisabled}
|
||||||
</div>
|
/>
|
||||||
</form>
|
</div>
|
||||||
)}
|
</form>
|
||||||
</div>
|
|
||||||
{realm.password && social.providers !== undefined && (
|
|
||||||
<div id="kc-social-providers" className={cx(props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass)}>
|
|
||||||
<ul
|
|
||||||
className={cx(
|
|
||||||
props.kcFormSocialAccountListClass,
|
|
||||||
social.providers.length > 4 && props.kcFormSocialAccountDoubleListClass
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{social.providers.map(p => (
|
|
||||||
<li key={p.providerId} className={cx(props.kcFormSocialAccountListLinkClass)}>
|
|
||||||
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
|
|
||||||
<span>{p.displayName}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
{realm.password && social.providers !== undefined && (
|
||||||
infoNode={
|
<div id="kc-social-providers" className={cx(kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass)}>
|
||||||
realm.password &&
|
<ul
|
||||||
realm.registrationAllowed &&
|
className={cx(
|
||||||
!registrationDisabled && (
|
kcProps.kcFormSocialAccountListClass,
|
||||||
<div id="kc-registration">
|
social.providers.length > 4 && kcProps.kcFormSocialAccountDoubleListClass
|
||||||
<span>
|
)}
|
||||||
{msg("noAccount")}
|
>
|
||||||
<a tabIndex={6} href={url.registrationUrl}>
|
{social.providers.map(p => (
|
||||||
{msg("doRegister")}
|
<li key={p.providerId} className={cx(kcProps.kcFormSocialAccountListLinkClass)}>
|
||||||
</a>
|
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
|
||||||
</span>
|
<span>{p.displayName}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
}
|
</div>
|
||||||
/>
|
}
|
||||||
);
|
infoNode={
|
||||||
}
|
realm.password &&
|
||||||
);
|
realm.registrationAllowed &&
|
||||||
|
!registrationDisabled && (
|
||||||
|
<div id="kc-registration">
|
||||||
|
<span>
|
||||||
|
{msg("noAccount")}
|
||||||
|
<a tabIndex={6} href={url.registrationUrl}>
|
||||||
|
{msg("doRegister")}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default LoginUsername;
|
export default LoginUsername;
|
||||||
|
@ -1,40 +1,43 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginVerifyEmail = memo(
|
export type LoginVerifyEmailProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LoginVerifyEmail;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LoginVerifyEmail; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { msg } = i18n;
|
|
||||||
|
|
||||||
const { url, user } = kcContext;
|
const LoginVerifyEmail = memo((props: LoginVerifyEmailProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
return (
|
const { msg } = i18n;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
const { url, user } = kcContext;
|
||||||
displayMessage={false}
|
|
||||||
headerNode={msg("emailVerifyTitle")}
|
return (
|
||||||
formNode={
|
<Template
|
||||||
<>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<p className="instruction">{msg("emailVerifyInstruction1", user?.email)}</p>
|
displayMessage={false}
|
||||||
<p className="instruction">
|
headerNode={msg("emailVerifyTitle")}
|
||||||
{msg("emailVerifyInstruction2")}
|
formNode={
|
||||||
<br />
|
<>
|
||||||
<a href={url.loginAction}>{msg("doClickHere")}</a>
|
<p className="instruction">{msg("emailVerifyInstruction1", user?.email)}</p>
|
||||||
|
<p className="instruction">
|
||||||
{msg("emailVerifyInstruction3")}
|
{msg("emailVerifyInstruction2")}
|
||||||
</p>
|
<br />
|
||||||
</>
|
<a href={url.loginAction}>{msg("doClickHere")}</a>
|
||||||
}
|
|
||||||
/>
|
{msg("emailVerifyInstruction3")}
|
||||||
);
|
</p>
|
||||||
}
|
</>
|
||||||
);
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default LoginVerifyEmail;
|
export default LoginVerifyEmail;
|
||||||
|
@ -1,68 +1,71 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LogoutConfirm = memo(
|
export type LogoutConfirmProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.LogoutConfirm;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.LogoutConfirm; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { url, client, logoutConfirm } = kcContext;
|
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const LogoutConfirm = memo((props: LogoutConfirmProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { url, client, logoutConfirm } = kcContext;
|
||||||
|
|
||||||
return (
|
const { cx } = useCssAndCx();
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
const { msg, msgStr } = i18n;
|
||||||
displayMessage={false}
|
|
||||||
headerNode={msg("logoutConfirmTitle")}
|
return (
|
||||||
formNode={
|
<Template
|
||||||
<>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<div id="kc-logout-confirm" className="content-area">
|
displayMessage={false}
|
||||||
<p className="instruction">{msg("logoutConfirmHeader")}</p>
|
headerNode={msg("logoutConfirmTitle")}
|
||||||
<form className="form-actions" action={url.logoutConfirmAction} method="POST">
|
formNode={
|
||||||
<input type="hidden" name="session_code" value={logoutConfirm.code} />
|
<>
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div id="kc-logout-confirm" className="content-area">
|
||||||
<div id="kc-form-options">
|
<p className="instruction">{msg("logoutConfirmHeader")}</p>
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}></div>
|
<form className="form-actions" action={url.logoutConfirmAction} method="POST">
|
||||||
</div>
|
<input type="hidden" name="session_code" value={logoutConfirm.code} />
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<input
|
<div id="kc-form-options">
|
||||||
tabIndex={4}
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}></div>
|
||||||
className={cx(
|
</div>
|
||||||
props.kcButtonClass,
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
|
||||||
props.kcButtonPrimaryClass,
|
<input
|
||||||
props.kcButtonBlockClass,
|
tabIndex={4}
|
||||||
props.kcButtonLargeClass
|
className={cx(
|
||||||
)}
|
kcProps.kcButtonClass,
|
||||||
name="confirmLogout"
|
kcProps.kcButtonPrimaryClass,
|
||||||
id="kc-logout"
|
kcProps.kcButtonBlockClass,
|
||||||
type="submit"
|
kcProps.kcButtonLargeClass
|
||||||
value={msgStr("doLogout")}
|
)}
|
||||||
/>
|
name="confirmLogout"
|
||||||
</div>
|
id="kc-logout"
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doLogout")}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
<div id="kc-info-message">
|
|
||||||
{!logoutConfirm.skipLink && client.baseUrl && (
|
|
||||||
<p>
|
|
||||||
<a href={client.baseUrl} dangerouslySetInnerHTML={{ __html: msgStr("backToApplication") }} />
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
|
<div id="kc-info-message">
|
||||||
|
{!logoutConfirm.skipLink && client.baseUrl && (
|
||||||
|
<p>
|
||||||
|
<a href={client.baseUrl} dangerouslySetInnerHTML={{ __html: msgStr("backToApplication") }} />
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
}
|
</>
|
||||||
/>
|
}
|
||||||
);
|
/>
|
||||||
}
|
);
|
||||||
);
|
});
|
||||||
|
|
||||||
export default LogoutConfirm;
|
export default LogoutConfirm;
|
||||||
|
@ -1,169 +1,172 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Register = memo(
|
export type RegisterProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.Register;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.Register; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const Register = memo((props: RegisterProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
||||||
|
|
||||||
return (
|
const { msg, msgStr } = i18n;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
const { cx } = useCssAndCx();
|
||||||
headerNode={msg("registerTitle")}
|
|
||||||
formNode={
|
return (
|
||||||
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
<Template
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("firstName", props.kcFormGroupErrorClass))}>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
headerNode={msg("registerTitle")}
|
||||||
<label htmlFor="firstName" className={cx(props.kcLabelClass)}>
|
formNode={
|
||||||
{msg("firstName")}
|
<form id="kc-register-form" className={cx(kcProps.kcFormClass)} action={url.registrationAction} method="post">
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("firstName", kcProps.kcFormGroupErrorClass))}>
|
||||||
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor="firstName" className={cx(kcProps.kcLabelClass)}>
|
||||||
|
{msg("firstName")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="firstName"
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
name="firstName"
|
||||||
|
defaultValue={register.formData.firstName ?? ""}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("lastName", kcProps.kcFormGroupErrorClass))}>
|
||||||
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor="lastName" className={cx(kcProps.kcLabelClass)}>
|
||||||
|
{msg("lastName")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="lastName"
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
name="lastName"
|
||||||
|
defaultValue={register.formData.lastName ?? ""}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("email", kcProps.kcFormGroupErrorClass))}>
|
||||||
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor="email" className={cx(kcProps.kcLabelClass)}>
|
||||||
|
{msg("email")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="email"
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
name="email"
|
||||||
|
defaultValue={register.formData.email ?? ""}
|
||||||
|
autoComplete="email"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{!realm.registrationEmailAsUsername && (
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("username", kcProps.kcFormGroupErrorClass))}>
|
||||||
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor="username" className={cx(kcProps.kcLabelClass)}>
|
||||||
|
{msg("username")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="firstName"
|
id="username"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
name="firstName"
|
name="username"
|
||||||
defaultValue={register.formData.firstName ?? ""}
|
defaultValue={register.formData.username ?? ""}
|
||||||
|
autoComplete="username"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("lastName", props.kcFormGroupErrorClass))}>
|
{passwordRequired && (
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<>
|
||||||
<label htmlFor="lastName" className={cx(props.kcLabelClass)}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("password", kcProps.kcFormGroupErrorClass))}>
|
||||||
{msg("lastName")}
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
</label>
|
<label htmlFor="password" className={cx(kcProps.kcLabelClass)}>
|
||||||
</div>
|
{msg("password")}
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="lastName"
|
|
||||||
className={cx(props.kcInputClass)}
|
|
||||||
name="lastName"
|
|
||||||
defaultValue={register.formData.lastName ?? ""}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("email", props.kcFormGroupErrorClass))}>
|
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
|
||||||
<label htmlFor="email" className={cx(props.kcLabelClass)}>
|
|
||||||
{msg("email")}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="email"
|
|
||||||
className={cx(props.kcInputClass)}
|
|
||||||
name="email"
|
|
||||||
defaultValue={register.formData.email ?? ""}
|
|
||||||
autoComplete="email"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{!realm.registrationEmailAsUsername && (
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("username", props.kcFormGroupErrorClass))}>
|
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
|
||||||
<label htmlFor="username" className={cx(props.kcLabelClass)}>
|
|
||||||
{msg("username")}
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="password"
|
||||||
id="username"
|
id="password"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
name="username"
|
name="password"
|
||||||
defaultValue={register.formData.username ?? ""}
|
autoComplete="new-password"
|
||||||
autoComplete="username"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
{passwordRequired && (
|
|
||||||
<>
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("password", props.kcFormGroupErrorClass))}>
|
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
|
||||||
<label htmlFor="password" className={cx(props.kcLabelClass)}>
|
|
||||||
{msg("password")}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id="password"
|
|
||||||
className={cx(props.kcInputClass)}
|
|
||||||
name="password"
|
|
||||||
autoComplete="new-password"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
props.kcFormGroupClass,
|
kcProps.kcFormGroupClass,
|
||||||
messagesPerField.printIfExists("password-confirm", props.kcFormGroupErrorClass)
|
messagesPerField.printIfExists("password-confirm", kcProps.kcFormGroupErrorClass)
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="password-confirm" className={cx(props.kcLabelClass)}>
|
<label htmlFor="password-confirm" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("passwordConfirm")}
|
{msg("passwordConfirm")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
<input type="password" id="password-confirm" className={cx(props.kcInputClass)} name="password-confirm" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
)}
|
<input type="password" id="password-confirm" className={cx(kcProps.kcInputClass)} name="password-confirm" />
|
||||||
{recaptchaRequired && (
|
|
||||||
<div className="form-group">
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</>
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
)}
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
{recaptchaRequired && (
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
<div className="form-group">
|
||||||
<span>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
|
||||||
<input
|
|
||||||
className={cx(
|
|
||||||
props.kcButtonClass,
|
|
||||||
props.kcButtonPrimaryClass,
|
|
||||||
props.kcButtonBlockClass,
|
|
||||||
props.kcButtonLargeClass
|
|
||||||
)}
|
|
||||||
type="submit"
|
|
||||||
value={msgStr("doRegister")}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
)}
|
||||||
}
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
/>
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
);
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
}
|
<span>
|
||||||
);
|
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
|
<input
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doRegister")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default Register;
|
export default Register;
|
||||||
|
@ -1,78 +1,81 @@
|
|||||||
import React, { useMemo, memo, useState } from "react";
|
import React, { useMemo, memo, useState } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
import { UserProfileFormFields } from "./shared/UserProfileCommons";
|
import { UserProfileFormFields } from "./shared/UserProfileCommons";
|
||||||
|
|
||||||
const RegisterUserProfile = memo(
|
export type RegisterUserProfileProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.RegisterUserProfile;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props_
|
};
|
||||||
}: { kcContext: KcContextBase.RegisterUserProfile; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const RegisterUserProfile = memo((props: RegisterUserProfileProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps_ } = props;
|
||||||
|
|
||||||
const { cx, css } = useCssAndCx();
|
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
||||||
|
|
||||||
const props = useMemo(
|
const { msg, msgStr } = i18n;
|
||||||
() => ({
|
|
||||||
...props_,
|
|
||||||
"kcFormGroupClass": cx(props_.kcFormGroupClass, css({ "marginBottom": 20 }))
|
|
||||||
}),
|
|
||||||
[cx, css]
|
|
||||||
);
|
|
||||||
|
|
||||||
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
|
const { cx, css } = useCssAndCx();
|
||||||
|
|
||||||
return (
|
const kcProps = useMemo(
|
||||||
<Template
|
() => ({
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
...kcProps_,
|
||||||
displayMessage={messagesPerField.exists("global")}
|
"kcFormGroupClass": cx(kcProps_.kcFormGroupClass, css({ "marginBottom": 20 }))
|
||||||
displayRequiredFields={true}
|
}),
|
||||||
headerNode={msg("registerTitle")}
|
[cx, css]
|
||||||
formNode={
|
);
|
||||||
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
|
||||||
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...props} />
|
|
||||||
{recaptchaRequired && (
|
|
||||||
<div className="form-group">
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
|
||||||
<span>
|
|
||||||
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
|
||||||
<input
|
|
||||||
className={cx(
|
return (
|
||||||
props.kcButtonClass,
|
<Template
|
||||||
props.kcButtonPrimaryClass,
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
props.kcButtonBlockClass,
|
displayMessage={messagesPerField.exists("global")}
|
||||||
props.kcButtonLargeClass
|
displayRequiredFields={true}
|
||||||
)}
|
headerNode={msg("registerTitle")}
|
||||||
type="submit"
|
formNode={
|
||||||
value={msgStr("doRegister")}
|
<form id="kc-register-form" className={cx(kcProps.kcFormClass)} action={url.registrationAction} method="post">
|
||||||
disabled={!isFomSubmittable}
|
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...kcProps} />
|
||||||
/>
|
{recaptchaRequired && (
|
||||||
|
<div className="form-group">
|
||||||
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
|
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
)}
|
||||||
}
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
/>
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
);
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
}
|
<span>
|
||||||
);
|
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
|
<input
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doRegister")}
|
||||||
|
disabled={!isFomSubmittable}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default RegisterUserProfile;
|
export default RegisterUserProfile;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, memo } from "react";
|
import React, { useEffect, memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
@ -12,6 +13,7 @@ import memoize from "memoizee";
|
|||||||
import { useConst } from "powerhooks/useConst";
|
import { useConst } from "powerhooks/useConst";
|
||||||
import { useConstCallback } from "powerhooks/useConstCallback";
|
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||||
import { Markdown } from "../tools/Markdown";
|
import { Markdown } from "../tools/Markdown";
|
||||||
|
import type { Extends } from "tsafe";
|
||||||
|
|
||||||
export const evtTermMarkdown = Evt.create<string | undefined>(undefined);
|
export const evtTermMarkdown = Evt.create<string | undefined>(undefined);
|
||||||
|
|
||||||
@ -22,7 +24,7 @@ export type KcContextLike = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
assert<KcContextBase extends KcContextLike ? true : false>();
|
assert<Extends<KcContextBase, KcContextLike>>();
|
||||||
|
|
||||||
/** Allow to avoid bundling the terms and download it on demand*/
|
/** Allow to avoid bundling the terms and download it on demand*/
|
||||||
export function useDownloadTerms(params: {
|
export function useDownloadTerms(params: {
|
||||||
@ -54,61 +56,63 @@ export function useDownloadTerms(params: {
|
|||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Terms = memo(
|
export type TermsProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.Terms;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.Terms; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { msg, msgStr } = i18n;
|
|
||||||
|
|
||||||
useRerenderOnStateChange(evtTermMarkdown);
|
const Terms = memo((props: TermsProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { url } = kcContext;
|
useRerenderOnStateChange(evtTermMarkdown);
|
||||||
|
|
||||||
if (evtTermMarkdown.state === undefined) {
|
const { cx } = useCssAndCx();
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
const { url } = kcContext;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
if (evtTermMarkdown.state === undefined) {
|
||||||
displayMessage={false}
|
return null;
|
||||||
headerNode={msg("termsTitle")}
|
|
||||||
formNode={
|
|
||||||
<>
|
|
||||||
<div id="kc-terms-text">{evtTermMarkdown.state && <Markdown>{evtTermMarkdown.state}</Markdown>}</div>
|
|
||||||
<form className="form-actions" action={url.loginAction} method="POST">
|
|
||||||
<input
|
|
||||||
className={cx(
|
|
||||||
props.kcButtonClass,
|
|
||||||
props.kcButtonClass,
|
|
||||||
props.kcButtonClass,
|
|
||||||
props.kcButtonPrimaryClass,
|
|
||||||
props.kcButtonLargeClass
|
|
||||||
)}
|
|
||||||
name="accept"
|
|
||||||
id="kc-accept"
|
|
||||||
type="submit"
|
|
||||||
value={msgStr("doAccept")}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
|
|
||||||
name="cancel"
|
|
||||||
id="kc-decline"
|
|
||||||
type="submit"
|
|
||||||
value={msgStr("doDecline")}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
<div className="clearfix" />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
return (
|
||||||
|
<Template
|
||||||
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
|
displayMessage={false}
|
||||||
|
headerNode={msg("termsTitle")}
|
||||||
|
formNode={
|
||||||
|
<>
|
||||||
|
<div id="kc-terms-text">{evtTermMarkdown.state && <Markdown>{evtTermMarkdown.state}</Markdown>}</div>
|
||||||
|
<form className="form-actions" action={url.loginAction} method="POST">
|
||||||
|
<input
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
name="accept"
|
||||||
|
id="kc-accept"
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doAccept")}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
|
||||||
|
name="cancel"
|
||||||
|
id="kc-decline"
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doDecline")}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
<div className="clearfix" />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default Terms;
|
export default Terms;
|
||||||
|
@ -1,77 +1,80 @@
|
|||||||
import React, { useState, memo } from "react";
|
import React, { useState, memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
import { UserProfileFormFields } from "./shared/UserProfileCommons";
|
import { UserProfileFormFields } from "./shared/UserProfileCommons";
|
||||||
|
|
||||||
const UpdateUserProfile = memo(
|
export type UpdateUserProfileProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.UpdateUserProfile;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.UpdateUserProfile; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { cx } = useCssAndCx();
|
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const UpdateUserProfile = memo((props: UpdateUserProfileProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { url, isAppInitiatedAction } = kcContext;
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
return (
|
const { url, isAppInitiatedAction } = kcContext;
|
||||||
<Template
|
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
|
||||||
headerNode={msg("loginProfileTitle")}
|
|
||||||
formNode={
|
|
||||||
<form id="kc-update-profile-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
|
||||||
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...props} />
|
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
return (
|
||||||
{isAppInitiatedAction ? (
|
<Template
|
||||||
<>
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
<input
|
headerNode={msg("loginProfileTitle")}
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
formNode={
|
||||||
type="submit"
|
<form id="kc-update-profile-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
value={msgStr("doSubmit")}
|
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...kcProps} />
|
||||||
/>
|
|
||||||
<button
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
type="submit"
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}></div>
|
||||||
name="cancel-aia"
|
|
||||||
value="true"
|
|
||||||
formNoValidate
|
|
||||||
>
|
|
||||||
{msg("doCancel")}
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<input
|
|
||||||
className={cx(
|
|
||||||
props.kcButtonClass,
|
|
||||||
props.kcButtonPrimaryClass,
|
|
||||||
props.kcButtonBlockClass,
|
|
||||||
props.kcButtonLargeClass
|
|
||||||
)}
|
|
||||||
type="submit"
|
|
||||||
defaultValue={msgStr("doSubmit")}
|
|
||||||
disabled={!isFomSubmittable}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
}
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
/>
|
{isAppInitiatedAction ? (
|
||||||
);
|
<>
|
||||||
}
|
<input
|
||||||
);
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doSubmit")}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
|
||||||
|
type="submit"
|
||||||
|
name="cancel-aia"
|
||||||
|
value="true"
|
||||||
|
formNoValidate
|
||||||
|
>
|
||||||
|
{msg("doCancel")}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
type="submit"
|
||||||
|
defaultValue={msgStr("doSubmit")}
|
||||||
|
disabled={!isFomSubmittable}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default UpdateUserProfile;
|
export default UpdateUserProfile;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useRef, useState, memo } from "react";
|
import React, { useRef, useState, memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
@ -7,198 +8,198 @@ import type { I18n, MessageKeyBase } from "../i18n";
|
|||||||
import { base64url } from "rfc4648";
|
import { base64url } from "rfc4648";
|
||||||
import { useConstCallback } from "powerhooks/useConstCallback";
|
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||||
|
|
||||||
const WebauthnAuthenticate = memo(
|
export type WebauthnAuthenticateProps = KcProps & {
|
||||||
({
|
kcContext: KcContextBase.WebauthnAuthenticate;
|
||||||
kcContext,
|
i18n: I18n;
|
||||||
i18n,
|
doFetchDefaultThemeResources?: boolean;
|
||||||
doFetchDefaultThemeResources = true,
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
...props
|
};
|
||||||
}: { kcContext: KcContextBase.WebauthnAuthenticate; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => {
|
|
||||||
const { url } = kcContext;
|
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const WebauthnAuthenticate = memo((props: WebauthnAuthenticateProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { authenticators, challenge, shouldDisplayAuthenticators, userVerification, rpId } = kcContext;
|
const { url } = kcContext;
|
||||||
const createTimeout = Number(kcContext.createTimeout);
|
|
||||||
const isUserIdentified = kcContext.isUserIdentified == "true";
|
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const webAuthnAuthenticate = useConstCallback(async () => {
|
const { authenticators, challenge, shouldDisplayAuthenticators, userVerification, rpId } = kcContext;
|
||||||
if (!isUserIdentified) {
|
const createTimeout = Number(kcContext.createTimeout);
|
||||||
return;
|
const isUserIdentified = kcContext.isUserIdentified == "true";
|
||||||
}
|
|
||||||
const allowCredentials = authenticators.authenticators.map(
|
|
||||||
authenticator =>
|
|
||||||
({
|
|
||||||
id: base64url.parse(authenticator.credentialId, { loose: true }),
|
|
||||||
type: "public-key"
|
|
||||||
} as PublicKeyCredentialDescriptor)
|
|
||||||
);
|
|
||||||
// Check if WebAuthn is supported by this browser
|
|
||||||
if (!window.PublicKeyCredential) {
|
|
||||||
setError(msgStr("webauthn-unsupported-browser-text"));
|
|
||||||
submitForm();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const publicKey: PublicKeyCredentialRequestOptions = {
|
const { cx } = useCssAndCx();
|
||||||
rpId,
|
|
||||||
challenge: base64url.parse(challenge, { loose: true })
|
|
||||||
};
|
|
||||||
|
|
||||||
if (createTimeout !== 0) {
|
const webAuthnAuthenticate = useConstCallback(async () => {
|
||||||
publicKey.timeout = createTimeout * 1000;
|
if (!isUserIdentified) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
const allowCredentials = authenticators.authenticators.map(
|
||||||
|
authenticator =>
|
||||||
|
({
|
||||||
|
id: base64url.parse(authenticator.credentialId, { loose: true }),
|
||||||
|
type: "public-key"
|
||||||
|
} as PublicKeyCredentialDescriptor)
|
||||||
|
);
|
||||||
|
// Check if WebAuthn is supported by this browser
|
||||||
|
if (!window.PublicKeyCredential) {
|
||||||
|
setError(msgStr("webauthn-unsupported-browser-text"));
|
||||||
|
submitForm();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (allowCredentials.length) {
|
const publicKey: PublicKeyCredentialRequestOptions = {
|
||||||
publicKey.allowCredentials = allowCredentials;
|
rpId,
|
||||||
}
|
challenge: base64url.parse(challenge, { loose: true })
|
||||||
|
};
|
||||||
|
|
||||||
if (userVerification !== "not specified") {
|
if (createTimeout !== 0) {
|
||||||
publicKey.userVerification = userVerification;
|
publicKey.timeout = createTimeout * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (allowCredentials.length) {
|
||||||
const resultRaw = await navigator.credentials.get({ publicKey });
|
publicKey.allowCredentials = allowCredentials;
|
||||||
if (!resultRaw || resultRaw.type != "public-key") return;
|
}
|
||||||
const result = resultRaw as PublicKeyCredential;
|
|
||||||
if (!("authenticatorData" in result.response)) return;
|
|
||||||
const response = result.response as AuthenticatorAssertionResponse;
|
|
||||||
const clientDataJSON = response.clientDataJSON;
|
|
||||||
const authenticatorData = response.authenticatorData;
|
|
||||||
const signature = response.signature;
|
|
||||||
|
|
||||||
setClientDataJSON(base64url.stringify(new Uint8Array(clientDataJSON), { pad: false }));
|
if (userVerification !== "not specified") {
|
||||||
setAuthenticatorData(base64url.stringify(new Uint8Array(authenticatorData), { pad: false }));
|
publicKey.userVerification = userVerification;
|
||||||
setSignature(base64url.stringify(new Uint8Array(signature), { pad: false }));
|
}
|
||||||
setCredentialId(result.id);
|
|
||||||
setUserHandle(base64url.stringify(new Uint8Array(response.userHandle!), { pad: false }));
|
|
||||||
submitForm();
|
|
||||||
} catch (err) {
|
|
||||||
setError(String(err));
|
|
||||||
submitForm();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const webAuthForm = useRef<HTMLFormElement>(null);
|
try {
|
||||||
const submitForm = useConstCallback(() => {
|
const resultRaw = await navigator.credentials.get({ publicKey });
|
||||||
webAuthForm.current!.submit();
|
if (!resultRaw || resultRaw.type != "public-key") return;
|
||||||
});
|
const result = resultRaw as PublicKeyCredential;
|
||||||
|
if (!("authenticatorData" in result.response)) return;
|
||||||
|
const response = result.response as AuthenticatorAssertionResponse;
|
||||||
|
const clientDataJSON = response.clientDataJSON;
|
||||||
|
const authenticatorData = response.authenticatorData;
|
||||||
|
const signature = response.signature;
|
||||||
|
|
||||||
const [clientDataJSON, setClientDataJSON] = useState("");
|
setClientDataJSON(base64url.stringify(new Uint8Array(clientDataJSON), { pad: false }));
|
||||||
const [authenticatorData, setAuthenticatorData] = useState("");
|
setAuthenticatorData(base64url.stringify(new Uint8Array(authenticatorData), { pad: false }));
|
||||||
const [signature, setSignature] = useState("");
|
setSignature(base64url.stringify(new Uint8Array(signature), { pad: false }));
|
||||||
const [credentialId, setCredentialId] = useState("");
|
setCredentialId(result.id);
|
||||||
const [userHandle, setUserHandle] = useState("");
|
setUserHandle(base64url.stringify(new Uint8Array(response.userHandle!), { pad: false }));
|
||||||
const [error, setError] = useState("");
|
submitForm();
|
||||||
|
} catch (err) {
|
||||||
|
setError(String(err));
|
||||||
|
submitForm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
const webAuthForm = useRef<HTMLFormElement>(null);
|
||||||
<Template
|
const submitForm = useConstCallback(() => {
|
||||||
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...props }}
|
webAuthForm.current!.submit();
|
||||||
headerNode={msg("webauthn-login-title")}
|
});
|
||||||
formNode={
|
|
||||||
<div id="kc-form-webauthn" className={cx(props.kcFormClass)}>
|
|
||||||
<form id="webauth" action={url.loginAction} ref={webAuthForm} method="post">
|
|
||||||
<input type="hidden" id="clientDataJSON" name="clientDataJSON" value={clientDataJSON} />
|
|
||||||
<input type="hidden" id="authenticatorData" name="authenticatorData" value={authenticatorData} />
|
|
||||||
<input type="hidden" id="signature" name="signature" value={signature} />
|
|
||||||
<input type="hidden" id="credentialId" name="credentialId" value={credentialId} />
|
|
||||||
<input type="hidden" id="userHandle" name="userHandle" value={userHandle} />
|
|
||||||
<input type="hidden" id="error" name="error" value={error} />
|
|
||||||
</form>
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
|
||||||
{authenticators &&
|
|
||||||
(() => (
|
|
||||||
<form id="authn_select" className={cx(props.kcFormClass)}>
|
|
||||||
{authenticators.authenticators.map(authenticator => (
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
name="authn_use_chk"
|
|
||||||
value={authenticator.credentialId}
|
|
||||||
key={authenticator.credentialId}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</form>
|
|
||||||
))()}
|
|
||||||
{authenticators &&
|
|
||||||
shouldDisplayAuthenticators &&
|
|
||||||
(() => (
|
|
||||||
<>
|
|
||||||
{authenticators.authenticators.length > 1 && (
|
|
||||||
<p className={cx(props.kcSelectAuthListItemTitle)}>{msg("webauthn-available-authenticators")}</p>
|
|
||||||
)}
|
|
||||||
<div className={cx(props.kcFormClass)}>
|
|
||||||
{authenticators.authenticators.map(authenticator => (
|
|
||||||
<div id="kc-webauthn-authenticator" className={cx(props.kcSelectAuthListItemClass)}>
|
|
||||||
<div className={cx(props.kcSelectAuthListItemIconClass)}>
|
|
||||||
<i
|
|
||||||
className={cx(
|
|
||||||
props[authenticator.transports.iconClass] ?? props.kcWebAuthnDefaultIcon,
|
|
||||||
props.kcSelectAuthListItemIconPropertyClass
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={cx(props.kcSelectAuthListItemBodyClass)}>
|
|
||||||
<div
|
|
||||||
id="kc-webauthn-authenticator-label"
|
|
||||||
className={cx(props.kcSelectAuthListItemHeadingClass)}
|
|
||||||
>
|
|
||||||
{authenticator.label}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{authenticator.transports && authenticator.transports.displayNameProperties.length && (
|
const [clientDataJSON, setClientDataJSON] = useState("");
|
||||||
<div
|
const [authenticatorData, setAuthenticatorData] = useState("");
|
||||||
id="kc-webauthn-authenticator-transport"
|
const [signature, setSignature] = useState("");
|
||||||
className={cx(props.kcSelectAuthListItemDescriptionClass)}
|
const [credentialId, setCredentialId] = useState("");
|
||||||
>
|
const [userHandle, setUserHandle] = useState("");
|
||||||
{authenticator.transports.displayNameProperties.map(
|
const [error, setError] = useState("");
|
||||||
(transport: MessageKeyBase, index: number) => (
|
|
||||||
<>
|
|
||||||
<span>{msg(transport)}</span>
|
|
||||||
{index < authenticator.transports.displayNameProperties.length - 1 && (
|
|
||||||
<span>{", "}</span>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className={cx(props.kcSelectAuthListItemDescriptionClass)}>
|
return (
|
||||||
<span id="kc-webauthn-authenticator-created-label">
|
<Template
|
||||||
{msg("webauthn-createdAt-label")}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
</span>
|
headerNode={msg("webauthn-login-title")}
|
||||||
<span id="kc-webauthn-authenticator-created">{authenticator.createdAt}</span>
|
formNode={
|
||||||
</div>
|
<div id="kc-form-webauthn" className={cx(kcProps.kcFormClass)}>
|
||||||
</div>
|
<form id="webauth" action={url.loginAction} ref={webAuthForm} method="post">
|
||||||
<div className={cx(props.kcSelectAuthListItemFillClass)} />
|
<input type="hidden" id="clientDataJSON" name="clientDataJSON" value={clientDataJSON} />
|
||||||
</div>
|
<input type="hidden" id="authenticatorData" name="authenticatorData" value={authenticatorData} />
|
||||||
))}
|
<input type="hidden" id="signature" name="signature" value={signature} />
|
||||||
</div>
|
<input type="hidden" id="credentialId" name="credentialId" value={credentialId} />
|
||||||
</>
|
<input type="hidden" id="userHandle" name="userHandle" value={userHandle} />
|
||||||
))()}
|
<input type="hidden" id="error" name="error" value={error} />
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
</form>
|
||||||
<input
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
id="authenticateWebAuthnButton"
|
{authenticators &&
|
||||||
type="button"
|
(() => (
|
||||||
onClick={webAuthnAuthenticate}
|
<form id="authn_select" className={cx(kcProps.kcFormClass)}>
|
||||||
autoFocus={true}
|
{authenticators.authenticators.map(authenticator => (
|
||||||
value={msgStr("webauthn-doAuthenticate")}
|
<input
|
||||||
className={cx(
|
type="hidden"
|
||||||
props.kcButtonClass,
|
name="authn_use_chk"
|
||||||
props.kcButtonPrimaryClass,
|
value={authenticator.credentialId}
|
||||||
props.kcButtonBlockClass,
|
key={authenticator.credentialId}
|
||||||
props.kcButtonLargeClass
|
/>
|
||||||
|
))}
|
||||||
|
</form>
|
||||||
|
))()}
|
||||||
|
{authenticators &&
|
||||||
|
shouldDisplayAuthenticators &&
|
||||||
|
(() => (
|
||||||
|
<>
|
||||||
|
{authenticators.authenticators.length > 1 && (
|
||||||
|
<p className={cx(kcProps.kcSelectAuthListItemTitle)}>{msg("webauthn-available-authenticators")}</p>
|
||||||
)}
|
)}
|
||||||
/>
|
<div className={cx(kcProps.kcFormClass)}>
|
||||||
</div>
|
{authenticators.authenticators.map(authenticator => (
|
||||||
|
<div id="kc-webauthn-authenticator" className={cx(kcProps.kcSelectAuthListItemClass)}>
|
||||||
|
<div className={cx(kcProps.kcSelectAuthListItemIconClass)}>
|
||||||
|
<i
|
||||||
|
className={cx(
|
||||||
|
kcProps[authenticator.transports.iconClass] ?? kcProps.kcWebAuthnDefaultIcon,
|
||||||
|
kcProps.kcSelectAuthListItemIconPropertyClass
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcSelectAuthListItemBodyClass)}>
|
||||||
|
<div
|
||||||
|
id="kc-webauthn-authenticator-label"
|
||||||
|
className={cx(kcProps.kcSelectAuthListItemHeadingClass)}
|
||||||
|
>
|
||||||
|
{authenticator.label}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{authenticator.transports && authenticator.transports.displayNameProperties.length && (
|
||||||
|
<div
|
||||||
|
id="kc-webauthn-authenticator-transport"
|
||||||
|
className={cx(kcProps.kcSelectAuthListItemDescriptionClass)}
|
||||||
|
>
|
||||||
|
{authenticator.transports.displayNameProperties.map(
|
||||||
|
(transport: MessageKeyBase, index: number) => (
|
||||||
|
<>
|
||||||
|
<span>{msg(transport)}</span>
|
||||||
|
{index < authenticator.transports.displayNameProperties.length - 1 && (
|
||||||
|
<span>{", "}</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={cx(kcProps.kcSelectAuthListItemDescriptionClass)}>
|
||||||
|
<span id="kc-webauthn-authenticator-created-label">{msg("webauthn-createdAt-label")}</span>
|
||||||
|
<span id="kc-webauthn-authenticator-created">{authenticator.createdAt}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcSelectAuthListItemFillClass)} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
))()}
|
||||||
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
|
<input
|
||||||
|
id="authenticateWebAuthnButton"
|
||||||
|
type="button"
|
||||||
|
onClick={webAuthnAuthenticate}
|
||||||
|
autoFocus={true}
|
||||||
|
value={msgStr("webauthn-doAuthenticate")}
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
/>
|
}
|
||||||
);
|
/>
|
||||||
}
|
);
|
||||||
);
|
});
|
||||||
|
|
||||||
export default WebauthnAuthenticate;
|
export default WebauthnAuthenticate;
|
||||||
|
25
yarn.lock
25
yarn.lock
@ -967,14 +967,14 @@ event-emitter@^0.3.5:
|
|||||||
d "1"
|
d "1"
|
||||||
es5-ext "~0.10.14"
|
es5-ext "~0.10.14"
|
||||||
|
|
||||||
evt@^2.4.5:
|
evt@^2.4.6:
|
||||||
version "2.4.5"
|
version "2.4.6"
|
||||||
resolved "https://registry.yarnpkg.com/evt/-/evt-2.4.5.tgz#9e383c20a7917977e26b9fbad178585916ea8142"
|
resolved "https://registry.yarnpkg.com/evt/-/evt-2.4.6.tgz#79e242cd2f84c2ec0b8119478712f7af80368832"
|
||||||
integrity sha512-shiXMrEhTHqTl5PacT3vdA1U9i3usnInuxt77Lj0Ph9igNM982b1Uf/3L24OSzI0SqLx/XIRVIJcgzUZ2Ij8AA==
|
integrity sha512-4kSu9YVklCeddEAu/UCzIISrxc5SAD82hX7gXjBox4yyrPNblYrmJr4GgTOOQpNXTGYci+UIpFUYikgZa9fyTQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
minimal-polyfills "^2.2.2"
|
minimal-polyfills "^2.2.2"
|
||||||
run-exclusive "^2.2.16"
|
run-exclusive "^2.2.16"
|
||||||
tsafe "^1.1.2"
|
tsafe "^1.1.3"
|
||||||
|
|
||||||
execa@^5.1.1:
|
execa@^5.1.1:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
@ -1634,12 +1634,12 @@ please-upgrade-node@^3.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
semver-compare "^1.0.0"
|
semver-compare "^1.0.0"
|
||||||
|
|
||||||
powerhooks@^0.20.22:
|
powerhooks@^0.20.23:
|
||||||
version "0.20.22"
|
version "0.20.23"
|
||||||
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.20.22.tgz#469ba048dfa9c1d549325c73a091f8f3b96895b3"
|
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.20.23.tgz#52619405f63bf621c52f0cb944c58414734b618f"
|
||||||
integrity sha512-xFv5s7JTkwQh+lDVR1yLgGXPYfpbmdmdg6qT4VTF4EArieaImhCXFp7arSul55FvYoCuD5+gR9ooctDG53LeLg==
|
integrity sha512-+44ltLJmvti6XZJuaRlfMbjX/2ZkJBw8CybkwDIh/GntOtyertWhbYa1k9QBy2deLKTcBj39Tx86/exIHoCcFA==
|
||||||
dependencies:
|
dependencies:
|
||||||
evt "^2.4.5"
|
evt "^2.4.6"
|
||||||
memoizee "^0.4.15"
|
memoizee "^0.4.15"
|
||||||
resize-observer-polyfill "^1.5.1"
|
resize-observer-polyfill "^1.5.1"
|
||||||
tsafe "^1.1.3"
|
tsafe "^1.1.3"
|
||||||
@ -1992,11 +1992,6 @@ trough@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
|
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
|
||||||
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
|
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
|
||||||
|
|
||||||
tsafe@^1.1.2:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.1.2.tgz#cece1900bca89e25a84a65e7087f00dff3664b2e"
|
|
||||||
integrity sha512-jom5KsB9vpvOE9dLx2yTrPAJzzwU9CRPgoatoD7m2Zb7FCqo6ueEdZ+AZk+OysM4N+m8EUnIa1s9Pq3IMRDYLA==
|
|
||||||
|
|
||||||
tsafe@^1.1.3:
|
tsafe@^1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.1.3.tgz#fa7c2ae7da689884292b70ee30febbca5c665d03"
|
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.1.3.tgz#fa7c2ae7da689884292b70ee30febbca5c665d03"
|
||||||
|
Reference in New Issue
Block a user