commit
6c14758e33
6
.github/workflows/ci.yaml
vendored
6
.github/workflows/ci.yaml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v2.3.4
|
||||||
- uses: actions/setup-node@v2.1.3
|
- uses: actions/setup-node@v2.1.3
|
||||||
- uses: bahmutov/npm-install@v1
|
- uses: bahmutov/npm-install@v1.8.15
|
||||||
- name: If this step fails run 'yarn format' then commit again.
|
- name: If this step fails run 'yarn format' then commit again.
|
||||||
run: |
|
run: |
|
||||||
PACKAGE_MANAGER=npm
|
PACKAGE_MANAGER=npm
|
||||||
@ -41,7 +41,7 @@ jobs:
|
|||||||
- uses: actions/setup-node@v2.1.3
|
- uses: actions/setup-node@v2.1.3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- uses: bahmutov/npm-install@v1
|
- uses: bahmutov/npm-install@v1.8.15
|
||||||
- if: steps.step1.outputs.npm_or_yarn == 'yarn'
|
- if: steps.step1.outputs.npm_or_yarn == 'yarn'
|
||||||
run: |
|
run: |
|
||||||
yarn build
|
yarn build
|
||||||
@ -123,7 +123,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: '15'
|
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.8.15
|
||||||
- run: |
|
- run: |
|
||||||
PACKAGE_MANAGER=npm
|
PACKAGE_MANAGER=npm
|
||||||
if [ -f "./yarn.lock" ]; then
|
if [ -f "./yarn.lock" ]; then
|
||||||
|
19
CHANGELOG.md
19
CHANGELOG.md
@ -1,3 +1,22 @@
|
|||||||
|
## **5.6.0** (2022-06-28)
|
||||||
|
|
||||||
|
- Merge pull request #127 from aidangilmore/add-totp-support
|
||||||
|
|
||||||
|
feat: add login-config-totp.ftl page
|
||||||
|
- Fix unknown algorithm name lookup in LoginConfigTotp
|
||||||
|
- Add totp config support
|
||||||
|
|
||||||
|
## **5.5.0** (2022-06-28)
|
||||||
|
|
||||||
|
- Make it possible to redirect to login by repacing the url (should be default in most case)
|
||||||
|
|
||||||
|
### **5.4.7** (2022-06-19)
|
||||||
|
|
||||||
|
- #121
|
||||||
|
- fmt
|
||||||
|
- Create CONTRIBUTING.md
|
||||||
|
- Enable users to link keycloak in their own app
|
||||||
|
|
||||||
### **5.4.6** (2022-06-16)
|
### **5.4.6** (2022-06-16)
|
||||||
|
|
||||||
- Use keycloak 18.0.1 i18n resources #120
|
- Use keycloak 18.0.1 i18n resources #120
|
||||||
|
@ -38,6 +38,10 @@
|
|||||||
|
|
||||||
# Changelog highlights
|
# Changelog highlights
|
||||||
|
|
||||||
|
## v5.6.0
|
||||||
|
|
||||||
|
Add support for `login-config-totp.ftl` page [#127](https://github.com/InseeFrLab/keycloakify/pull/127).
|
||||||
|
|
||||||
## v5.3.0
|
## v5.3.0
|
||||||
|
|
||||||
Rename `keycloak_theme_email` to `keycloak_email`.
|
Rename `keycloak_theme_email` to `keycloak_email`.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloakify",
|
||||||
"version": "5.4.6",
|
"version": "5.6.0",
|
||||||
"description": "Keycloak theme generator for Reacts app",
|
"description": "Keycloak theme generator for Reacts app",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -63,13 +63,13 @@
|
|||||||
"@emotion/react": "^11.4.1",
|
"@emotion/react": "^11.4.1",
|
||||||
"@types/memoizee": "^0.4.7",
|
"@types/memoizee": "^0.4.7",
|
||||||
"@types/node": "^17.0.25",
|
"@types/node": "^17.0.25",
|
||||||
"@types/react": "^17.0.0",
|
"@types/react": "18.0.9",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"husky": "^4.3.8",
|
"husky": "^4.3.8",
|
||||||
"lint-staged": "^11.0.0",
|
"lint-staged": "^11.0.0",
|
||||||
"prettier": "^2.3.0",
|
"prettier": "^2.3.0",
|
||||||
"properties-parser": "^0.3.1",
|
"properties-parser": "^0.3.1",
|
||||||
"react": "^17.0.1",
|
"react": "18.1.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"typescript": "^4.2.3"
|
"typescript": "^4.2.3"
|
||||||
},
|
},
|
||||||
|
@ -155,33 +155,6 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
|||||||
|
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
<#if key == "showUsername" && are_same_path(path, ["auth"])>
|
|
||||||
<#attempt>
|
|
||||||
<#return auth.showUsername()?c>
|
|
||||||
<#recover>
|
|
||||||
<#local out_seq += ["/*Couldn't evaluate auth.showUsername()*/"]>
|
|
||||||
<#continue>
|
|
||||||
</#attempt>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#if key == "showResetCredentials" && are_same_path(path, ["auth"])>
|
|
||||||
<#attempt>
|
|
||||||
<#return auth.showResetCredentials()?c>
|
|
||||||
<#recover>
|
|
||||||
<#local out_seq += ["/*Couldn't evaluate auth.showResetCredentials()*/"]>
|
|
||||||
<#continue>
|
|
||||||
</#attempt>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#if key == "showTryAnotherWayLink" && are_same_path(path, ["auth"])>
|
|
||||||
<#attempt>
|
|
||||||
<#return auth.showTryAnotherWayLink()?c>
|
|
||||||
<#recover>
|
|
||||||
<#local out_seq += ["/*Couldn't evaluate auth.showTryAnotherWayLink()*/"]>
|
|
||||||
<#continue>
|
|
||||||
</#attempt>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<#attempt>
|
<#attempt>
|
||||||
<#if !object[key]??>
|
<#if !object[key]??>
|
||||||
<#continue>
|
<#continue>
|
||||||
@ -229,6 +202,31 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
|||||||
</#attempt>
|
</#attempt>
|
||||||
|
|
||||||
<#if isMethod>
|
<#if isMethod>
|
||||||
|
|
||||||
|
<#if are_same_path(path, ["auth", "showUsername"])>
|
||||||
|
<#attempt>
|
||||||
|
<#return auth.showUsername()?c>
|
||||||
|
<#recover>
|
||||||
|
<#return "ABORT: Couldn't evaluate auth.showUsername()">
|
||||||
|
</#attempt>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<#if are_same_path(path, ["auth", "showResetCredentials"])>
|
||||||
|
<#attempt>
|
||||||
|
<#return auth.showResetCredentials()?c>
|
||||||
|
<#recover>
|
||||||
|
<#return "ABORT: Couldn't evaluate auth.showResetCredentials()">
|
||||||
|
</#attempt>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<#if are_same_path(path, ["auth", "showTryAnotherWayLink"])>
|
||||||
|
<#attempt>
|
||||||
|
<#return auth.showTryAnotherWayLink()?c>
|
||||||
|
<#recover>
|
||||||
|
<#return "ABORT: Couldn't evaluate auth.showTryAnotherWayLink()">
|
||||||
|
</#attempt>
|
||||||
|
</#if>
|
||||||
|
|
||||||
<#return "ABORT: It's a method">
|
<#return "ABORT: It's a method">
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ export const pageIds = [
|
|||||||
"login-idp-link-confirm.ftl",
|
"login-idp-link-confirm.ftl",
|
||||||
"login-idp-link-email.ftl",
|
"login-idp-link-email.ftl",
|
||||||
"login-page-expired.ftl",
|
"login-page-expired.ftl",
|
||||||
|
"login-config-totp.ftl",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type PageId = typeof pageIds[number];
|
export type PageId = typeof pageIds[number];
|
||||||
|
@ -15,6 +15,7 @@ import { LoginUpdateProfile } from "./LoginUpdateProfile";
|
|||||||
import { LoginIdpLinkConfirm } from "./LoginIdpLinkConfirm";
|
import { LoginIdpLinkConfirm } from "./LoginIdpLinkConfirm";
|
||||||
import { LoginPageExpired } from "./LoginPageExpired";
|
import { LoginPageExpired } from "./LoginPageExpired";
|
||||||
import { LoginIdpLinkEmail } from "./LoginIdpLinkEmail";
|
import { LoginIdpLinkEmail } from "./LoginIdpLinkEmail";
|
||||||
|
import { LoginConfigTotp } from "./LoginConfigTotp";
|
||||||
|
|
||||||
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContextBase } & KcProps) => {
|
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContextBase } & KcProps) => {
|
||||||
switch (kcContext.pageId) {
|
switch (kcContext.pageId) {
|
||||||
@ -46,5 +47,7 @@ export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContextBase }
|
|||||||
return <LoginIdpLinkEmail {...{ kcContext, ...props }} />;
|
return <LoginIdpLinkEmail {...{ kcContext, ...props }} />;
|
||||||
case "login-page-expired.ftl":
|
case "login-page-expired.ftl":
|
||||||
return <LoginPageExpired {...{ kcContext, ...props }} />;
|
return <LoginPageExpired {...{ kcContext, ...props }} />;
|
||||||
|
case "login-config-totp.ftl":
|
||||||
|
return <LoginConfigTotp {...{ kcContext, ...props }} />;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
183
src/lib/components/LoginConfigTotp.tsx
Normal file
183
src/lib/components/LoginConfigTotp.tsx
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
import { memo } from "react";
|
||||||
|
import { Template } from "./Template";
|
||||||
|
import type { KcProps } from "./KcProps";
|
||||||
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
|
import { getMsg } from "../i18n";
|
||||||
|
import { useCssAndCx } from "tss-react";
|
||||||
|
|
||||||
|
export const LoginConfigTotp = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginConfigTotp } & KcProps) => {
|
||||||
|
const { url, isAppInitiatedAction, totp, mode, messagesPerField } = kcContext;
|
||||||
|
|
||||||
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
|
const { msg, msgStr } = getMsg(kcContext);
|
||||||
|
const algToKeyUriAlg: Record<KcContextBase.LoginConfigTotp["totp"]["policy"]["algorithm"], string> = {
|
||||||
|
HmacSHA1: "SHA1",
|
||||||
|
HmacSHA256: "SHA256",
|
||||||
|
HmacSHA512: "SHA512",
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Template
|
||||||
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
|
headerNode={msg("loginTotpTitle")}
|
||||||
|
formNode={
|
||||||
|
<>
|
||||||
|
<ol id="kc-totp-settings">
|
||||||
|
<li>
|
||||||
|
<p>{msg("loginTotpStep1")}</p>
|
||||||
|
|
||||||
|
<ul id="kc-totp-supported-apps">
|
||||||
|
{totp.policy.supportedApplications.map(app => (
|
||||||
|
<li>{app}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{mode && mode == "manual" ? (
|
||||||
|
<>
|
||||||
|
<li>
|
||||||
|
<p>{msg("loginTotpManualStep2")}</p>
|
||||||
|
<p>
|
||||||
|
<span id="kc-totp-secret-key">{totp.totpSecretEncoded}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href={totp.qrUrl} id="mode-barcode">
|
||||||
|
{msg("loginTotpScanBarcode")}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</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>
|
||||||
|
<p>{msg("loginTotpStep2")}</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>
|
||||||
|
<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">
|
||||||
|
<div className={cx(props.kcFormGroupClass)}>
|
||||||
|
<div className={cx(props.kcInputWrapperClass)}>
|
||||||
|
<label htmlFor="totp" className={cx(props.kcLabelClass)}>
|
||||||
|
{msg("authenticatorCode")}
|
||||||
|
</label>{" "}
|
||||||
|
<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 className={cx(props.kcFormGroupClass)}>
|
||||||
|
<div className={cx(props.kcInputWrapperClass)}>
|
||||||
|
<label htmlFor="userLabel" className={cx(props.kcLabelClass)}>
|
||||||
|
{msg("loginTotpDeviceName")}
|
||||||
|
</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>
|
||||||
|
|
||||||
|
{isAppInitiatedAction ? (
|
||||||
|
<>
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
||||||
|
id="saveTOTPBtn"
|
||||||
|
value={msgStr("doSubmit")}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className={cx(
|
||||||
|
props.kcButtonClass,
|
||||||
|
props.kcButtonDefaultClass,
|
||||||
|
props.kcButtonLargeClass,
|
||||||
|
props.kcButtonLargeClass,
|
||||||
|
)}
|
||||||
|
id="cancelTOTPBtn"
|
||||||
|
name="cancel-aia"
|
||||||
|
value="true"
|
||||||
|
>
|
||||||
|
${msg("doCancel")}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
||||||
|
id="saveTOTPBtn"
|
||||||
|
value={msgStr("doSubmit")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
@ -24,7 +24,8 @@ export type KcContextBase =
|
|||||||
| KcContextBase.LoginUpdateProfile
|
| KcContextBase.LoginUpdateProfile
|
||||||
| KcContextBase.LoginIdpLinkConfirm
|
| KcContextBase.LoginIdpLinkConfirm
|
||||||
| KcContextBase.LoginIdpLinkEmail
|
| KcContextBase.LoginIdpLinkEmail
|
||||||
| KcContextBase.LoginPageExpired;
|
| KcContextBase.LoginPageExpired
|
||||||
|
| KcContextBase.LoginConfigTotp;
|
||||||
|
|
||||||
export declare namespace KcContextBase {
|
export declare namespace KcContextBase {
|
||||||
export type Common = {
|
export type Common = {
|
||||||
@ -223,6 +224,34 @@ export declare namespace KcContextBase {
|
|||||||
export type LoginPageExpired = Common & {
|
export type LoginPageExpired = Common & {
|
||||||
pageId: "login-page-expired.ftl";
|
pageId: "login-page-expired.ftl";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type LoginConfigTotp = Common & {
|
||||||
|
pageId: "login-config-totp.ftl";
|
||||||
|
mode?: "qr" | "manual" | undefined | null;
|
||||||
|
totp: {
|
||||||
|
totpSecretEncoded: string;
|
||||||
|
qrUrl: string;
|
||||||
|
policy: {
|
||||||
|
supportedApplications: string[];
|
||||||
|
algorithm: "HmacSHA1" | "HmacSHA256" | "HmacSHA512";
|
||||||
|
digits: number;
|
||||||
|
lookAheadWindow: number;
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
type: "totp";
|
||||||
|
period: number;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "hotp";
|
||||||
|
initialCounter: number;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
totpSecretQrCode: string;
|
||||||
|
manualUrl: string;
|
||||||
|
totpSecret: string;
|
||||||
|
otpCredentials: { id: string; userLabel: string }[];
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Attribute = {
|
export type Attribute = {
|
||||||
|
@ -387,4 +387,25 @@ export const kcContextMocks: KcContextBase[] = [
|
|||||||
"username": "anUsername",
|
"username": "anUsername",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
id<KcContextBase.LoginConfigTotp>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "login-config-totp.ftl",
|
||||||
|
totp: {
|
||||||
|
totpSecretEncoded: "KVVF G2BY N4YX S6LB IUYT K2LH IFYE 4SBV",
|
||||||
|
qrUrl: "#",
|
||||||
|
totpSecretQrCode:
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACM0lEQVR4Xu3OIZJgOQwDUDFd2UxiurLAVnnbHw4YGDKtSiWOn4Gxf81//7r/+q8b4HfLGBZDK9d85NmNR+sB42sXvOYrN5P1DcgYYFTGfOlbzE8gzwy3euweGizw7cfdl34/GRhlkxjKNV+5AebPXPORX1JuB9x8ZfbyyD2y1krWAKsbMq1HnqQDaLfa77p4+MqvzEGSqvSAD/2IHW2yHaigR9tX3m8dDIYGcNf3f+gDpVBZbZU77zyJ6Rlcy+qoTMG887KAPD9hsh6a1Sv3gJUHGHUAxSMzj7zqDDe7Phmt2eG+8UsMxjRGm816MAO+8VMl1R1jGHOrZB/5Zo/WXAPgxixm9Mo96vDGrM1eOto8c4Ax4wF437mifOXlpiPzCnN7Y9l95NnEMxgMY9AAGA8fucH14Y1aVb6N/cqrmyh0BVht7k1e+bU8LK0Cg5vmVq9c5vHIjOfqxDIfeTraNVTwewa4wVe+SW5N+uP1qACeudUZbqGOfA6VZV750Noq2Xx3kpveV44ZelSV1V7KFHzkWyVrrlUwG0Pl9pWnoy3vsQoME6vKI69i5osVgwWzHT7zjmJtMcNUSVn1oYMd7ZodbgowZl45VG0uVuLPUr1yc79uaQBag/mqR34xhlWyHm1prplHboCWdZ4TeZjsK8+dI+jbz1C5hl65mcpgB5dhcj8+dGO+0Ko68+lD37JDD83dpDLzzK+TrQyaVwGj6pUboGV+7+AyN8An/pf84/7rv/4/1l4OCc/1BYMAAAAASUVORK5CYII=",
|
||||||
|
manualUrl: "#",
|
||||||
|
totpSecret: "G4nsI8lQagRMUchH8jEG",
|
||||||
|
otpCredentials: [],
|
||||||
|
policy: {
|
||||||
|
supportedApplications: ["FreeOTP", "Google Authenticator"],
|
||||||
|
algorithm: "HmacSHA1",
|
||||||
|
digits: 6,
|
||||||
|
lookAheadWindow: 1,
|
||||||
|
type: "totp",
|
||||||
|
period: 30,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
@ -44,8 +44,9 @@ export declare namespace keycloak_js {
|
|||||||
export function createKeycloakAdapter(params: {
|
export function createKeycloakAdapter(params: {
|
||||||
keycloakInstance: keycloak_js.KeycloakInstance;
|
keycloakInstance: keycloak_js.KeycloakInstance;
|
||||||
transformUrlBeforeRedirect(url: string): string;
|
transformUrlBeforeRedirect(url: string): string;
|
||||||
|
getRedirectMethod?: () => "overwrite location.href" | "location.replace";
|
||||||
}): keycloak_js.KeycloakAdapter {
|
}): keycloak_js.KeycloakAdapter {
|
||||||
const { keycloakInstance, transformUrlBeforeRedirect } = params;
|
const { keycloakInstance, transformUrlBeforeRedirect, getRedirectMethod = () => "overwrite location.href" } = params;
|
||||||
|
|
||||||
const neverResolvingPromise: keycloak_js.KeycloakPromise<void, void> = Object.defineProperties(new Promise(() => {}), {
|
const neverResolvingPromise: keycloak_js.KeycloakPromise<void, void> = Object.defineProperties(new Promise(() => {}), {
|
||||||
"success": { "value": () => {} },
|
"success": { "value": () => {} },
|
||||||
@ -54,25 +55,50 @@ export function createKeycloakAdapter(params: {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"login": options => {
|
"login": options => {
|
||||||
window.location.href = transformUrlBeforeRedirect(keycloakInstance.createLoginUrl(options));
|
const newHref = transformUrlBeforeRedirect(keycloakInstance.createLoginUrl(options));
|
||||||
|
switch (getRedirectMethod()) {
|
||||||
|
case "location.replace":
|
||||||
|
window.location.replace(newHref);
|
||||||
|
break;
|
||||||
|
case "overwrite location.href":
|
||||||
|
window.location.href = newHref;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return neverResolvingPromise;
|
||||||
|
},
|
||||||
|
"register": options => {
|
||||||
|
const newHref = transformUrlBeforeRedirect(keycloakInstance.createRegisterUrl(options));
|
||||||
|
switch (getRedirectMethod()) {
|
||||||
|
case "location.replace":
|
||||||
|
window.location.replace(newHref);
|
||||||
|
break;
|
||||||
|
case "overwrite location.href":
|
||||||
|
window.location.href = newHref;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return neverResolvingPromise;
|
return neverResolvingPromise;
|
||||||
},
|
},
|
||||||
"logout": options => {
|
"logout": options => {
|
||||||
window.location.replace(transformUrlBeforeRedirect(keycloakInstance.createLogoutUrl(options)));
|
window.location.replace(transformUrlBeforeRedirect(keycloakInstance.createLogoutUrl(options)));
|
||||||
return neverResolvingPromise;
|
return neverResolvingPromise;
|
||||||
},
|
},
|
||||||
"register": options => {
|
|
||||||
window.location.href = transformUrlBeforeRedirect(keycloakInstance.createRegisterUrl(options));
|
|
||||||
|
|
||||||
return neverResolvingPromise;
|
|
||||||
},
|
|
||||||
"accountManagement": () => {
|
"accountManagement": () => {
|
||||||
var accountUrl = transformUrlBeforeRedirect(keycloakInstance.createAccountUrl());
|
const accountUrl = transformUrlBeforeRedirect(keycloakInstance.createAccountUrl());
|
||||||
if (typeof accountUrl !== "undefined") {
|
|
||||||
window.location.href = accountUrl;
|
if (accountUrl === "undefined") {
|
||||||
} else {
|
|
||||||
throw new Error("Not supported by the OIDC server");
|
throw new Error("Not supported by the OIDC server");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (getRedirectMethod()) {
|
||||||
|
case "location.replace":
|
||||||
|
window.location.replace(accountUrl);
|
||||||
|
break;
|
||||||
|
case "overwrite location.href":
|
||||||
|
window.location.href = accountUrl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return neverResolvingPromise;
|
return neverResolvingPromise;
|
||||||
},
|
},
|
||||||
"redirectUri": options => {
|
"redirectUri": options => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user