@authara/react 0.0.1-trusted.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +381 -77
- package/dist/AppleButton.d.ts +24 -5
- package/dist/AppleButton.js +125 -7
- package/dist/GoogleButton.d.ts +17 -3
- package/dist/GoogleButton.js +162 -28
- package/dist/MicrosoftButton.d.ts +27 -0
- package/dist/MicrosoftButton.js +157 -0
- package/dist/actions.d.ts +18 -0
- package/dist/actions.js +137 -0
- package/dist/color-mode.d.ts +3 -0
- package/dist/color-mode.js +20 -0
- package/dist/context.d.ts +4 -3
- package/dist/context.js +4 -3
- package/dist/contract.d.ts +129 -2
- package/dist/errors.d.ts +34 -0
- package/dist/errors.js +104 -0
- package/dist/generated-error-codes.d.ts +8 -0
- package/dist/generated-error-codes.js +42 -0
- package/dist/index.d.ts +8 -5
- package/dist/index.js +5 -3
- package/dist/provider-sdks.d.ts +20 -15
- package/dist/provider-sdks.js +6 -3
- package/dist/services.d.ts +17 -7
- package/dist/services.js +97 -41
- package/package.json +17 -7
package/dist/AppleButton.js
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
|
-
import { Fragment as _Fragment,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState, } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveAction, runAppleAction } from "./actions.js";
|
|
4
|
+
import { isAppleCancellation, isProviderUnavailable, toAutharaButtonError, toError, } from "./errors.js";
|
|
4
5
|
import { useAutharaServices } from "./context.js";
|
|
5
|
-
|
|
6
|
+
import { useResolvedAutharaColorMode, } from "./color-mode.js";
|
|
7
|
+
function appleButtonText(type) {
|
|
8
|
+
switch (type) {
|
|
9
|
+
case "sign-in":
|
|
10
|
+
return "Sign in with Apple";
|
|
11
|
+
case "sign-up":
|
|
12
|
+
return "Sign up with Apple";
|
|
13
|
+
case "continue":
|
|
14
|
+
return "Continue with Apple";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function AppleLogo() {
|
|
18
|
+
return (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0 814 1000", width: "16", height: "20", focusable: "false", children: _jsx("path", { fill: "currentColor", d: "M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2zm-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3z" }) }));
|
|
19
|
+
}
|
|
20
|
+
export function AppleCredentialButton({ disabled = false, locale = "en_US", color = "black", border = false, buttonText, type = "continue", mode = "center-align", borderRadius = 12, fallback = null, onAuthorization, onCancel, onError, onUnavailable, style, ...divProps }) {
|
|
21
|
+
if (!Number.isFinite(borderRadius) || borderRadius < 0 || borderRadius > 50) {
|
|
22
|
+
throw new RangeError("Apple button borderRadius must be between 0 and 50.");
|
|
23
|
+
}
|
|
6
24
|
const { apple } = useAutharaServices();
|
|
7
25
|
const targetRef = useRef(null);
|
|
8
26
|
const authorizationRef = useRef(onAuthorization);
|
|
@@ -40,7 +58,7 @@ export function AppleButton({ disabled = false, locale = "en_US", color = "black
|
|
|
40
58
|
return () => {
|
|
41
59
|
active = false;
|
|
42
60
|
};
|
|
43
|
-
}, [apple, border, color, locale, mode, type]);
|
|
61
|
+
}, [apple, border, borderRadius, color, locale, mode, type]);
|
|
44
62
|
const beginAuthorization = (event) => {
|
|
45
63
|
event.preventDefault();
|
|
46
64
|
event.stopPropagation();
|
|
@@ -62,10 +80,110 @@ export function AppleButton({ disabled = false, locale = "en_US", color = "black
|
|
|
62
80
|
if (!available)
|
|
63
81
|
return _jsx(_Fragment, { children: fallback });
|
|
64
82
|
const inactive = disabled || busy;
|
|
65
|
-
|
|
83
|
+
const label = buttonText ?? appleButtonText(type);
|
|
84
|
+
return (_jsxs("div", { ...divProps, "aria-busy": busy || undefined, "aria-disabled": inactive || undefined, "data-authara-provider": "apple", "data-authara-ready": ready ? "true" : "false", onClickCapture: beginAuthorization, style: {
|
|
85
|
+
position: "relative",
|
|
86
|
+
display: "inline-block",
|
|
66
87
|
width: "100%",
|
|
67
88
|
height: 44,
|
|
89
|
+
overflow: "hidden",
|
|
90
|
+
borderRadius,
|
|
91
|
+
cursor: !ready ? "wait" : inactive ? "not-allowed" : "pointer",
|
|
92
|
+
lineHeight: 0,
|
|
93
|
+
opacity: inactive ? 0.62 : 1,
|
|
94
|
+
transition: "opacity 150ms ease",
|
|
95
|
+
verticalAlign: "middle",
|
|
96
|
+
WebkitTapHighlightColor: "transparent",
|
|
97
|
+
...style,
|
|
98
|
+
pointerEvents: !ready || inactive ? "none" : style?.pointerEvents,
|
|
99
|
+
}, children: [_jsx("div", { ref: targetRef, "aria-hidden": "true", "data-color": color, "data-border": String(border), "data-border-radius": borderRadius, "data-width": "100%", "data-height": "100%", "data-type": type, "data-mode": mode, style: {
|
|
100
|
+
position: "absolute",
|
|
101
|
+
inset: 0,
|
|
102
|
+
overflow: "hidden",
|
|
103
|
+
borderRadius,
|
|
104
|
+
opacity: 0,
|
|
105
|
+
pointerEvents: "none",
|
|
106
|
+
} }), _jsxs("button", { type: "button", disabled: !ready || inactive, "aria-label": mode === "logo-only" ? label : undefined, "data-authara-apple-presentation": true, style: {
|
|
107
|
+
position: "absolute",
|
|
108
|
+
inset: 0,
|
|
109
|
+
display: "flex",
|
|
110
|
+
width: "100%",
|
|
111
|
+
height: "100%",
|
|
112
|
+
alignItems: "center",
|
|
113
|
+
justifyContent: "center",
|
|
114
|
+
gap: mode === "logo-only" || mode === "left-align" ? 0 : 10,
|
|
115
|
+
padding: mode === "logo-only" ? 0 : "0 14px",
|
|
116
|
+
overflow: "hidden",
|
|
117
|
+
border: "1px solid",
|
|
118
|
+
borderColor: border
|
|
119
|
+
? color === "black"
|
|
120
|
+
? "#000"
|
|
121
|
+
: "#9aa0a6"
|
|
122
|
+
: "transparent",
|
|
123
|
+
borderRadius,
|
|
124
|
+
background: color === "black" ? "#000" : "#fff",
|
|
125
|
+
boxShadow: "none",
|
|
126
|
+
color: color === "black" ? "#fff" : "#202124",
|
|
127
|
+
cursor: inactive ? "not-allowed" : ready ? "pointer" : "wait",
|
|
128
|
+
fontFamily: "Arial, sans-serif",
|
|
129
|
+
fontSize: 14,
|
|
130
|
+
fontWeight: 500,
|
|
131
|
+
lineHeight: 1,
|
|
132
|
+
whiteSpace: "nowrap",
|
|
133
|
+
}, children: [_jsx("span", { "aria-hidden": "true", style: {
|
|
134
|
+
display: "inline-flex",
|
|
135
|
+
width: 20,
|
|
136
|
+
height: 20,
|
|
137
|
+
flex: "0 0 20px",
|
|
138
|
+
alignItems: "center",
|
|
139
|
+
justifyContent: "center",
|
|
140
|
+
position: mode === "left-align" ? "absolute" : "static",
|
|
141
|
+
left: mode === "left-align" ? 14 : undefined,
|
|
142
|
+
}, children: _jsx(AppleLogo, {}) }), mode !== "logo-only" && _jsx("span", { children: label })] })] }));
|
|
143
|
+
}
|
|
144
|
+
const appleButtonHeights = {
|
|
145
|
+
small: 30,
|
|
146
|
+
medium: 36,
|
|
147
|
+
large: 44,
|
|
148
|
+
};
|
|
149
|
+
const appleIconButtonSizes = {
|
|
150
|
+
small: 36,
|
|
151
|
+
medium: 44,
|
|
152
|
+
large: 52,
|
|
153
|
+
};
|
|
154
|
+
function appleType(label) {
|
|
155
|
+
return label;
|
|
156
|
+
}
|
|
157
|
+
export function AppleButton(props) {
|
|
158
|
+
const { client } = useAutharaServices();
|
|
159
|
+
const internal = props;
|
|
160
|
+
const { action = "login", audience, authenticationChallengeId, recoveryLinkId, onSuccess, onError, onCancel, variant = "text", size = "large", label = action === "login" ? "sign-in" : "continue", align = "center", mode = "light", width, color, borderRadius, style, ...buttonProps } = internal;
|
|
161
|
+
const height = variant === "icon" ? appleIconButtonSizes[size] : appleButtonHeights[size];
|
|
162
|
+
const resolvedMode = useResolvedAutharaColorMode(mode);
|
|
163
|
+
return (_jsx(AppleCredentialButton, { ...buttonProps, color: color ?? (resolvedMode === "dark" ? "white" : "black"), type: appleType(label), mode: variant === "icon" ? "logo-only" : `${align}-align`, borderRadius: borderRadius, style: {
|
|
164
|
+
width: width ?? (variant === "icon" ? height : "100%"),
|
|
165
|
+
height,
|
|
68
166
|
...style,
|
|
69
|
-
|
|
70
|
-
|
|
167
|
+
}, onAuthorization: async (proof) => {
|
|
168
|
+
let result;
|
|
169
|
+
let selected;
|
|
170
|
+
try {
|
|
171
|
+
selected = resolveAction(action, audience, authenticationChallengeId, recoveryLinkId);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
throw toAutharaButtonError(error, "The Apple button action is not configured correctly.", "configuration");
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
result = await runAppleAction(client, selected, proof);
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
throw toAutharaButtonError(error, `Apple ${action} failed.`, "authara");
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
await onSuccess?.(result);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
throw toAutharaButtonError(error, "The Apple success callback failed.", "consumer");
|
|
187
|
+
}
|
|
188
|
+
}, onCancel: onCancel, onError: (error) => onError?.(toAutharaButtonError(error, "Apple authentication failed.", "provider")) }));
|
|
71
189
|
}
|
package/dist/GoogleButton.d.ts
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import { type ComponentPropsWithoutRef, type ReactNode } from "react";
|
|
2
|
-
import type { MaybePromise } from "./contract.js";
|
|
2
|
+
import type { AutharaActionProps, MaybePromise } from "./contract.js";
|
|
3
3
|
import type { GoogleButtonConfiguration } from "./provider-sdks.js";
|
|
4
4
|
import type { GoogleCredential } from "./services.js";
|
|
5
|
+
import { type AutharaColorMode } from "./color-mode.js";
|
|
5
6
|
type GoogleAppearance = Pick<GoogleButtonConfiguration, "type" | "theme" | "size" | "text" | "shape" | "logo_alignment" | "width">;
|
|
6
|
-
export interface
|
|
7
|
+
export interface GoogleCredentialButtonProps extends Omit<ComponentPropsWithoutRef<"button">, "children" | "onError" | "size" | "type">, GoogleAppearance {
|
|
7
8
|
disabled?: boolean;
|
|
9
|
+
border?: boolean;
|
|
10
|
+
buttonText?: string;
|
|
8
11
|
locale?: string;
|
|
9
12
|
fallback?: ReactNode;
|
|
10
13
|
onCredential: (credential: GoogleCredential) => MaybePromise<void>;
|
|
11
14
|
onError?: (error: Error) => void;
|
|
12
15
|
onUnavailable?: () => void;
|
|
13
16
|
}
|
|
14
|
-
export declare function
|
|
17
|
+
export declare function GoogleCredentialButton({ disabled, border, buttonText, locale, fallback, onCredential, onError, onUnavailable, type, theme, size, text, shape, logo_alignment, width, style, onClick, ...buttonProps }: GoogleCredentialButtonProps): React.JSX.Element | null;
|
|
18
|
+
export type GoogleButtonVariant = "icon" | "text";
|
|
19
|
+
export type GoogleButtonLabel = "continue" | "sign-in" | "sign-up";
|
|
20
|
+
type GoogleButtonVisualProps = Omit<GoogleCredentialButtonProps, "logo_alignment" | "onCredential" | "onError" | "size" | "text" | "type"> & {
|
|
21
|
+
variant?: GoogleButtonVariant;
|
|
22
|
+
size?: "small" | "medium" | "large";
|
|
23
|
+
label?: GoogleButtonLabel;
|
|
24
|
+
logoAlignment?: "left" | "center";
|
|
25
|
+
mode?: AutharaColorMode;
|
|
26
|
+
};
|
|
27
|
+
export type GoogleButtonProps = GoogleButtonVisualProps & AutharaActionProps<"google">;
|
|
28
|
+
export declare function GoogleButton(props: GoogleButtonProps): React.JSX.Element | null;
|
|
15
29
|
export {};
|
package/dist/GoogleButton.js
CHANGED
|
@@ -1,10 +1,64 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState, } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveAction, runGoogleAction } from "./actions.js";
|
|
4
|
+
import { isProviderUnavailable, toAutharaButtonError, toError, } from "./errors.js";
|
|
4
5
|
import { useAutharaServices } from "./context.js";
|
|
5
|
-
|
|
6
|
+
import { useResolvedAutharaColorMode, } from "./color-mode.js";
|
|
7
|
+
const googleButtonHeights = {
|
|
8
|
+
small: 30,
|
|
9
|
+
medium: 36,
|
|
10
|
+
large: 44,
|
|
11
|
+
};
|
|
12
|
+
const googleIconButtonSizes = {
|
|
13
|
+
small: 36,
|
|
14
|
+
medium: 44,
|
|
15
|
+
large: 52,
|
|
16
|
+
};
|
|
17
|
+
function googleButtonText(text) {
|
|
18
|
+
switch (text) {
|
|
19
|
+
case "signin":
|
|
20
|
+
case "signin_with":
|
|
21
|
+
return "Sign in with Google";
|
|
22
|
+
case "signup_with":
|
|
23
|
+
return "Sign up with Google";
|
|
24
|
+
case "continue_with":
|
|
25
|
+
return "Continue with Google";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function googleButtonRadius(shape, height) {
|
|
29
|
+
switch (shape) {
|
|
30
|
+
case "circle":
|
|
31
|
+
case "pill":
|
|
32
|
+
return height / 2;
|
|
33
|
+
case "square":
|
|
34
|
+
return 6;
|
|
35
|
+
case "rectangular":
|
|
36
|
+
return 12;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function googleButtonColors(theme) {
|
|
40
|
+
switch (theme) {
|
|
41
|
+
case "light":
|
|
42
|
+
return { background: "#fff", borderColor: "#747775", color: "#1f1f1f" };
|
|
43
|
+
case "dark":
|
|
44
|
+
return {
|
|
45
|
+
background: "#131314",
|
|
46
|
+
borderColor: "#8e918f",
|
|
47
|
+
color: "#e3e3e3",
|
|
48
|
+
};
|
|
49
|
+
case "neutral":
|
|
50
|
+
return {
|
|
51
|
+
background: "#f2f2f2",
|
|
52
|
+
borderColor: "transparent",
|
|
53
|
+
color: "#1f1f1f",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function GoogleLogo() {
|
|
58
|
+
return (_jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 18 18", width: "18", height: "18", focusable: "false", children: [_jsx("path", { fill: "#4285F4", d: "M17.64 9.205c0-.638-.057-1.252-.164-1.841H9v3.481h4.844a4.14 4.14 0 0 1-1.797 2.715v2.258h2.909c1.702-1.567 2.684-3.875 2.684-6.613Z" }), _jsx("path", { fill: "#34A853", d: "M9 18c2.43 0 4.468-.806 5.956-2.182l-2.91-2.258c-.805.54-1.835.86-3.046.86-2.344 0-4.328-1.585-5.037-3.714H.956v2.332A9 9 0 0 0 9 18Z" }), _jsx("path", { fill: "#FBBC05", d: "M3.963 10.706A5.41 5.41 0 0 1 3.682 9c0-.592.102-1.168.281-1.706V4.962H.956A9 9 0 0 0 0 9c0 1.452.347 2.827.956 4.038l3.007-2.332Z" }), _jsx("path", { fill: "#EA4335", d: "M9 3.58c1.321 0 2.507.454 3.44 1.345l2.581-2.58C13.464.892 11.426 0 9 0A9 9 0 0 0 .956 4.962l3.007 2.332C4.672 5.165 6.656 3.58 9 3.58Z" })] }));
|
|
59
|
+
}
|
|
60
|
+
export function GoogleCredentialButton({ disabled = false, border, buttonText, locale, fallback = null, onCredential, onError, onUnavailable, type = "standard", theme = "light", size = "large", text = "continue_with", shape = "rectangular", logo_alignment = "center", width, style, onClick, ...buttonProps }) {
|
|
6
61
|
const { google } = useAutharaServices();
|
|
7
|
-
const targetRef = useRef(null);
|
|
8
62
|
const credentialRef = useRef(onCredential);
|
|
9
63
|
const errorRef = useRef(onError);
|
|
10
64
|
const unavailableRef = useRef(onUnavailable);
|
|
@@ -12,41 +66,34 @@ export function GoogleButton({ disabled = false, locale, fallback = null, onCred
|
|
|
12
66
|
const [available, setAvailable] = useState(true);
|
|
13
67
|
const [ready, setReady] = useState(false);
|
|
14
68
|
const [busy, setBusy] = useState(false);
|
|
69
|
+
const [requestCode, setRequestCode] = useState();
|
|
15
70
|
credentialRef.current = onCredential;
|
|
16
71
|
errorRef.current = onError;
|
|
17
72
|
unavailableRef.current = onUnavailable;
|
|
18
73
|
disabledRef.current = disabled;
|
|
19
74
|
useEffect(() => {
|
|
20
|
-
const element = targetRef.current;
|
|
21
|
-
if (!element)
|
|
22
|
-
return;
|
|
23
75
|
let active = true;
|
|
24
|
-
let dispose;
|
|
25
76
|
void google
|
|
26
|
-
.
|
|
27
|
-
type,
|
|
28
|
-
theme,
|
|
29
|
-
size,
|
|
30
|
-
text,
|
|
31
|
-
shape,
|
|
32
|
-
logo_alignment,
|
|
33
|
-
width: width ?? Math.min(element.clientWidth || 320, 400),
|
|
34
|
-
}, locale, (credential) => {
|
|
77
|
+
.prepare(locale, (credential) => {
|
|
35
78
|
if (!active || disabledRef.current)
|
|
36
79
|
return;
|
|
37
80
|
setBusy(true);
|
|
38
|
-
void Promise.resolve(
|
|
81
|
+
void Promise.resolve()
|
|
82
|
+
.then(() => credentialRef.current(credential))
|
|
39
83
|
.catch((error) => errorRef.current?.(toError(error, "Google authentication failed.")))
|
|
40
84
|
.finally(() => {
|
|
41
85
|
if (active)
|
|
42
86
|
setBusy(false);
|
|
43
87
|
});
|
|
44
|
-
})
|
|
45
|
-
.then((cleanup) => {
|
|
88
|
+
}, (error) => {
|
|
46
89
|
if (!active)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
90
|
+
return;
|
|
91
|
+
setBusy(false);
|
|
92
|
+
errorRef.current?.(error);
|
|
93
|
+
})
|
|
94
|
+
.then((trigger) => {
|
|
95
|
+
if (active) {
|
|
96
|
+
setRequestCode(() => trigger);
|
|
50
97
|
setReady(true);
|
|
51
98
|
}
|
|
52
99
|
})
|
|
@@ -62,14 +109,101 @@ export function GoogleButton({ disabled = false, locale, fallback = null, onCred
|
|
|
62
109
|
});
|
|
63
110
|
return () => {
|
|
64
111
|
active = false;
|
|
65
|
-
dispose?.();
|
|
66
112
|
};
|
|
67
|
-
}, [google, locale
|
|
113
|
+
}, [google, locale]);
|
|
68
114
|
if (!available)
|
|
69
115
|
return _jsx(_Fragment, { children: fallback });
|
|
70
116
|
const inactive = disabled || busy;
|
|
71
|
-
|
|
117
|
+
const iconOnly = type === "icon";
|
|
118
|
+
const height = iconOnly
|
|
119
|
+
? googleIconButtonSizes[size]
|
|
120
|
+
: googleButtonHeights[size];
|
|
121
|
+
const colors = googleButtonColors(theme);
|
|
122
|
+
const radius = googleButtonRadius(shape, height);
|
|
123
|
+
const label = buttonText ?? googleButtonText(text);
|
|
124
|
+
const showBorder = border ?? theme !== "neutral";
|
|
125
|
+
return (_jsxs("button", { ...buttonProps, type: "button", disabled: !ready || inactive, "aria-busy": busy || undefined, "aria-label": buttonProps["aria-label"] ?? (iconOnly ? label : undefined), "data-authara-provider": "google", "data-authara-ready": ready ? "true" : "false", "data-authara-google-presentation": true, "data-border": String(showBorder), onClick: (event) => {
|
|
126
|
+
onClick?.(event);
|
|
127
|
+
if (event.defaultPrevented || !requestCode)
|
|
128
|
+
return;
|
|
129
|
+
setBusy(true);
|
|
130
|
+
requestCode();
|
|
131
|
+
}, style: {
|
|
132
|
+
position: "relative",
|
|
133
|
+
display: "inline-flex",
|
|
134
|
+
width: width ?? (iconOnly ? height : "100%"),
|
|
135
|
+
height,
|
|
136
|
+
alignItems: "center",
|
|
137
|
+
justifyContent: "center",
|
|
138
|
+
gap: iconOnly || logo_alignment === "left" ? 0 : 10,
|
|
139
|
+
padding: iconOnly ? 0 : "0 12px",
|
|
140
|
+
overflow: "hidden",
|
|
141
|
+
border: "1px solid",
|
|
142
|
+
borderRadius: radius,
|
|
143
|
+
boxSizing: "border-box",
|
|
144
|
+
boxShadow: "none",
|
|
145
|
+
cursor: !ready ? "wait" : inactive ? "not-allowed" : "pointer",
|
|
146
|
+
fontFamily: '"Google Sans", Roboto, Arial, sans-serif',
|
|
147
|
+
fontSize: 14,
|
|
148
|
+
fontWeight: 500,
|
|
149
|
+
lineHeight: "20px",
|
|
150
|
+
whiteSpace: "nowrap",
|
|
151
|
+
...colors,
|
|
152
|
+
borderColor: showBorder ? colors.borderColor : "transparent",
|
|
153
|
+
outlineOffset: 2,
|
|
154
|
+
opacity: inactive ? 0.62 : 1,
|
|
155
|
+
transition: "opacity 150ms ease",
|
|
156
|
+
verticalAlign: "middle",
|
|
157
|
+
WebkitTapHighlightColor: "transparent",
|
|
72
158
|
...style,
|
|
73
|
-
|
|
74
|
-
|
|
159
|
+
}, children: [_jsx("span", { "aria-hidden": "true", style: {
|
|
160
|
+
display: "inline-flex",
|
|
161
|
+
width: 20,
|
|
162
|
+
height: 20,
|
|
163
|
+
flex: "0 0 20px",
|
|
164
|
+
alignItems: "center",
|
|
165
|
+
justifyContent: "center",
|
|
166
|
+
background: "#fff",
|
|
167
|
+
borderRadius: 2,
|
|
168
|
+
position: !iconOnly && logo_alignment === "left" ? "absolute" : "static",
|
|
169
|
+
left: !iconOnly && logo_alignment === "left" ? 12 : undefined,
|
|
170
|
+
}, children: _jsx(GoogleLogo, {}) }), !iconOnly && _jsx("span", { children: label })] }));
|
|
171
|
+
}
|
|
172
|
+
function googleText(label) {
|
|
173
|
+
switch (label) {
|
|
174
|
+
case "sign-in":
|
|
175
|
+
return "signin_with";
|
|
176
|
+
case "sign-up":
|
|
177
|
+
return "signup_with";
|
|
178
|
+
case "continue":
|
|
179
|
+
return "continue_with";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export function GoogleButton(props) {
|
|
183
|
+
const { client } = useAutharaServices();
|
|
184
|
+
const internal = props;
|
|
185
|
+
const { action = "login", audience, authenticationChallengeId, recoveryLinkId, onSuccess, onError, variant = "text", size = "large", label = action === "login" ? "sign-in" : "continue", logoAlignment = "center", mode = "light", border, shape, theme, ...buttonProps } = internal;
|
|
186
|
+
const resolvedMode = useResolvedAutharaColorMode(mode);
|
|
187
|
+
return (_jsx(GoogleCredentialButton, { ...buttonProps, border: border, theme: theme ?? resolvedMode, type: variant === "icon" ? "icon" : "standard", size: size, text: googleText(label), shape: shape ?? "rectangular", logo_alignment: logoAlignment, onCredential: async (proof) => {
|
|
188
|
+
let result;
|
|
189
|
+
let selected;
|
|
190
|
+
try {
|
|
191
|
+
selected = resolveAction(action, audience, authenticationChallengeId, recoveryLinkId);
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
throw toAutharaButtonError(error, "The Google button action is not configured correctly.", "configuration");
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
result = await runGoogleAction(client, selected, proof);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
throw toAutharaButtonError(error, `Google ${action} failed.`, "authara");
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
await onSuccess?.(result);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
throw toAutharaButtonError(error, "The Google success callback failed.", "consumer");
|
|
207
|
+
}
|
|
208
|
+
}, onError: (error) => onError?.(toAutharaButtonError(error, "Google authentication failed.", "provider")) }));
|
|
75
209
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ComponentPropsWithoutRef, type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
import { type AutharaActionProps, type MaybePromise } from "./contract.js";
|
|
3
|
+
import type { MicrosoftCredential } from "./services.js";
|
|
4
|
+
import { type AutharaColorMode } from "./color-mode.js";
|
|
5
|
+
export type MicrosoftButtonVariant = "icon" | "text";
|
|
6
|
+
export type MicrosoftButtonLabel = "continue" | "sign-in" | "sign-up";
|
|
7
|
+
export interface MicrosoftCredentialButtonProps extends Omit<ComponentPropsWithoutRef<"button">, "children" | "onError" | "size" | "type"> {
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
variant?: MicrosoftButtonVariant;
|
|
10
|
+
size?: "small" | "medium" | "large";
|
|
11
|
+
label?: MicrosoftButtonLabel;
|
|
12
|
+
buttonText?: string;
|
|
13
|
+
align?: "center" | "left";
|
|
14
|
+
mode?: AutharaColorMode;
|
|
15
|
+
border?: boolean;
|
|
16
|
+
width?: CSSProperties["width"];
|
|
17
|
+
fallback?: ReactNode;
|
|
18
|
+
onCredential: (credential: MicrosoftCredential) => MaybePromise<void>;
|
|
19
|
+
onCancel?: () => void;
|
|
20
|
+
onError?: (error: Error) => void;
|
|
21
|
+
onUnavailable?: () => void;
|
|
22
|
+
}
|
|
23
|
+
export declare function MicrosoftCredentialButton({ disabled, variant, size, label, buttonText, align, mode, border, width, fallback, onCredential, onCancel, onError, onUnavailable, style, onClick, ...buttonProps }: MicrosoftCredentialButtonProps): React.JSX.Element | null;
|
|
24
|
+
type MicrosoftButtonVisualProps = Omit<MicrosoftCredentialButtonProps, "onCredential" | "onError">;
|
|
25
|
+
export type MicrosoftButtonProps = MicrosoftButtonVisualProps & AutharaActionProps<"microsoft">;
|
|
26
|
+
export declare function MicrosoftButton(props: MicrosoftButtonProps): React.JSX.Element | null;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState, } from "react";
|
|
3
|
+
import { resolveAction, runMicrosoftAction } from "./actions.js";
|
|
4
|
+
import { useAutharaServices } from "./context.js";
|
|
5
|
+
import { isMicrosoftCancellation, isProviderUnavailable, toAutharaButtonError, toError, } from "./errors.js";
|
|
6
|
+
import { useResolvedAutharaColorMode, } from "./color-mode.js";
|
|
7
|
+
const textButtonHeights = { small: 30, medium: 36, large: 44 };
|
|
8
|
+
const iconButtonSizes = { small: 36, medium: 44, large: 52 };
|
|
9
|
+
function buttonLabel(label) {
|
|
10
|
+
switch (label) {
|
|
11
|
+
case "sign-in":
|
|
12
|
+
return "Sign in with Microsoft";
|
|
13
|
+
case "sign-up":
|
|
14
|
+
return "Sign up with Microsoft";
|
|
15
|
+
case "continue":
|
|
16
|
+
return "Continue with Microsoft";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function MicrosoftLogo() {
|
|
20
|
+
return (_jsxs("span", { "aria-hidden": "true", style: {
|
|
21
|
+
display: "grid",
|
|
22
|
+
width: 18,
|
|
23
|
+
height: 18,
|
|
24
|
+
flex: "0 0 18px",
|
|
25
|
+
gridTemplateColumns: "repeat(2, 1fr)",
|
|
26
|
+
gridTemplateRows: "repeat(2, 1fr)",
|
|
27
|
+
gap: 1,
|
|
28
|
+
}, children: [_jsx("span", { style: { background: "#f25022" } }), _jsx("span", { style: { background: "#7fba00" } }), _jsx("span", { style: { background: "#00a4ef" } }), _jsx("span", { style: { background: "#ffb900" } })] }));
|
|
29
|
+
}
|
|
30
|
+
export function MicrosoftCredentialButton({ disabled = false, variant = "text", size = "large", label = "continue", buttonText, align = "center", mode = "light", border = true, width, fallback = null, onCredential, onCancel, onError, onUnavailable, style, onClick, ...buttonProps }) {
|
|
31
|
+
const { microsoft } = useAutharaServices();
|
|
32
|
+
const credentialRef = useRef(onCredential);
|
|
33
|
+
const cancelRef = useRef(onCancel);
|
|
34
|
+
const errorRef = useRef(onError);
|
|
35
|
+
const unavailableRef = useRef(onUnavailable);
|
|
36
|
+
const [available, setAvailable] = useState(true);
|
|
37
|
+
const [ready, setReady] = useState(false);
|
|
38
|
+
const [busy, setBusy] = useState(false);
|
|
39
|
+
const resolvedMode = useResolvedAutharaColorMode(mode);
|
|
40
|
+
credentialRef.current = onCredential;
|
|
41
|
+
cancelRef.current = onCancel;
|
|
42
|
+
errorRef.current = onError;
|
|
43
|
+
unavailableRef.current = onUnavailable;
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
let active = true;
|
|
46
|
+
void microsoft
|
|
47
|
+
.prepare()
|
|
48
|
+
.then(() => {
|
|
49
|
+
if (active)
|
|
50
|
+
setReady(true);
|
|
51
|
+
})
|
|
52
|
+
.catch((error) => {
|
|
53
|
+
if (!active)
|
|
54
|
+
return;
|
|
55
|
+
if (isProviderUnavailable(error)) {
|
|
56
|
+
setAvailable(false);
|
|
57
|
+
unavailableRef.current?.();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
errorRef.current?.(toError(error, "Microsoft authentication is unavailable."));
|
|
61
|
+
});
|
|
62
|
+
return () => {
|
|
63
|
+
active = false;
|
|
64
|
+
};
|
|
65
|
+
}, [microsoft]);
|
|
66
|
+
if (!available)
|
|
67
|
+
return _jsx(_Fragment, { children: fallback });
|
|
68
|
+
const iconOnly = variant === "icon";
|
|
69
|
+
const height = iconOnly ? iconButtonSizes[size] : textButtonHeights[size];
|
|
70
|
+
const text = buttonText ?? buttonLabel(label);
|
|
71
|
+
const inactive = disabled || busy;
|
|
72
|
+
const dark = resolvedMode === "dark";
|
|
73
|
+
return (_jsxs("button", { ...buttonProps, type: "button", disabled: !ready || inactive, "aria-busy": busy || undefined, "aria-label": iconOnly ? text : buttonProps["aria-label"], "data-authara-provider": "microsoft", "data-authara-ready": ready ? "true" : "false", onClick: (event) => {
|
|
74
|
+
onClick?.(event);
|
|
75
|
+
if (event.defaultPrevented || !ready || inactive)
|
|
76
|
+
return;
|
|
77
|
+
setReady(false);
|
|
78
|
+
setBusy(true);
|
|
79
|
+
const authorization = microsoft.authorize();
|
|
80
|
+
void authorization
|
|
81
|
+
.then((proof) => credentialRef.current(proof))
|
|
82
|
+
.catch((error) => {
|
|
83
|
+
if (isMicrosoftCancellation(error)) {
|
|
84
|
+
cancelRef.current?.();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
errorRef.current?.(toError(error, "Microsoft authentication failed."));
|
|
88
|
+
})
|
|
89
|
+
.finally(() => {
|
|
90
|
+
setBusy(false);
|
|
91
|
+
void microsoft
|
|
92
|
+
.prepare()
|
|
93
|
+
.then(() => setReady(true))
|
|
94
|
+
.catch((error) => {
|
|
95
|
+
errorRef.current?.(toError(error, "Microsoft authentication is unavailable."));
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}, style: {
|
|
99
|
+
position: "relative",
|
|
100
|
+
display: "inline-flex",
|
|
101
|
+
width: width ?? (iconOnly ? height : "100%"),
|
|
102
|
+
height,
|
|
103
|
+
alignItems: "center",
|
|
104
|
+
justifyContent: "center",
|
|
105
|
+
gap: iconOnly || align === "left" ? 0 : 10,
|
|
106
|
+
padding: iconOnly ? 0 : "0 12px",
|
|
107
|
+
border: "1px solid",
|
|
108
|
+
borderColor: border ? (dark ? "#8e918f" : "#8c8c8c") : "transparent",
|
|
109
|
+
borderRadius: 12,
|
|
110
|
+
boxSizing: "border-box",
|
|
111
|
+
background: dark ? "#2f2f2f" : "#fff",
|
|
112
|
+
color: dark ? "#fff" : "#1f1f1f",
|
|
113
|
+
cursor: !ready ? "wait" : inactive ? "not-allowed" : "pointer",
|
|
114
|
+
fontFamily: '"Segoe UI", Arial, sans-serif',
|
|
115
|
+
fontSize: 14,
|
|
116
|
+
fontWeight: 600,
|
|
117
|
+
lineHeight: "20px",
|
|
118
|
+
opacity: inactive ? 0.62 : 1,
|
|
119
|
+
outlineOffset: 2,
|
|
120
|
+
transition: "opacity 150ms ease",
|
|
121
|
+
whiteSpace: "nowrap",
|
|
122
|
+
verticalAlign: "middle",
|
|
123
|
+
WebkitTapHighlightColor: "transparent",
|
|
124
|
+
...style,
|
|
125
|
+
}, children: [_jsx("span", { style: {
|
|
126
|
+
display: "inline-flex",
|
|
127
|
+
position: !iconOnly && align === "left" ? "absolute" : "static",
|
|
128
|
+
left: !iconOnly && align === "left" ? 12 : undefined,
|
|
129
|
+
}, children: _jsx(MicrosoftLogo, {}) }), !iconOnly && _jsx("span", { children: text })] }));
|
|
130
|
+
}
|
|
131
|
+
export function MicrosoftButton(props) {
|
|
132
|
+
const { client } = useAutharaServices();
|
|
133
|
+
const internal = props;
|
|
134
|
+
const { action = "login", audience, authenticationChallengeId, recoveryLinkId, onSuccess, onError, label = action === "login" ? "sign-in" : "continue", ...buttonProps } = internal;
|
|
135
|
+
return (_jsx(MicrosoftCredentialButton, { ...buttonProps, label: label, onCredential: async (proof) => {
|
|
136
|
+
let selected;
|
|
137
|
+
try {
|
|
138
|
+
selected = resolveAction(action, audience, authenticationChallengeId, recoveryLinkId);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
throw toAutharaButtonError(error, "The Microsoft button action is not configured correctly.", "configuration");
|
|
142
|
+
}
|
|
143
|
+
let result;
|
|
144
|
+
try {
|
|
145
|
+
result = await runMicrosoftAction(client, selected, proof);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw toAutharaButtonError(error, `Microsoft ${action} failed.`, "authara");
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
await onSuccess?.(result);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
throw toAutharaButtonError(error, "The Microsoft success callback failed.", "consumer");
|
|
155
|
+
}
|
|
156
|
+
}, onError: (error) => onError?.(toAutharaButtonError(error, "Microsoft authentication failed.", "provider")) }));
|
|
157
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AppleProof, AutharaAction, AutharaActionSuccess, AutharaAudience, AutharaReactClient, GoogleProof, MicrosoftProof } from "./contract.js";
|
|
2
|
+
export type ResolvedAutharaAction = {
|
|
3
|
+
action: "login";
|
|
4
|
+
audience: AutharaAudience;
|
|
5
|
+
} | {
|
|
6
|
+
action: "link";
|
|
7
|
+
} | {
|
|
8
|
+
action: "reauthenticate";
|
|
9
|
+
authenticationChallengeId: string;
|
|
10
|
+
} | {
|
|
11
|
+
action: "recover";
|
|
12
|
+
audience: "app";
|
|
13
|
+
recoveryLinkId: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function runGoogleAction(client: AutharaReactClient, selected: ResolvedAutharaAction, proof: GoogleProof): Promise<AutharaActionSuccess<"google">>;
|
|
16
|
+
export declare function runAppleAction(client: AutharaReactClient, selected: ResolvedAutharaAction, proof: AppleProof): Promise<AutharaActionSuccess<"apple">>;
|
|
17
|
+
export declare function runMicrosoftAction(client: AutharaReactClient, selected: ResolvedAutharaAction, proof: MicrosoftProof): Promise<AutharaActionSuccess<"microsoft">>;
|
|
18
|
+
export declare function resolveAction(action: AutharaAction | undefined, audience: AutharaAudience | undefined, authenticationChallengeId: string | undefined, recoveryLinkId: string | undefined): ResolvedAutharaAction;
|