Skip to content

Commit c46338e

Browse files
author
Herve Tribouilloy
committed
Added OAuth login flow
1 parent 1b48d9c commit c46338e

16 files changed

Lines changed: 75 additions & 106 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ www
77
widget.local.crt
88
widget.local.key
99
test-results
10-
README-dev.md
10+
README-dev.md
11+
*.html

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,13 @@ Planned work includes:
7979
- Step-up authentication after repeated login failure
8080
- Turnstile token reuse across booking flow
8181
- Expanded Playwright coverage on abuse scenarios
82-
- Playwright tests focused on core flow integrity
82+
- Playwright tests focused on core flow integrity
83+
84+
## Hosts examples
85+
| Host | Test to check it works | Notes on the component |
86+
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
87+
| **booking-api.local** | Open `https://booking-api.local/api/graphql` in browser → expect GraphQL JSON (or GraphiQL), then run `query { __typename }` | Keystone persistence + GraphQL. Must be bound to `0.0.0.0`, HTTPS cert trusted, CORS must allow `https://southerndemo.com` **with credentials**. Auth logic must accept JWT (not rely on `context.session`). |
88+
| **oauth-api.local** | `POST https://oauth-api.local/auth/login` → expect `200` + token in JSON | Token issuer only. No cookies here unless explicitly designed. Verify token format (JWT vs opaque) and expiry. HTTPS required but CORS usually not (server-to-server or same origin). |
89+
| **auth-bridge.local** | Hit login/refresh endpoint → DevTools → Application → Cookies → verify cookie appears | Most fragile component. Must set cookie with `Secure; SameSite=None; Domain=.southerndemo.com; Path=/`. Must return `Access-Control-Allow-Origin: https://southerndemo.com` **and** `Allow-Credentials: true`. Any mismatch silently breaks auth. |
90+
| **southerndemo.com** | Load page → Network tab shows widget JS `200`, no CSP/CORS errors | Host page. Must be HTTPS. Must not block third-party scripts or credentials. CSP must allow widget domain and auth-bridge. Cookies should be visible under this domain after auth. |
91+
| **widget.bookingsystem.co.uk** | Widget loads, then triggers calls to auth-bridge and booking-api with `credentials: include` | Execution layer only. Should not read cookies manually. Should rely on browser cookie attachment. Any auth failure here usually originates upstream (cookies or CORS). |

vite_project/index.html

Lines changed: 0 additions & 30 deletions
This file was deleted.

vite_project/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vite_project/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "widget-booking",
33
"private": true,
4-
"version": "0.2.0",
4+
"version": "0.2.1",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

vite_project/src/BookingSystemConfig.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ interface BookingSystemConfig {
99
widgets: {
1010
booking?: {
1111
api: string;
12+
auth: string;
1213
};
1314
};
1415
integrations?: {
@@ -40,6 +41,10 @@ export function readBookingSystemConfig(): BookingSystemConfig {
4041
throw new Error('BookingWidget: booking.api missing in global config');
4142
}
4243

44+
if (!config.widgets?.booking?.auth) {
45+
throw new Error('BookingWidget: booking.auth missing in global config');
46+
}
47+
4348
return config;
4449
}
4550

vite_project/src/BookingSystemWidget.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type Props = {
99
};
1010

1111
export function BookingSystemWidget({ host }: Props) {
12-
const { booking } = useWidgetConfig(host);
12+
const { booking, user } = useWidgetConfig(host);
1313

1414
if (!booking) {
1515
activity('bootstrap', '[ContactUs] Widget is not correctly configured', null, 'warn');
@@ -18,7 +18,7 @@ export function BookingSystemWidget({ host }: Props) {
1818

1919
return (
2020
<SystemStateProvider config={booking}>
21-
<UserStateProvider>
21+
<UserStateProvider config={user}>
2222
<BookingSystemWrapper venueId={booking.venueId} />
2323
</UserStateProvider>
2424
</SystemStateProvider>

vite_project/src/components/BookingSystemWrapper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function BookingSystemWrapper({venueId}: Props) {
2727
} = useEventTypeGroups(venue?.id);
2828

2929
if (venueError || hostsError || eventTypeGroupError) {
30-
activity('bootstrap', 'Keystone data cannot be returned', null, 'error');
30+
activity('bootstrap', 'Keystone data cannot be returned', {venue, hostsError, eventTypeGroupError}, 'error');
3131
return <ErrorState />;
3232
}
3333

vite_project/src/components/event/Dashboard/DayEvent/AddToCart.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function AddToCart({onRequireAuth}: AddToCartProps) {
6060
eventId: eventState.activeEventId,
6161
eventTypeId: visitIntent.eventTypeId,
6262
shampoo: eventState.shampoo ? 1 : 0,
63-
userId: user?.id,
63+
userId: user?.id || '',
6464
turnstileToken,
6565
});
6666

vite_project/src/components/user-authentication/Sign.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {useLoginUser} from "../../hooks/domain/useLoginUser.tsx";
21
import {useState} from "react";
32
import {useUserState} from "../../state/User/useUserState.ts";
43
import {loginWithCredentials} from "../../domain/user/authentication.ts";
@@ -8,15 +7,15 @@ export const Sign: React.FC = () => {
87
const [password, setPassword] = useState('');
98
const [submitting, setSubmitting] = useState(false);
109
const [errorMessage, setErrorMessage] = useState<string | null>(null);
11-
const { refreshUser} = useUserState()
10+
const { refreshUser, config} = useUserState()
1211

1312
const handleSubmit = async (e: React.FormEvent) => {
1413
e.preventDefault();
1514
setErrorMessage(null);
1615

1716
try {
1817
setSubmitting(true);
19-
await loginWithCredentials(email, password);
18+
await loginWithCredentials(email, password, config);
2019
await refreshUser();
2120
} catch {
2221
setErrorMessage('Incorrect email or password');

0 commit comments

Comments
 (0)