feat: Addition of FederatedIdentity Account Page
This commit is contained in:
@ -3,6 +3,7 @@ import type { PageProps } from "keycloakify/account/pages/PageProps";
|
|||||||
import type { I18n } from "keycloakify/account/i18n";
|
import type { I18n } from "keycloakify/account/i18n";
|
||||||
import type { KcContext } from "./kcContext";
|
import type { KcContext } from "./kcContext";
|
||||||
import { assert, type Equals } from "tsafe/assert";
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
|
import FederatedIdentity from "./pages/FederatedIdentity";
|
||||||
|
|
||||||
const Password = lazy(() => import("keycloakify/account/pages/Password"));
|
const Password = lazy(() => import("keycloakify/account/pages/Password"));
|
||||||
const Account = lazy(() => import("keycloakify/account/pages/Account"));
|
const Account = lazy(() => import("keycloakify/account/pages/Account"));
|
||||||
@ -30,6 +31,8 @@ export default function Fallback(props: PageProps<KcContext, I18n>) {
|
|||||||
return <Applications kcContext={kcContext} {...rest} />;
|
return <Applications kcContext={kcContext} {...rest} />;
|
||||||
case "log.ftl":
|
case "log.ftl":
|
||||||
return <Log kcContext={kcContext} {...rest} />;
|
return <Log kcContext={kcContext} {...rest} />;
|
||||||
|
case "federatedIdentity.ftl":
|
||||||
|
return <FederatedIdentity kcContext={kcContext} {...rest} />;
|
||||||
}
|
}
|
||||||
assert<Equals<typeof kcContext, never>>(false);
|
assert<Equals<typeof kcContext, never>>(false);
|
||||||
})()}
|
})()}
|
||||||
|
@ -3,7 +3,14 @@ import { assert } from "tsafe/assert";
|
|||||||
import type { Equals } from "tsafe";
|
import type { Equals } from "tsafe";
|
||||||
import { type ThemeType } from "keycloakify/bin/constants";
|
import { type ThemeType } from "keycloakify/bin/constants";
|
||||||
|
|
||||||
export type KcContext = KcContext.Password | KcContext.Account | KcContext.Sessions | KcContext.Totp | KcContext.Applications | KcContext.Log;
|
export type KcContext =
|
||||||
|
| KcContext.Password
|
||||||
|
| KcContext.Account
|
||||||
|
| KcContext.Sessions
|
||||||
|
| KcContext.Totp
|
||||||
|
| KcContext.Applications
|
||||||
|
| KcContext.Log
|
||||||
|
| KcContext.FederatedIdentity;
|
||||||
|
|
||||||
export declare namespace KcContext {
|
export declare namespace KcContext {
|
||||||
export type Common = {
|
export type Common = {
|
||||||
@ -264,6 +271,20 @@ export declare namespace KcContext {
|
|||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type FederatedIdentity = Common & {
|
||||||
|
pageId: "federatedIdentity.ftl";
|
||||||
|
stateChecker: string;
|
||||||
|
federatedIdentity: {
|
||||||
|
identities: {
|
||||||
|
providerId: string;
|
||||||
|
displayName: string;
|
||||||
|
userName: string;
|
||||||
|
connected: boolean;
|
||||||
|
}[];
|
||||||
|
removeLinkPossible: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -240,5 +240,21 @@ export const kcContextMocks: KcContext[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
id<KcContext.FederatedIdentity>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"stateChecker": "",
|
||||||
|
"pageId": "federatedIdentity.ftl",
|
||||||
|
"federatedIdentity": {
|
||||||
|
"identities": [
|
||||||
|
{
|
||||||
|
"providerId": "keycloak-oidc",
|
||||||
|
"displayName": "keycloak-oidc",
|
||||||
|
"userName": "John",
|
||||||
|
"connected": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"removeLinkPossible": true
|
||||||
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
58
src/account/pages/FederatedIdentity.tsx
Normal file
58
src/account/pages/FederatedIdentity.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { PageProps } from "keycloakify/account";
|
||||||
|
import { I18n } from "keycloakify/account/i18n";
|
||||||
|
import { KcContext } from "keycloakify/account/kcContext";
|
||||||
|
|
||||||
|
export default function FederatedIdentity(props: PageProps<Extract<KcContext, { pageId: "federatedIdentity.ftl" }>, I18n>) {
|
||||||
|
const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
|
||||||
|
|
||||||
|
const { url, federatedIdentity, stateChecker } = kcContext;
|
||||||
|
const { msg } = i18n;
|
||||||
|
return (
|
||||||
|
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="federatedIdentity">
|
||||||
|
<div className="main-layout social">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-10">
|
||||||
|
<h2>{msg("federatedIdentitiesHtmlTitle")}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="federated-identities">
|
||||||
|
{federatedIdentity.identities.map(identity => (
|
||||||
|
<div key={identity.providerId} className="row margin-bottom">
|
||||||
|
<div className="col-sm-2 col-md-2">
|
||||||
|
<label htmlFor={identity.providerId} className="control-label">
|
||||||
|
{identity.displayName}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-5 col-md-5">
|
||||||
|
<input disabled className="form-control" value={identity.userName} />
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-5 col-md-5">
|
||||||
|
{identity.connected ? (
|
||||||
|
federatedIdentity.removeLinkPossible && (
|
||||||
|
<form action={url.socialUrl} method="post" className="form-inline">
|
||||||
|
<input type="hidden" name="stateChecker" value={stateChecker} />
|
||||||
|
<input type="hidden" name="action" value="remove" />
|
||||||
|
<input type="hidden" name="providerId" value={identity.providerId} />
|
||||||
|
<button id={`remove-link-${identity.providerId}`} className="btn btn-default">
|
||||||
|
{msg("doRemove")}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<form action={url.socialUrl} method="post" className="form-inline">
|
||||||
|
<input type="hidden" name="stateChecker" value={stateChecker} />
|
||||||
|
<input type="hidden" name="action" value="add" />
|
||||||
|
<input type="hidden" name="providerId" value={identity.providerId} />
|
||||||
|
<button id={`add-link-${identity.providerId}`} className="btn btn-default">
|
||||||
|
{msg("doAdd")}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Template>
|
||||||
|
);
|
||||||
|
}
|
@ -27,7 +27,15 @@ export const loginThemePageIds = [
|
|||||||
"saml-post-form.ftl"
|
"saml-post-form.ftl"
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const accountThemePageIds = ["password.ftl", "account.ftl", "sessions.ftl", "totp.ftl", "applications.ftl", "log.ftl"] as const;
|
export const accountThemePageIds = [
|
||||||
|
"password.ftl",
|
||||||
|
"account.ftl",
|
||||||
|
"sessions.ftl",
|
||||||
|
"totp.ftl",
|
||||||
|
"applications.ftl",
|
||||||
|
"log.ftl",
|
||||||
|
"federatedIdentity.ftl"
|
||||||
|
] as const;
|
||||||
|
|
||||||
export type LoginThemePageId = (typeof loginThemePageIds)[number];
|
export type LoginThemePageId = (typeof loginThemePageIds)[number];
|
||||||
export type AccountThemePageId = (typeof accountThemePageIds)[number];
|
export type AccountThemePageId = (typeof accountThemePageIds)[number];
|
||||||
|
34
stories/account/pages/FederatedIdentity.stories.tsx
Normal file
34
stories/account/pages/FederatedIdentity.stories.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Meta } from "@storybook/react";
|
||||||
|
import { createPageStory } from "../createPageStory";
|
||||||
|
|
||||||
|
const pageId = "federatedIdentity.ftl";
|
||||||
|
|
||||||
|
const { PageStory } = createPageStory({ pageId });
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: "account/FederatedIdentity",
|
||||||
|
component: PageStory
|
||||||
|
} satisfies Meta<typeof PageStory>;
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
export const Default = () => <PageStory />;
|
||||||
|
|
||||||
|
export const NotConnected = () => (
|
||||||
|
<PageStory
|
||||||
|
kcContext={{
|
||||||
|
pageId: "federatedIdentity.ftl",
|
||||||
|
federatedIdentity: {
|
||||||
|
identities: [
|
||||||
|
{
|
||||||
|
providerId: "google",
|
||||||
|
displayName: "keycloak-oidc",
|
||||||
|
connected: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
removeLinkPossible: true
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
Reference in New Issue
Block a user