Spit Sign In - Sign Up
All checks were successful
Client Registration / build-deploy (push) Successful in 10s

This commit is contained in:
Grae Jones
2026-03-23 09:39:44 -07:00
parent 3a310c5d3f
commit 732f81333b
5 changed files with 157 additions and 51 deletions

View File

@@ -61,7 +61,7 @@ npm start # http://localhost:3001
Runs in **mock mode** when `API_BASE_URL` is empty in `authConfig.js`. To connect to the real Registration Function, update the constants in `src/auth/authConfig.js`: Runs in **mock mode** when `API_BASE_URL` is empty in `authConfig.js`. To connect to the real Registration Function, update the constants in `src/auth/authConfig.js`:
```js ```js
export const API_BASE_URL = 'https://portal.positivespend.com'; export const API_BASE_URL = 'https://adpregapi.usimdev.com';
export const API_FUNCTION_KEY = 'your-function-key'; export const API_FUNCTION_KEY = 'your-function-key';
``` ```

File diff suppressed because one or more lines are too long

View File

@@ -1,22 +1,21 @@
/** /**
* MSAL Configuration for Entra External ID (Customer Identity / CIAM) * MSAL Configuration for Entra External ID (Customer Identity)
* *
* Tenant: Positive Spend Clients * This uses the External ID tenant (CIAM) — different from the
* Domain: positiveclients.onmicrosoft.com * internal Entra tenant used by the Admin/Management console.
* Tenant ID: cbf8b7d7-1e13-486d-b5b0-287ba79fdf0b *
* SPA App: AdPlatform Client SPA (43c493e4-e1ed-4cd7-ab0a-e507e20af724) * TODO: Replace placeholder values with actual External ID tenant details.
* Authority: https://positiveclients.ciamlogin.com/
*/ */
export const msalConfig = { export const msalConfig = {
auth: { auth: {
clientId: '43c493e4-e1ed-4cd7-ab0a-e507e20af724', clientId: '154c9111-14a0-4c0f-8132-7bc68254a74e',
authority: 'https://positiveclients.ciamlogin.com/', authority: 'https://usimclients.ciamlogin.com/891f98f1-ed34-42a1-9b6c-28b0554d92c2',
redirectUri: 'https://register.positivespend.com', redirectUri: window.location.origin,
postLogoutRedirectUri: 'https://register.positivespend.com', postLogoutRedirectUri: window.location.origin,
knownAuthorities: ['positiveclients.ciamlogin.com'], knownAuthorities: ['usimclients.ciamlogin.com'],
}, },
cache: { cache: {
cacheLocation: 'sessionStorage', cacheLocation: 'sessionStorage',
storeAuthStateInCookie: false, storeAuthStateInCookie: false,
}, },
}; };
@@ -25,6 +24,9 @@ export const loginRequest = {
scopes: ['openid', 'profile', 'email'], scopes: ['openid', 'profile', 'email'],
}; };
// Gateway forwards to registration:8080 internally // Registration Function API
export const API_BASE_URL = 'https://portal.positivespend.com'; export const API_BASE_URL = 'https://adpregapi.usimdev.com';
// Function key for Registration API (AuthorizationLevel.Function)
// TODO: Set this from your Azure Function → App Keys → default host key
export const API_FUNCTION_KEY = ''; export const API_FUNCTION_KEY = '';

View File

@@ -1,53 +1,130 @@
import React from 'react'; import React, { useState } from 'react';
import { useAuth } from '../../auth/AuthProvider'; import { useAuth } from '../../auth/AuthProvider';
export default function SignInStep() { export default function SignInStep() {
const { signIn, isLoading, error } = useAuth(); const { signIn, isLoading, error } = useAuth();
const [mode, setMode] = useState(null); // null | 'new' | 'returning'
const providers = (
<div className="provider-list">
<button
className="btn-provider btn-provider-google"
onClick={() => signIn('google')}
disabled={isLoading}
>
<span className="provider-icon">G</span>
{isLoading ? 'Connecting...' : 'Continue with Google'}
</button>
<button
className="btn-provider btn-provider-apple"
onClick={() => signIn('apple')}
disabled={isLoading}
>
<span className="provider-icon">🍎</span>
{isLoading ? 'Connecting...' : 'Continue with Apple'}
</button>
<button
className="btn-provider btn-provider-microsoft"
onClick={() => signIn('microsoft')}
disabled={isLoading}
>
<span className="provider-icon"></span>
{isLoading ? 'Connecting...' : 'Continue with Microsoft'}
</button>
</div>
);
// Landing — user hasn't chosen yet
if (!mode) {
return (
<div className="step-card">
<div className="step-header">
<span className="step-icon">🔷</span>
<h2>Welcome to AdPlatform</h2>
<p className="step-description">
What would you like to do?
</p>
</div>
{error && <div className="error-message">{error}</div>}
<div className="path-choice">
<button
className="btn-path btn-path-primary"
onClick={() => setMode('new')}
>
<span className="path-icon"></span>
<div className="path-text">
<strong>Apply for Access</strong>
<span>New to AdPlatform? Start your registration here.</span>
</div>
<span className="path-arrow"></span>
</button>
<button
className="btn-path btn-path-secondary"
onClick={() => setMode('returning')}
>
<span className="path-icon">🔄</span>
<div className="path-text">
<strong>Check My Application</strong>
<span>Already applied? Sign in to view your status.</span>
</div>
<span className="path-arrow"></span>
</button>
</div>
</div>
);
}
// New applicant — sign up path
if (mode === 'new') {
return (
<div className="step-card">
<button className="btn-back" onClick={() => setMode(null)}> Back</button>
<div className="step-header">
<span className="step-icon"></span>
<h2>Create Your Account</h2>
<p className="step-description">
Choose how you'd like to sign up. We'll create your AdPlatform identity
and walk you through the application.
</p>
</div>
{error && <div className="error-message">{error}</div>}
{providers}
<p className="step-fine-print">
By continuing, you agree to AdPlatform's Terms of Service and Privacy Policy.
Your account will be managed through Microsoft Entra External ID.
</p>
</div>
);
}
// Returning applicant — sign in path
return ( return (
<div className="step-card"> <div className="step-card">
<button className="btn-back" onClick={() => setMode(null)}>← Back</button>
<div className="step-header"> <div className="step-header">
<span className="step-icon">🔐</span> <span className="step-icon">🔄</span>
<h2>Create Your Account</h2> <h2>Welcome Back</h2>
<p className="step-description"> <p className="step-description">
Sign in with your preferred provider to get started with AdPlatform. Sign in with the same account you used when you applied.
We'll pick up right where you left off.
</p> </p>
</div> </div>
{error && <div className="error-message">{error}</div>} {error && <div className="error-message">{error}</div>}
<div className="provider-list"> {providers}
<button
className="btn-provider btn-provider-google"
onClick={() => signIn('google')}
disabled={isLoading}
>
<span className="provider-icon">G</span>
{isLoading ? 'Connecting...' : 'Continue with Google'}
</button>
<button
className="btn-provider btn-provider-apple"
onClick={() => signIn('apple')}
disabled={isLoading}
>
<span className="provider-icon">🍎</span>
{isLoading ? 'Connecting...' : 'Continue with Apple'}
</button>
<button
className="btn-provider btn-provider-microsoft"
onClick={() => signIn('microsoft')}
disabled={isLoading}
>
<span className="provider-icon"></span>
{isLoading ? 'Connecting...' : 'Continue with Microsoft'}
</button>
</div>
<p className="step-fine-print"> <p className="step-fine-print">
By continuing, you agree to AdPlatform's Terms of Service and Privacy Policy. Use the same provider you signed up with. If you need help, contact
Your account will be managed through Microsoft Entra External ID. support@positivespend.com.
</p> </p>
</div> </div>
); );

View File

@@ -288,6 +288,33 @@ body {
.btn-provider-microsoft { background: #0078d4; color: #fff; border-color: #0078d4; } .btn-provider-microsoft { background: #0078d4; color: #fff; border-color: #0078d4; }
.btn-provider-microsoft:hover { background: #006abc; border-color: #006abc; } .btn-provider-microsoft:hover { background: #006abc; border-color: #006abc; }
/* Two-path landing */
.path-choice { display: flex; flex-direction: column; gap: 12px; margin-top: 8px; }
.btn-path {
display: flex; align-items: center; gap: 16px;
padding: 18px 20px; border-radius: 12px; border: 2px solid var(--border);
background: #fff; cursor: pointer; text-align: left;
transition: border-color 0.15s, box-shadow 0.15s;
width: 100%;
}
.btn-path:hover { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }
.btn-path-primary:hover { border-color: #0078d4; box-shadow: 0 0 0 3px rgba(0,120,212,0.1); }
.path-icon { font-size: 24px; flex-shrink: 0; }
.path-text { flex: 1; display: flex; flex-direction: column; gap: 2px; }
.path-text strong { font-size: 15px; color: var(--text); font-weight: 600; }
.path-text span { font-size: 13px; color: var(--text-muted); }
.path-arrow { font-size: 18px; color: var(--text-muted); flex-shrink: 0; }
.btn-back {
display: inline-flex; align-items: center; gap: 4px;
background: none; border: none; cursor: pointer;
color: var(--text-muted); font-size: 14px; padding: 0 0 16px 0;
transition: color 0.15s;
}
.btn-back:hover { color: var(--text); }
.provider-icon { .provider-icon {
font-size: 18px; font-size: 18px;
width: 24px; width: 24px;