ソースを参照

Taak 130: stappenplan social login (Facebook/Google) via OneAll

Onderzocht: Direct Connect vereist Personal Advanced (Starter geeft een
harde weigering). Vastgelegd waarom een embedded WebView niet kan en
waarom de app de sessie als waarde terug moet krijgen i.p.v. als cookie.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bob 15 時間 前
コミット
d81eb1bb23
1 ファイル変更65 行追加0 行削除
  1. 65 0
      TASKS.md

+ 65 - 0
TASKS.md

@@ -57,6 +57,7 @@ Google Play en één doorloop die alleen Bob kan doen.
 | **127** | **Kopie-knop op `MijnAanmeldingen`** (clonen) | Wens Bob 2026-09-20, **vóór de livegang**. Backend bestaat al; Drupal-patch voor 3 gaten staat klaar en is getest (127-A). App-deel in stappen B t/m F; het activiteitendeel (127-E) deelt de cascade met 124. Eigen blok hieronder |
 | **128-A** | ✅ **KLAAR** — nieuwsbrieven-resource staat live | Gedeployd en getest op productie 2026-09-21. Rest: één tikfout in de omschrijving van tid 36676 (*verstuurt* → verstuurd), zie het blok hieronder |
 | **129** | Sessie-verlopen-melding afmaken/verifiëren | **Niet in deze chat** (Bob 2026-09-21). De afhandeling bestaat: `drupalRequest` herkent 401 en 403-met-"anonymous", zet `sessieVerlopen` en `VerbindingsBanner` hangt in `HeaderButtonsComponent`. Staat alleen **in de builder, niet in de gecommitte `lib/`** — dus nooit geëxporteerd/gecommit. Nalopen of het af is en of de banner ook echt verschijnt. Eigen blok hieronder |
+| **130** | **Inloggen met Facebook/Google (OneAll)** | Wens Bob 2026-09-21. Onderzocht en akkoord: OneAll-abonnement gaat naar **Personal Advanced** ($27/mnd jaarlijks) voor *Direct Connect*. Geen Play-blocker, maar wél de privacyverklaring/Data Safety als het meegaat in de eerste release. Volledig stappenplan in een eigen blok onderaan |
 
 ---
 #
@@ -1771,3 +1772,67 @@ widgets: alleen nog `#09B34A`/`#EEEEEE` (merkkleuren, niet getokeniseerd) en
 transparante/schaduwwaarden.
 **`showsTestAd: true` staat nog aan** — het restant van G0.6 (`tel-04-horeca.png`)
 is dus nog steeds geblokkeerd op de AdMob-schakelaar.
+
+---
+
+## 🔑 130 · Inloggen met Facebook/Google via OneAll · Eigenaar: Bob (Drupal) + Claude (app)
+
+**Besloten 2026-09-21** na onderzoek. De site gebruikt al OneAll Social Login
+(module 7.x-2.12, subdomein `uitgaanskrantcom`); 42 van de 298 accounts hebben
+een koppeling (Facebook 29, Google 14, GitHub 3, Twitter/X 3, Microsoft 3). Nu
+ook in de app.
+
+**Gekozen route: Direct Connect.** Eén URL per netwerk, geopend in een Chrome
+Custom Tab:
+`https://uitgaanskrantcom.api.oneall.com/socialize/connect/direct/facebook/?service=social_login&callback_uri=<urlencoded>`
+OneAll doet de OAuth en redirect naar de callback met `?connection_token=…`.
+⚠️ Direct Connect zit **niet** in het Starter-plan (getest: *"Direct Connect is
+not available in the Starter plan"*) — vandaar de upgrade naar Personal
+Advanced.
+
+**Twee dingen die dit ontwerp bepalen — niet omheen te bouwen:**
+1. **Embedded WebView mag niet.** Google weigert OAuth in een WebView
+   (`disallowed_useragent`), Facebook ook. Het moet een Custom Tab /
+   `SFSafariViewController` zijn: `url_launcher` met
+   `LaunchMode.inAppBrowserView` (6.3.1 staat al in `pubspec`) — **niet**
+   `inAppWebView`, dat is wél een WebView.
+2. **De app werkt niet met een cookiejar.** Hij bewaart `sessid`/`session_name`
+   als losse App State-waarden en plakt zelf `Cookie: …` op elke call. Een
+   browser-login levert dus niks op tenzij iets die sessie *als waarde*
+   teruggeeft. Dat is het hele bestaansrecht van het claim-endpoint hieronder.
+
+**De keten:** app maakt random `state` → opent Custom Tab → gebruiker logt in bij
+Facebook → OneAll levert `connection_token` bij de callback → callback wisselt
+'m in bij de OneAll API, logt in met `user_login_finalize()`, bewaart
+sessid+session_name onder die `state` (cache, TTL 5 min, eenmalig) → app pollt
+het claim-endpoint elke 2 s → krijgt de sessie → `closeInAppWebView()`.
+
+**Bewuste keuze: pollen, geen deep link.** Het schema
+`uitgaanskrant://uitgaanskrant.com` bestaat en werkt, maar `flutter_web_auth_2`
+vereist een eigen activity in `AndroidManifest.xml` en de export overschrijft
+dat bestand. Pollen + `closeInAppWebView()` kost geen package, geen manifest en
+geen go_router-route.
+
+**Voordeel dat je moet kennen:** er hoeft géén aparte Facebook-app geregistreerd
+te worden. Alle OAuth-redirects lopen naar oneall.com; Facebook kent alleen
+OneAll als client. Er verandert dus niets aan de Facebook-instellingen van de site.
+
+### Stappen
+
+| # | Wie | Wat |
+|---|---|---|
+| **130-A** | Bob | Upgrade naar Personal Advanced + controleer dat `uitgaanskrant.com` bij de allowed domains staat |
+| **130-B** | Bob | **Besluit:** wat gebeurt er met een *nieuwe* gebruiker? `registration_method` staat op `manual`, dus die krijgt nu een Drupal-registratieformulier i.p.v. een sessie. Kiezen: automatisch account op het geverifieerde e-mailadres, of terugsturen met "maak eerst een account aan". (`registration_auto_link = enable`, dus een bestaand account met hetzelfde e-mailadres wordt sowieso gekoppeld) |
+| **130-C** | Bob | **Besluit:** iOS. App Store-richtlijn 4.8 eist bij third-party login een gelijkwaardig privacyvriendelijk alternatief. Apple staat **niet** in de providerlijst van de D7-module (aan: facebook, github, google, twitter, windowslive). Voor Play speelt dit niet |
+| **130-D** | Claude schrijft, Bob deployt | `custom.social_login_app.inc`: callback-pagina die de token inwisselt (zelfde API-call als `social_login_core.module` regel ~290), inlogt en de sessie onder de `state` wegschrijft + een "je kunt terug naar de app"-pagina toont |
+| **130-E** | Claude schrijft, Bob deployt | Claim-resource `POST /flutterdrup/app_social_login/claim.json` → geeft **exact het antwoordformaat van `user/login.json`** terug (`sessid`, `session_name`, `token`, `user.uid/name/mail`), zodat de app-kant een kopie van `drupalLogin` is |
+| **130-F** | Bob | Resource aanzetten in het endpoint — **inclusief de losse operatie eronder**, anders blijft het 404 (bekende valkuil, zie `CLAUDE.md`). Daarna curl-test |
+| **130-G** | Claude | Custom action `socialLoginStart(provider)`: state genereren, Custom Tab openen, pollen, App State vullen (`userSessionid`, `userSessionname`, `userToken`, `userName`, `userUid`, `userMail`), tab sluiten. Timeout ~3 min |
+| **130-H** | Claude | Knoppen op `lib/login/login/login_widget.dart` onder het bestaande formulier |
+| **130-I** | Claude | Vertalingen (P1-17-patroon: globe per widget) |
+| **130-J** | Bob | Echte doorloop met een Facebook- én een Google-account op een profile-build. Claude kan dit niet — geen wachtwoorden |
+| **130-K** | Bob | Privacyverklaring + Play **Data Safety** bijwerken: de app ontvangt dan naam/e-mail/avatar van een derde partij |
+| **130-L** | Claude | Bevindingen naar `CLAUDE.md` |
+
+**Volgorde:** A t/m C zijn besluiten en blokkeren D. F blokkeert G. K moet af
+vóór de Play-inzending als 130 in de eerste release meegaat.