Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions apps/kyc-demo/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# kyc-demo

## 0.1.82

### Patch Changes

- Updated dependencies [6b21fed]
- @slashid/react@1.36.0

## 0.1.81

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion apps/kyc-demo/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "kyc-demo",
"private": true,
"version": "0.1.81",
"version": "0.1.82",
"type": "module",
"scripts": {
"start": "vite",
Expand Down
8 changes: 8 additions & 0 deletions apps/react-nextjs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# react-nextjs

## 0.1.96

### Patch Changes

- Updated dependencies [6b21fed]
- @slashid/react@1.36.0
- demo-form@0.0.95

## 0.1.95

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion apps/react-nextjs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-nextjs",
"version": "0.1.95",
"version": "0.1.96",
"private": true,
"scripts": {
"dev": "next dev",
Expand Down
8 changes: 8 additions & 0 deletions apps/react-remix/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# react-remix

## 0.1.82

### Patch Changes

- Updated dependencies [6b21fed]
- @slashid/react@1.36.0
- demo-form@0.0.95

## 0.1.81

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion apps/react-remix/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-remix",
"version": "0.1.81",
"version": "0.1.82",
"private": true,
"sideEffects": false,
"scripts": {
Expand Down
8 changes: 8 additions & 0 deletions apps/react-vite/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# react-vite

## 0.0.102

### Patch Changes

- Updated dependencies [6b21fed]
- @slashid/react@1.36.0
- demo-form@0.0.95

## 0.0.101

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion apps/react-vite/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-vite",
"private": true,
"version": "0.0.101",
"version": "0.0.102",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
6 changes: 6 additions & 0 deletions apps/slashid-remix-impl/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# my-remix-app

## 0.0.39

### Patch Changes

- @slashid/remix@0.2.35

## 0.0.38

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion apps/slashid-remix-impl/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "slashid-remix-impl",
"private": true,
"version": "0.0.38",
"version": "0.0.39",
"sideEffects": false,
"type": "module",
"scripts": {
Expand Down
2 changes: 2 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
specs/
plans/
7 changes: 7 additions & 0 deletions packages/demo-form/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# demo-form

## 0.0.95

### Patch Changes

- Updated dependencies [6b21fed]
- @slashid/react@1.36.0

## 0.0.94

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/demo-form/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "demo-form",
"version": "0.0.94",
"version": "0.0.95",
"private": true,
"type": "module",
"module": "./dist/demo-form.es.js",
Expand Down
6 changes: 6 additions & 0 deletions packages/react/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @slashid/react

## 1.36.0

### Minor Changes

- 6b21fed: Add `provider.*` text keys so OIDC and SAML provider display names can be localized. The authenticating screen title now shows the same name as the initial screen button, including the factor `label`. The default name for `azuread` is now "Microsoft".

## 1.35.0

### Minor Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@slashid/react",
"version": "1.35.0",
"version": "1.36.0",
"private": false,
"publishConfig": {
"access": "public"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ export const promptResend = style({
gap: "4px",
});

export const oidcTitle = style({
textTransform: "capitalize",
});

export const otpForm = style({
margin: "16px 0",
});
Expand Down
13 changes: 6 additions & 7 deletions packages/react/src/components/form/authenticating/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import {
isFactorOTP,
isFactorPassword,
isFactorSSO,
isFactorTOTP,
} from "../../../domain/handles";
import { Text } from "../../text";
import { getProviderName } from "../../text/provider-name";
import { useConfiguration } from "../../../hooks/use-configuration";

import { getAuthenticatingMessage } from "./messages";
import { OTPState } from "./otp";
Expand Down Expand Up @@ -35,6 +38,7 @@ const DELAY_BEFORE_RETRY = TIME_MS.second * 30;

const LoadingState = ({ flowState }: Props) => {
const { factor, handle } = flowState.context.config;
const { text } = useConfiguration();
const { title, message, tokens } = getAuthenticatingMessage(factor, handle);
const [showPrompt, setShowPrompt] = useState(true);

Expand All @@ -53,13 +57,8 @@ const LoadingState = ({ flowState }: Props) => {
t={title}
variant={{ size: "2xl-title", weight: "bold" }}
>
{factor.method === "oidc" ? (
<span className={styles.oidcTitle}>
{factor.options?.provider as unknown as string}
</span>
) : undefined}
{factor.method === "saml" ? (
<span className={styles.oidcTitle}>SSO</span>
{isFactorSSO(factor) ? (
<span>{getProviderName(text, factor)}</span>
) : undefined}
</Text>
<AuthenticatingSubtitle />
Expand Down
146 changes: 146 additions & 0 deletions packages/react/src/components/form/initial/sso.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import {
AuthnContextUpdateChallengeReceivedEvent,
User,
} from "@slashid/slashid";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { vi, describe, test, expect } from "vitest";
import { Form } from "..";
import { TextConfig } from "../../text/constants";
import { createTestUser, MockSlashID } from "../../test-utils";
import { TestSlashIDProvider } from "../../../context/test-providers";
import { ConfigurationProvider } from "../../../context/config-context";
import { FactorConfiguration } from "../../../domain/types";

function renderForm(
factors: FactorConfiguration[],
text?: Partial<TextConfig>
) {
let resolveLogin: ((u: User) => void) | undefined;
const sid = new MockSlashID({ oid: "oid", analyticsEnabled: false });
const logIn = vi.fn(() => {
sid.mockPublish("authnContextUpdateChallengeReceivedEvent", {
targetOrgId: "oid",
factor: factors[0] as AuthnContextUpdateChallengeReceivedEvent["factor"],
});
return new Promise<User>((resolve) => {
resolveLogin = resolve;
});
});

render(
<TestSlashIDProvider sid={sid} sdkState="ready" logIn={logIn}>
<ConfigurationProvider factors={factors} text={text}>
<Form />
</ConfigurationProvider>
</TestSlashIDProvider>
);

return { finishLogin: () => resolveLogin?.(createTestUser()) };
}

async function clickAndReadAuthenticatingTitle(buttonName: string) {
const user = userEvent.setup();
await user.click(screen.getByRole("button", { name: buttonName }));
return screen.findByRole("heading", { level: 1 });
}

const github: FactorConfiguration = {
method: "oidc",
options: { provider: "github", client_id: "github" },
};

const saml: FactorConfiguration = {
method: "saml",
options: { provider_credentials_id: "saml-1" },
};

describe("SSO provider names", () => {
test("renders the default OIDC provider name on the initial button and the authenticating title", async () => {
const { finishLogin } = renderForm([github]);

expect(
screen.getByRole("button", { name: "Sign in with GitHub" })
).toBeInTheDocument();

const title = await clickAndReadAuthenticatingTitle("Sign in with GitHub");
expect(title).toHaveTextContent("Sign in with GitHub");

finishLogin();
});

test("renders an overridden OIDC provider name in both places", async () => {
const { finishLogin } = renderForm([github], {
"provider.github": "Octocat",
});

expect(
screen.getByRole("button", { name: "Sign in with Octocat" })
).toBeInTheDocument();

const title = await clickAndReadAuthenticatingTitle("Sign in with Octocat");
expect(title).toHaveTextContent("Sign in with Octocat");

finishLogin();
});

test("prefers the factor label over the overridden provider name", async () => {
const { finishLogin } = renderForm([{ ...github, label: "Work GitHub" }], {
"provider.github": "Octocat",
});

expect(
screen.getByRole("button", { name: "Sign in with Work GitHub" })
).toBeInTheDocument();

const title = await clickAndReadAuthenticatingTitle(
"Sign in with Work GitHub"
);
expect(title).toHaveTextContent("Sign in with Work GitHub");

finishLogin();
});

test("renders the default SAML name in both places", async () => {
const { finishLogin } = renderForm([saml]);

expect(
screen.getByRole("button", { name: "Sign in with SAML" })
).toBeInTheDocument();

const title = await clickAndReadAuthenticatingTitle("Sign in with SAML");
expect(title).toHaveTextContent("Sign in with SAML");

finishLogin();
});

test("renders an overridden SAML name in both places", async () => {
const { finishLogin } = renderForm([saml], { "provider.saml": "Corp SSO" });

expect(
screen.getByRole("button", { name: "Sign in with Corp SSO" })
).toBeInTheDocument();

const title = await clickAndReadAuthenticatingTitle(
"Sign in with Corp SSO"
);
expect(title).toHaveTextContent("Sign in with Corp SSO");

finishLogin();
});

test("prefers the SAML factor label over the overridden SAML name", async () => {
const { finishLogin } = renderForm([{ ...saml, label: "Acme" }], {
"provider.saml": "Corp SSO",
});

expect(
screen.getByRole("button", { name: "Sign in with Acme" })
).toBeInTheDocument();

const title = await clickAndReadAuthenticatingTitle("Sign in with Acme");
expect(title).toHaveTextContent("Sign in with Acme");

finishLogin();
});
});
19 changes: 3 additions & 16 deletions packages/react/src/components/form/initial/sso.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
FactorLabeledOIDC,
} from "../../../domain/types";
import { useConfiguration } from "../../../hooks/use-configuration";
import { getProviderName } from "../../text/provider-name";

import * as styles from "./initial.css";

Expand All @@ -34,18 +35,6 @@ const PROVIDER_TO_ICON: Record<OAuthProvider, React.ReactNode> = {
apple: <Apple />,
};

const PROVIDER_TO_PRETTY_NAME: Record<OAuthProvider, string> = {
google: "Google",
facebook: "Facebook",
github: "GitHub",
gitlab: "GitLab",
line: "LINE",
bitbucket: "Bitbucket",
azuread: "Azure AD",
okta: "Okta",
apple: "Apple",
};

export type SAMLProviderProps = {
provider: FactorCustomizableSAML;
handleClick: (factor: Factor) => void;
Expand All @@ -66,7 +55,7 @@ export function SAMLProvider({ provider: p, handleClick }: SAMLProviderProps) {
className={clsx("sid-saml--button")}
>
{text["initial.sso"]}
<span className={styles.ssoProvider}>{p.label || "SAML"}</span>
<span className={styles.ssoProvider}>{getProviderName(text, p)}</span>
</Button>
);
}
Expand Down Expand Up @@ -118,9 +107,7 @@ export function OIDCProvider({ provider: p, handleClick }: OIDCProviderProps) {
className={clsx("sid-oidc--button")}
>
{text["initial.oidc"]}
<span className={styles.oidcProvider}>
{p.label || PROVIDER_TO_PRETTY_NAME[p.options?.provider]}
</span>
<span className={styles.oidcProvider}>{getProviderName(text, p)}</span>
</Button>
);
}
Expand Down
Loading
Loading