Neilk1021 opened a new pull request, #8314: URL: https://github.com/apache/texera/pull/8314
Adds Sign in with Apple as a third identity provider alongside local and Google sign-in, following the shape the ORCID work established: one more button in the login card's `social-buttons` block, one more provider resource on the backend, and no new login surface. Off by default. `gui.login.apple-login` gates the button, and the flow needs a Services ID (`user-sys.apple.clientId`) that only an operator can supply, so a plain checkout is unaffected. ## Backend `AppleAuthResource` (`/auth/apple`) exposes the client id and verifies Apple identity tokens: - `GET /auth/apple/clientid` — mirrors `GoogleAuthResource`, so the Services ID never lands in a config payload. - `POST /auth/apple/login` — takes the raw identity token as `text/plain` and verifies it against Apple's published JWKS (`https://appleid.apple.com/auth/keys`), pinning the issuer, the audience to our Services ID, and RS256. A malformed or unverifiable credential becomes a 401 rather than escaping as a 500. Provisioning goes through `ExternalAuthProvisioner`, extended here to handle an identity with no email address. Three Apple-specific details drove that: - **`sub` is the provider id.** It is stable per user but scoped to the Apple developer *team* — transferring the app rotates it for every user, and `transfer_sub` is only available for 60 days after such a move. Worth knowing before any team transfer. - **An unverified address is refused, an absent one is not.** Mapping an address Apple did not verify onto an existing account would be a takeover. But Apple omits `email` entirely for Sign in with Apple at Work & School accounts, so refusing on absence would lock those users out of a provider the deployment has enabled; they are provisioned identity-only and asked for an address once inside. - **Apple types its claims inconsistently.** `email_verified` arrives as either a JSON boolean or a quoted string, and Apple documents both. Reading only one shape silently yields `false`, which would reject legitimate logins — `booleanClaim` handles both. Apple sends the display name only on a user's first authorization, outside the identity token, so it never reaches this endpoint and would be unsigned and untrusted anyway. The address stands in, as it already does for a Google account with no name, and `sub` stands in when there is no address either. Apple supplies no avatar. ## Frontend `AppleAuthService` owns Apple's SDK: it injects `appleid.auth.js` from Apple's CDN on first use, configures it with `usePopup: true`, and resolves with the identity token. `usePopup` is load-bearing twice over — it keeps the token in the page, and it stops Apple posting a form to `redirectURI`, which would navigate away from the SPA. The script is fetched on click, so a visitor who only uses the password form never calls Apple at all. `appleLogin` joins the anonymous `/config/pre-login` payload, since the login page has to decide whether to draw the button before anyone is signed in. ### Why the button is ours rather than Apple's rendered one Apple's SDK can render its own button, and this branch used it for a while. It had to be abandoned: the SDK derives the label's size from the button's height (a 13px label inside a `viewBox` scaled by `height / 30`, so a 44px button yields ~19px type) and offers only three logo widths, all keyed to height. Neither can be set independently, so the button could not be matched to the Google button beside it. Apple permits a custom button provided it carries their logo artwork and one of their three approved titles. So the button is a plain `<button>` with `assets/logos/apple-logo-white.svg` and a "Continue with Apple" label, styled to sit flush with Google's: same 328px width, 14px label, and a height no smaller than Google's, which is what Apple's "no smaller than other sign-in buttons" rule requires. The logo is a 16×44 box carrying the glyph's own clear space, so it is neither cropped nor given extra vertical padding. Label alignment follows `.orcid-login`: `flex: 1; text-align: center` with no compensation for the logo's width, which centres the label in the space to the right of the mark — what Google's button does — rather than across the whole button. The reasoning behind each value is in the template and SCSS comments, since these are the kind of numbers that get "tidied" into something non-compliant later. ## Database `sql/updates/43.sql` adds `APPLE` to `auth_provider.provider_type`. Nothing inserts an `APPLE` row in the same transaction, because Postgres forbids using a new enum value in the transaction that adds it — the value is only declared, and the first Apple login writes it. The type is schema-qualified because the two migration runners disagree about the search path. ## Tests - `AppleAuthResourceSpec` — 11 cases over token verification and claim mapping, with the network seam overridden rather than signing real tokens: verified/unverified/absent email, both claim encodings, malformed credentials, and the 401 paths. - `apple-auth.service.spec.ts` — 9 cases over the SDK mechanics: the client id fetched before the script is injected, the pinned CDN url, memoization across calls, a retry after a failed load, `usePopup`/`redirectURI` in the init argument, and popup dismissal resolving as "no token". - `texera-login.component.spec.ts` — the Apple button and click flow, including two that assert the compliance-sensitive bits: the label is one of Apple's permitted titles, and the logo `src` is Apple's asset rather than an icon-set glyph. Also folded in a fix for a pre-existing failure in that spec: its `render()` helper set only `localLogin` and `googleLogin`, while `MockGuiConfigService` defaults `appleLogin: true` and the divider condition reads all three, so the "only local login" divider assertion failed. The helper now takes all three flags, and there are new cases for the Apple-only divider path, which nothing covered. Full frontend suite passes: 210 files, 5472 tests. ## Testing Verified end to end against a real Apple app and Services ID. Apple will not accept `http://localhost` as a redirect URI and requires HTTPS on a domain registered against the Services ID, so the flow cannot be exercised on a plain local checkout. I routed a local dev server through an ngrok tunnel to get a stable HTTPS domain Apple would accept: 1. Registered the ngrok domain against the Services ID in the Apple developer console, with the return URL pointing at the tunnel origin, and served Apple's domain-association file from `/.well-known/` (hence the `angular.json` asset glob). 2. Set `USER_SYS_APPLE_CLIENT_ID` to the Services ID and `GUI_LOGIN_APPLE_LOGIN=true`. 3. Ran the Angular dev server bound so the tunnel host is accepted, and opened the tunnel URL rather than localhost — `redirectURI` derives from `window.location.origin`, so it has to be the tunnel origin for Apple to match its registration. 4. Signed in through Apple's popup and confirmed the identity token reaches `POST /auth/apple/login`, verifies against Apple's JWKS, provisions the user, and returns a session that lands on the workflow page. 5. Re-ran with the popup dismissed to confirm no error is surfaced, and with an account whose address Apple had not verified to confirm the 401. The first sign-in and a subsequent one were both exercised, which matters because Apple only sends the display name on the first authorization. ## Before merging - `frontend/angular.json` carries my ngrok host in `allowedHosts`, which is developer-local config in a shared file. It needs removing or moving behind a local override before this merges. - `frontend/.well-known/.gitkeep` is a placeholder for Apple's domain-association file; a deployment enabling Apple has to supply the real file. - Committing Apple's logo artwork may warrant an entry under `licenses/` or `licenses-3rd-party-code/`. The ORCID work committed its brand PNG without one, so there may be no established practice here — flagging for whoever knows the policy. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
