Skip to content

Commit ffa620f

Browse files
committed
feat: github pipeline and code refactoring
1 parent 977ff7a commit ffa620f

File tree

24 files changed

+499
-567
lines changed

24 files changed

+499
-567
lines changed

.github/actions/configure-auth0-screens/action.yml

Lines changed: 142 additions & 340 deletions
Large diffs are not rendered by default.

.github/actions/setup-auth0-cli/action.yml

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
name: 'Setup Auth0 CLI'
2-
description: 'Installs and authorizes the Auth0 CLI'
1+
name: "Setup Auth0 CLI"
2+
description: "Installs and authorizes the Auth0 CLI for use with ACUL"
33
inputs:
44
auth0-domain:
5-
description: 'Auth0 domain'
5+
description: "Auth0 domain"
66
required: true
77
auth0-client-id:
8-
description: 'Auth0 client ID for M2M API access'
8+
description: "Auth0 client ID for M2M API access"
99
required: true
1010
auth0-client-secret:
11-
description: 'Auth0 client secret for M2M API access'
11+
description: "Auth0 client secret for M2M API access"
1212
required: true
1313
runs:
14-
using: 'composite'
14+
using: "composite"
1515
steps:
1616
- name: Install Auth0 CLI
1717
shell: bash
@@ -32,9 +32,7 @@ runs:
3232
echo "Attempting to log in to Auth0 CLI for domain: $AUTH0_DOMAIN"
3333
# Log in Universal Login customization
3434
auth0 login \
35-
--domain "$AUTH0_DOMAIN" \
36-
--client-id "$AUTH0_CLIENT_ID" \
37-
--client-secret "$AUTH0_CLIENT_SECRET"
35+
--domain ${{ inputs.auth0-domain }} \
36+
--client-id ${{ inputs.auth0-client-id }} \
37+
--client-secret ${{ inputs.auth0-client-secret }}
3838
echo "✅ Auth0 CLI authorized."
39-
# Verify connection by listing prompts
40-
auth0 prompts list

.github/deploy_config.yml

Lines changed: 2 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,82 +4,6 @@
44
# when the workflow is run without specific overriding inputs.
55

66
default_screen_deployment_status:
7-
"email-identifier-challenge": false
8-
"interstitial-captcha": false
97
"login-id": true
10-
"login-password": false
11-
"login-passwordless-email-code": false
12-
"login-passwordless-sms-otp": false
13-
"passkey-enrollment": false
14-
"passkey-enrollment-local": false
15-
"phone-identifier-challenge": false
16-
"phone-identifier-enrollment": false
17-
"signup-id": false
18-
"signup-password": false
19-
"login": false
20-
"reset-password": false
21-
"reset-password-email": false
22-
"reset-password-error": false
23-
"reset-password-request": false
24-
"reset-password-success": false
25-
"signup": false
26-
"mfa-begin-enroll-options": false
27-
"mfa-country-codes": false
28-
"mfa-detect-browser-capabilities": false
29-
"mfa-email-challenge": false
30-
"mfa-email-list": false
31-
"mfa-enroll-result": false
32-
"mfa-login-options": false
33-
"mfa-push-challenge-push": false
34-
"mfa-push-enrollment-qr": false
35-
"mfa-push-list": false
36-
"mfa-push-welcome": false
37-
"mfa-sms-challenge": false
38-
"mfa-sms-enrollment": false
39-
"mfa-sms-list": false
40-
"accept-invitation": false
41-
"mfa-otp-challenge": false
42-
"mfa-otp-enrollment-code": false
43-
"mfa-otp-enrollment-qr": false
44-
"organization-picker": false
45-
"organization-selection": false
46-
"reset-password-mfa-email-challenge": false
47-
"reset-password-mfa-otp-challenge": false
48-
"reset-password-mfa-push-challenge-push": false
49-
"reset-password-mfa-sms-challenge": false
50-
"device-code-activation": false
51-
"device-code-activation-allowed": false
52-
"device-code-activation-denied": false
53-
"device-code-confirmation": false
54-
"mfa-phone-challenge": false
55-
"mfa-phone-enrollment": false
56-
"mfa-voice-challenge": false
57-
"mfa-voice-enrollment": false
58-
"reset-password-mfa-phone-challenge": false
59-
"reset-password-mfa-voice-challenge": false
60-
"mfa-recovery-code-challenge": false
61-
"mfa-recovery-code-enrollment": false
62-
"reset-password-mfa-recovery-code-challenge": false
63-
"redeem-ticket": false
64-
"mfa-recovery-code-challenge-new-code": false
65-
"consent": false
66-
"customized-consent": false
67-
"email-otp-challenge": false
68-
"email-verification-result": false
69-
"login-email-verification": false
70-
"logout": false
71-
"logout-aborted": false
72-
"logout-complete": false
73-
"mfa-webauthn-change-key-nickname": false
74-
"mfa-webauthn-enrollment-success": false
75-
"mfa-webauthn-error": false
76-
"mfa-webauthn-not-available-error": false
77-
"mfa-webauthn-platform-challenge": false
78-
"mfa-webauthn-platform-enrollment": false
79-
"mfa-webauthn-roaming-challenge": false
80-
"mfa-webauthn-roaming-enrollment": false
81-
"reset-password-mfa-webauthn-platform-challenge": false
82-
"reset-password-mfa-webauthn-roaming-challenge": false
83-
"brute-force-protection-unblock": false
84-
"brute-force-protection-unblock-failure": false
85-
"brute-force-protection-unblock-success": false
8+
"login-password": true
9+
"login": true

.github/workflows/acul-deploy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ jobs:
8585

8686
- name: Discover available screens
8787
id: discover-screens
88-
uses: ./.github/actions/discover-acul-screens
88+
uses: ./.github/actions/discover-screens
8989
with:
9090
working-directory: ${{ env.WORKING_DIR }}
9191

@@ -112,7 +112,7 @@ jobs:
112112

113113
- name: Deployment Summary
114114
if: always()
115-
uses: ./.github/actions/acul-deployment-summary
115+
uses: ./.github/actions/deployment-summary
116116
with:
117117
job-status: ${{ job.status }}
118118
deployed_screens: ${{ steps.configure-auth0.outputs.deployed_screens }}

DEPLOYMENT.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ CloudFront acts as a secure gateway to your S3 assets. It allows end users to ac
162162
5. Under **Default Cache Behavior**:
163163
- For **Viewer Protocol Policy**, select "Redirect HTTP to HTTPS"
164164
- For **Cache Policy**, select "CachingOptimized"
165+
- For **Origin request Policy**, select CORS-S3Origin
166+
- For **Response headers policy**, SimpleCORS (or create custom with CORS headers)
165167
6. Under **Settings**:
166168
- Set **Default Root Object** to `index.html`
167169
- Choose your desired **Price Class**
@@ -258,7 +260,7 @@ Go to your repository, then:
258260
| `S3_BUCKET_NAME` | `your-acul-assets-bucket` | Your S3 bucket name |
259261
| `AWS_REGION` | `us-east-1` | Region where your bucket is located |
260262
| `S3_CDN_URL` | `https://d1234abcdef.cloudfront.net` or `https://your-bucket.s3.region.amazonaws.com` | Base URL for assets (no trailing slash) |
261-
| `AUTH0_DOMAIN` | `your-domain.auth0.com` | Your Auth0 domain which has custom domain configured |
263+
| `AUTH0_DOMAIN` | `dev-domain.auth0.com` | Your Auth0 domain which has custom domain configured |
262264
| `AUTH0_CLIENT_ID` | `abcdef123456789` | M2M application client ID |
263265
| `AUTH0_CLIENT_SECRET` | `your-secret-here` | M2M application client secret |
264266

src/common/FormField/index.tsx

Lines changed: 63 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useRef } from "react";
1+
import React from "react";
22
import Label from "@/common/Label";
33
import type { LabelProps } from "@/common/Label";
44
import Input from "@/common/Input";
@@ -17,70 +17,75 @@ export interface FormFieldProps {
1717
errorTextClassName?: string;
1818
}
1919

20-
const FormField: React.FC<FormFieldProps> = ({
21-
labelProps,
22-
inputProps,
23-
error,
24-
className,
25-
inputIcon,
26-
isParentFocused,
27-
inputWrapperClassName,
28-
errorTextClassName,
29-
}) => {
30-
const { id: inputId, className: inputSpecificClassName } = inputProps;
31-
const internalInputRef = useRef<HTMLInputElement>(null);
20+
const FormField = React.forwardRef<HTMLInputElement, FormFieldProps>(
21+
({
22+
labelProps,
23+
inputProps,
24+
error,
25+
className,
26+
inputIcon,
27+
isParentFocused,
28+
inputWrapperClassName,
29+
errorTextClassName,
30+
}) => {
31+
const { id: inputId, className: inputSpecificClassName } = inputProps;
3232

33-
let inputCombinedClassName = inputSpecificClassName || "";
34-
const errorRingStyles = "border-error focus:ring-error focus:border-error";
33+
const { ref: refForInputElement, ...remainingInputProps } = inputProps;
3534

36-
let finalInputProps = { ...inputProps };
37-
delete finalInputProps.className;
35+
let inputCombinedClassName = inputSpecificClassName || "";
36+
const errorRingStyles = "border-error focus:ring-error focus:border-error";
3837

39-
if (inputIcon) {
40-
inputCombinedClassName += " pr-16";
41-
}
38+
let finalInputProps = { ...remainingInputProps };
39+
delete finalInputProps.className;
4240

43-
if (error) {
44-
inputCombinedClassName += ` ${errorRingStyles}`;
45-
} else if (isParentFocused) {
46-
finalInputProps.forceFocusStyle = true;
47-
}
41+
if (inputIcon) {
42+
inputCombinedClassName += " pr-16";
43+
}
4844

49-
return (
50-
<div className={`${className || ""}`}>
51-
<div
52-
className={`relative mt-1 rounded-md shadow-sm ${inputWrapperClassName || ""}`}
53-
>
54-
<div className="relative w-full">
55-
<Input
56-
{...finalInputProps}
57-
ref={internalInputRef}
58-
className={inputCombinedClassName}
59-
aria-invalid={!!error}
60-
aria-describedby={error ? `${inputId}-error` : undefined}
61-
/>
62-
<Label {...labelProps} htmlFor={inputId} isError={!!error} />
63-
{inputIcon && (
64-
<div
65-
className={`absolute inset-y-0 right-0 flex items-center rounded-r-md mt-px mt-3 ${isParentFocused ? "bg-primary/15" : ""}`}
66-
>
67-
{inputIcon}
68-
</div>
69-
)}
70-
</div>
71-
</div>
72-
{error && (
45+
if (error) {
46+
inputCombinedClassName += ` ${errorRingStyles}`;
47+
} else if (isParentFocused) {
48+
finalInputProps.forceFocusStyle = true;
49+
}
50+
51+
return (
52+
<div className={`${className || ""}`}>
7353
<div
74-
id={`${inputId}-error`}
75-
className={`flex items-center mt-2 text-sm text-error ${errorTextClassName || ""}`}
76-
role="alert"
54+
className={`relative mt-1 rounded-md shadow-sm ${inputWrapperClassName || ""}`}
7755
>
78-
<Icon As={ExclamationCircleIcon} className="h-4 w-4 mr-1" />
79-
{error}
56+
<div className="relative w-full">
57+
<Input
58+
{...finalInputProps}
59+
ref={refForInputElement}
60+
className={inputCombinedClassName}
61+
aria-invalid={!!error}
62+
aria-describedby={error ? `${inputId}-error` : undefined}
63+
/>
64+
<Label {...labelProps} htmlFor={inputId} isError={!!error} />
65+
{inputIcon && (
66+
<div
67+
className={`absolute inset-y-0 right-0 flex items-center rounded-r-md mt-px mt-3 ${isParentFocused ? "bg-primary/15" : ""}`}
68+
>
69+
{inputIcon}
70+
</div>
71+
)}
72+
</div>
8073
</div>
81-
)}
82-
</div>
83-
);
84-
};
74+
{error && (
75+
<div
76+
id={`${inputId}-error`}
77+
className={`flex items-center mt-2 text-sm text-error ${errorTextClassName || ""}`}
78+
role="alert"
79+
>
80+
<Icon As={ExclamationCircleIcon} className="h-4 w-4 mr-1" />
81+
{error}
82+
</div>
83+
)}
84+
</div>
85+
);
86+
},
87+
);
88+
89+
FormField.displayName = "FormField";
8590

8691
export default FormField;

src/common/SocialProviderButton/index.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import React from "react";
22

3-
export interface SocialProviderButtonProps {
4-
providerName: string;
5-
icon: React.ReactNode;
3+
interface SocialProviderButtonProps {
64
onClick: () => void;
5+
displayName: string;
6+
iconComponent: React.ReactNode | null;
77
disabled?: boolean;
88
className?: string;
99
}
1010

1111
const SocialProviderButton: React.FC<SocialProviderButtonProps> = ({
12-
providerName,
13-
icon,
1412
onClick,
13+
displayName,
14+
iconComponent,
1515
disabled = false,
1616
className = "",
1717
}) => {
18+
const dataTestId = `social-provider-button-${displayName.toLowerCase().replace(/\s+/g, "-")}`;
19+
1820
const baseStyles =
1921
"flex items-center justify-start w-full max-w-[320px] h-[52px] py-[14px] px-[16px] border rounded gap-x-4 focus:outline-none transition-colors duration-150 ease-in-out focus:ring-4 focus:ring-primary/15";
2022

@@ -28,13 +30,18 @@ const SocialProviderButton: React.FC<SocialProviderButtonProps> = ({
2830
<button
2931
type="button"
3032
onClick={onClick}
31-
disabled={disabled}
32-
className={`${baseStyles} ${disabled ? disabledStyles : enabledStyles}
33-
${className}`}
34-
aria-label={`Continue with ${providerName}`}
33+
className={`${baseStyles} ${disabled ? disabledStyles : enabledStyles} ${className}`}
34+
data-testid={dataTestId}
35+
title={`Continue with ${displayName}`}
3536
>
36-
{icon}
37-
<span className="font-normal text-base">{`Continue with ${providerName}`}</span>
37+
{iconComponent && (
38+
<span className="mr-3 w-5 h-5 flex items-center justify-center flex-shrink-0">
39+
{iconComponent}
40+
</span>
41+
)}
42+
<span className="overflow-hidden whitespace-nowrap text-ellipsis font-normal text-base">
43+
Continue with {displayName}
44+
</span>
3845
</button>
3946
);
4047
};

src/mock-data/login-id.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@
4242
"name": "google-oauth2",
4343
"strategy": "google"
4444
},
45+
{
46+
"name": "hugging-face",
47+
"strategy": "oauth2"
48+
},
49+
{
50+
"name": "klarna-social-connection",
51+
"strategy": "oauth2"
52+
},
53+
{
54+
"name": "digitalocean",
55+
"strategy": "oauth2"
56+
},
4557
{
4658
"name": "linkedin",
4759
"strategy": "linkedin"

src/mock-data/login-password.json

Lines changed: 4 additions & 0 deletions
Large diffs are not rendered by default.

src/mock-data/login.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@
4242
"name": "google-oauth2",
4343
"strategy": "google"
4444
},
45+
{
46+
"name": "hugging-face",
47+
"strategy": "oauth2"
48+
},
49+
{
50+
"name": "klarna-social-connection",
51+
"strategy": "oauth2"
52+
},
53+
{
54+
"name": "digitalocean",
55+
"strategy": "oauth2"
56+
},
4557
{
4658
"name": "linkedin",
4759
"strategy": "linkedin"

0 commit comments

Comments
 (0)