Skip to content

Commit 39efe45

Browse files
committed
auth worker for login
1 parent 49db124 commit 39efe45

File tree

5 files changed

+216
-0
lines changed

5 files changed

+216
-0
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
"build:json:definitions": "merge-json-cli -i \"ue/models/component-definition.json\" -o \"component-definition.json\"",
1414
"build:json:filters": "merge-json-cli -i \"ue/models/component-filters.json\" -o \"component-filters.json\"",
1515
"prepare": "husky",
16+
"dev:auth": "wrangler dev --config ./workers/auth/wrangler.toml",
17+
"deploy:auth": "wrangler deploy --config ./workers/auth/wrangler.toml",
18+
"tail:auth": "wrangler tail --config ./workers/auth/wrangler.toml",
1619
"dev:contact-us": "wrangler dev --config ./workers/contact_us/wrangler.toml",
1720
"deploy:contact-us": "wrangler deploy --config ./workers/contact_us/wrangler.toml",
1821
"tail:contact-us": "wrangler tail --config ./workers/contact_us/wrangler.toml"

scripts/shared/auth-api.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const AUTH_ORIGIN = 'https://demo-bbird-auth.aem-poc-lab.workers.dev';
2+
const AUTH_PATHS = {
3+
login: '/auth/login',
4+
session: '/auth/session',
5+
};
6+
7+
const AUTH_LABELS = {
8+
login: 'Login',
9+
logout: 'Logout',
10+
};
11+
12+
function authUrl(path) {
13+
return new URL(path, AUTH_ORIGIN).toString();
14+
}
15+
16+
export function getDefaultAuthLabel(type) {
17+
return AUTH_LABELS[type] || '';
18+
}
19+
20+
export function getLoginUrl(returnTo = window.location.href) {
21+
const target = new URL(AUTH_PATHS.login, AUTH_ORIGIN);
22+
target.searchParams.set('returnTo', returnTo);
23+
return target.toString();
24+
}
25+
26+
export function getLogoutUrl() {
27+
return authUrl('/auth/logout');
28+
}
29+
30+
export async function getSessionState() {
31+
const response = await fetch(authUrl(AUTH_PATHS.session), {
32+
method: 'GET',
33+
credentials: 'include',
34+
headers: { Accept: 'application/json' },
35+
});
36+
if (!response.ok) {
37+
throw new Error(`Auth session request failed: ${response.status}`);
38+
}
39+
return response.json();
40+
}

workers/auth/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Auth Test Worker
2+
3+
Lightweight Cloudflare Worker to drive login/logout/session state for the header auth button.
4+
5+
## Endpoints
6+
7+
- `GET /auth/login?returnTo=<url-or-path>` -> triggers Access and redirects to `returnTo`
8+
- `GET /auth/logout` -> logs out via app domain `/cdn-cgi/access/logout` (logout success page)
9+
- `GET /auth/session` -> returns `{ authenticated, email, hasJwtAssertion }`
10+
11+
## Setup
12+
13+
1. Make sure your Cloudflare Access application protects the same hostname and `/auth/*` path.
14+
2. Use the configured worker name in `wrangler.toml` (`demo-bbird-auth`) or change it.
15+
3. Deploy from the project root:
16+
17+
```bash
18+
npm install
19+
npm run deploy:auth
20+
```
21+
22+
## Local dev
23+
24+
```bash
25+
npm run dev:auth
26+
```
27+
28+
## Test flow
29+
30+
1. Open an incognito window.
31+
2. Visit the login endpoint, for example:
32+
33+
```text
34+
https://demo-bbird-auth.aem-poc-lab.workers.dev/auth/login?returnTo=http://localhost:3000/
35+
```
36+
37+
3. Enter an allowed email address on the Access screen.
38+
4. Enter the one-time PIN.
39+
5. Confirm your site loads and `/auth/session` shows `authenticated: true`.
40+
41+
## Logout
42+
43+
Visit:
44+
45+
```text
46+
https://demo-bbird-auth.aem-poc-lab.workers.dev/auth/logout
47+
```
48+
49+
## Site integration
50+
51+
Header auth URLs are centralized in:
52+
53+
`scripts/shared/auth-api.js`
54+
55+
Update `AUTH_ORIGIN` there if your worker hostname changes.
56+
57+
Logout uses app-domain endpoint: `https://demo-bbird-auth.aem-poc-lab.workers.dev/cdn-cgi/access/logout`
58+
without additional redirect parameters for demo simplicity.

workers/auth/index.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
const JSON_HEADERS = {
2+
'Cache-Control': 'no-store',
3+
'Content-Type': 'application/json; charset=utf-8',
4+
};
5+
6+
const DEFAULT_RETURN_PATH = '/auth/session';
7+
8+
function getAccessContext(request) {
9+
const email = request.headers.get('Cf-Access-Authenticated-User-Email');
10+
const jwt = request.headers.get('Cf-Access-Jwt-Assertion');
11+
12+
return {
13+
authenticated: Boolean(email || jwt),
14+
email: email || '',
15+
hasJwtAssertion: Boolean(jwt),
16+
};
17+
}
18+
19+
function getSafeReturnTo(requestUrl) {
20+
const returnTo = requestUrl.searchParams.get('returnTo');
21+
22+
if (!returnTo) return `${requestUrl.origin}${DEFAULT_RETURN_PATH}`;
23+
24+
try {
25+
const absolute = new URL(returnTo, requestUrl.origin);
26+
if (absolute.protocol === 'http:' || absolute.protocol === 'https:') return absolute.toString();
27+
} catch (e) {
28+
// no-op
29+
}
30+
31+
return `${requestUrl.origin}${DEFAULT_RETURN_PATH}`;
32+
}
33+
34+
function getCorsHeaders(request) {
35+
const origin = request.headers.get('Origin');
36+
return origin ? {
37+
'Access-Control-Allow-Credentials': 'true',
38+
'Access-Control-Allow-Headers': 'Content-Type',
39+
'Access-Control-Allow-Methods': 'GET, OPTIONS',
40+
'Access-Control-Allow-Origin': origin,
41+
Vary: 'Origin',
42+
} : {};
43+
}
44+
45+
function json(request, body, init = {}) {
46+
return new Response(JSON.stringify(body, null, 2), {
47+
...init,
48+
headers: {
49+
...JSON_HEADERS,
50+
...getCorsHeaders(request),
51+
...(init.headers || {}),
52+
},
53+
});
54+
}
55+
56+
function redirect(location) {
57+
const headers = { Location: location };
58+
return new Response(null, {
59+
status: 302,
60+
headers,
61+
});
62+
}
63+
64+
export default {
65+
async fetch(request) {
66+
const url = new URL(request.url);
67+
const { pathname } = url;
68+
const access = getAccessContext(request);
69+
70+
if (request.method === 'OPTIONS') {
71+
return new Response(null, {
72+
status: 204,
73+
headers: {
74+
...getCorsHeaders(request),
75+
},
76+
});
77+
}
78+
79+
if (request.method !== 'GET') {
80+
return json(request, { error: 'Method not allowed' }, {
81+
status: 405,
82+
headers: { Allow: 'GET' },
83+
});
84+
}
85+
86+
if (pathname === '/auth/login') {
87+
return redirect(getSafeReturnTo(url));
88+
}
89+
90+
if (pathname === '/auth/logout') {
91+
const logoutUrl = new URL('/cdn-cgi/access/logout', url.origin);
92+
return redirect(logoutUrl.toString());
93+
}
94+
95+
if (pathname === '/auth/session') {
96+
return json(request, {
97+
...access,
98+
path: pathname,
99+
});
100+
}
101+
102+
return json(request, {
103+
error: 'Not found',
104+
availablePaths: ['/auth/login', '/auth/logout', '/auth/session'],
105+
}, { status: 404 });
106+
},
107+
};

workers/auth/wrangler.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name = "demo-bbird-auth"
2+
main = "index.js"
3+
compatibility_date = "2026-03-13"
4+
workers_dev = true
5+
account_id = "ac7ee4615628005dc5ba0a22f0aae2a6"
6+
7+
[observability.logs]
8+
enabled = true

0 commit comments

Comments
 (0)