Skip to content

Instantly share code, notes, and snippets.

@R4wm
Last active April 23, 2026 23:32
Show Gist options
  • Select an option

  • Save R4wm/58355c4e11797cfe8705225197f29682 to your computer and use it in GitHub Desktop.

Select an option

Save R4wm/58355c4e11797cfe8705225197f29682 to your computer and use it in GitHub Desktop.
IDP SSO Login Flow — ASCII flowchart + process outline (Play / Parler Social)

IDP SSO Login Flow — Play / Parler Social

                                    SESSION CHECK PATH (existing IDP session)
                                    =========================================

  MOBILE APP                        IDP SERVICE                         WEB CALLBACK BRIDGE              GO BACKEND
  (play-phoenix-mobile)             (parler-identity-service)           (playtv-vue-ui /                 (go-social-api)
                                                                         social-vue-ui)
  +---------------------------+
  | User taps                 |
  | "Sign in with Parler ID"  |
  +------------+--------------+
               |
               | WebBrowser.openAuthSessionAsync()
               | Opens system browser (CCT / SFSafariVC)
               v
               |     GET /session/check?client_id={id}&platform=mobile
               +------------------------------------------------------------>+---------------------------+
                                                                             | Read idp_token cookie     |
                                                                             | (HttpOnly, Secure)        |
                                                                             | Contains IDP JWT          |
                                                                             +------------+--------------+
                                                                                          |
                                                                                          v
                                                                             +---------------------------+
                                                                             | Verify IDP JWT (RSA-256)  |
                                                                             | Claims: ulk, identifier,  |
                                                                             | session_id, exp, iat      |
                                                                             +------------+--------------+
                                                                                          |
                                                                              +-----------+-----------+
                                                                              |  Session valid?       |
                                                                              +-----------+-----------+
                                                                                 YES |          | NO
                                                                                     v          v
                                                                             +-------------+  Redirect with
                                                                             | Show account|  #action=unauthorized
                                                                             | picker      |  (goes to OTP path)
                                                                             +------+------+
                                                                                    |
                                                                                    | User selects account
                                                                                    | POST /api/v1/session/continue
                                                                                    v
                                                                             +---------------------------+
                                                                             | Encrypt tokens            |
                                                                             | AES-256-GCM               |
                                                                             | Key: SHA256(client_secret)|
                                                                             | Nonce: 12 random bytes    |
                                                                             +------------+--------------+
                                                                                          |
                                                                                          | HTTP redirect to platform redirect_url
                                                                                          | Fragment: #action=ok&payload={base64(nonce+ciphertext+tag)}
                                                                                          v
                                                                             +---------------------------+
                                                                             | IDPCallback.vue           |
                                                                             | Parse URL fragment        |
                                                                             | Extract encrypted payload |
                                                                             +------------+--------------+
                                                                                          |
                                                                                          | POST /v3/{app}/authenticate
                                                                                          | Body: { "payload": "<base64>" }
                                                                                          |
                                                                                          | (via Laravel proxy if Play)
                                                                                          v
                                                                                                                         +---------------------------+
                                                                                                                         | Decrypt AES-256-GCM       |
                                                                                                                         | Extract IDP JWT           |
                                                                                                                         +------------+--------------+
                                                                                                                                      |
                                                                                                                                      v
                                                                                                                         +---------------------------+
                                                                                                                         | Verify IDP JWT (RSA-256)  |
                                                                                                                         | Fetch JWKS from IDP       |
                                                                                                                         | Validate: iss, exp, nbf   |
                                                                                                                         | (NO aud check)            |
                                                                                                                         +------------+--------------+
                                                                                                                                      |
                                                                                                                                      v
                                                                                                                         +---------------------------+
                                                                                                                         | Lookup user by ULK        |
                                                                                                                         | or bootstrap by           |
                                                                                                                         | email/phone               |
                                                                                                                         +------------+--------------+
                                                                                                                                      |
                                                                                                                                      v
                                                                                                                         +---------------------------+
                                                                                                                         | Mint Passport tokens      |
                                                                                                                         | JWT (HS256)               |
                                                                                                                         | + refresh token           |
                                                                                                                         +------------+--------------+
                                                                                                                                      |
                                                                                          +-------------------------------------------+
                                                                                          | Response 200: { access_token, refresh_token }
                                                                                          v
                                                                             +---------------------------+
                                                                             | Build deep link:          |
                                                                             | {scheme}://auth/callback  |
                                                                             | ?access_token={PASSPORT}  |
                                                                             | &refresh_token={REFRESH}  |
                                                                             | &auth_type=idp_session    |
                                                                             +------------+--------------+
                                                                                          |
               +<------------------------------------------------------------- window.location.href = deepLink
               |
               v
  +---------------------------+
  | Parse callback params     |
  | auth_type = idp_session   |
  | Tokens are Passport JWTs  |
  | Store directly            |
  +------------+--------------+
               |
               v
  +---------------------------+
  | User is logged in         |
  | AsyncStorage persists     |
  | tokens for API calls      |
  +---------------------------+



                                    OTP LOGIN PATH (no existing IDP session)
                                    ========================================

  MOBILE APP                        IDP SERVICE                         WEB CALLBACK BRIDGE              GO BACKEND

  +---------------------------+
  | WebBrowser still open     |
  | IDP returned              |
  | #action=unauthorized      |
  +------------+--------------+
               |
               v
                                                                             +---------------------------+
                                                                             | IDPCallback.vue detects   |
                                                                             | action=unauthorized       |
                                                                             | Shows IDP login form      |
                                                                             +------------+--------------+
                                                                                          |
                                                                                          | User enters email/phone
                                                                                          v
                                                                             +---------------------------+
                                                                             | IDP sends OTP code        |
                                                                             | User enters code          |
                                                                             | IDP verifies OTP          |
                                                                             +------------+--------------+
                                                                                          |
                                                                                          | Sets idp_token cookie
                                                                                          | Redirects with IDP JWT (not encrypted)
                                                                                          v
                                                                             +---------------------------+
                                                                             | Deep link to app:         |
                                                                             | {scheme}://auth/callback  |
                                                                             | ?access_token={IDP_JWT}   |
                                                                             | &auth_type=idp_login      |
                                                                             +------------+--------------+
                                                                                          |
               +<------------------------------------------------------------------------|
               |
               v
  +---------------------------+
  | Parse callback params     |
  | auth_type = idp_login     |
  | Token is IDP JWT          |
  | (NOT Passport yet)        |
  +------------+--------------+
               |
               | POST /v3/{app}/login/idp
               | Body: { "token": "<IDP JWT>" }
               +----------------------------------------------------------------------------------->+---------------------------+
                                                                                                    | Verify IDP JWT (RSA-256)  |
                                                                                                    | Lookup/bootstrap user     |
                                                                                                    | Mint Passport tokens      |
                                                                                                    +------------+--------------+
                                                                                                                 |
               +<-----------------------------------------------------------------------------------------------|
               | Response 200: { access_token, refresh_token }
               v
  +---------------------------+
  | Store Passport tokens     |
  | User is logged in         |
  +---------------------------+



                                    SOCIAL LOGIN PATH (Google / Apple via IDP)
                                    ==========================================

  WEB BROWSER                       IDP SERVICE                         WEB LOGIN PAGE                   GO BACKEND

  +---------------------------+
  | User on login page        |
  | (web or mobile bridge)    |
  +------------+--------------+
               |
               | Clicks "Sign in with Google" or "Sign in with Apple"
               v
  +---------------------------+
  | Google: GIS SDK popup     |
  |   returns credential      |
  |   (id_token JWT)          |
  | Apple: AppleID.auth popup |
  |   returns id_token        |
  +------------+--------------+
               |
               | POST {IDP_BASE_URL}/api/v1/auth/social/login
               | Body: { client_id, provider: "google"|"apple", id_token }
               | credentials: "include" (sends IDP cookies)
               +------------------------------------------------------------>+---------------------------+
                                                                             | Verify provider id_token  |
                                                                             | Google: JWKS validation   |
                                                                             | Apple: JWKS validation    |
                                                                             | Check aud matches         |
                                                                             |   platform google_client_id|
                                                                             |   or apple_client_id      |
                                                                             +------------+--------------+
                                                                                          |
                                                                             +------------+--------------+
                                                                             | Lookup by provider_sub    |
                                                                             | in external_providers     |
                                                                             +------------+--------------+
                                                                                          |
                                                                              +-----------+-----------+
                                                                              | User exists?          |
                                                                              +-----------+-----------+
                                                                              YES |              | NO
                                                                                  v              v
                                                                          +------------+  +------------------+
                                                                          | Login      |  | AUTO-CREATE:     |
                                                                          | existing   |  | - New identity   |
                                                                          | identity   |  | - linked_identity|
                                                                          +------------+  | - external_prov  |
                                                                                          | - profile        |
                                                                                          +------------------+
                                                                                          |
                                                                             +------------+--------------+
                                                                             | Return IDP JWT            |
                                                                             | { access_token,           |
                                                                             |   refresh_token,          |
                                                                             |   expires_in }            |
                                                                             +------------+--------------+
                                                                                          |
               +<------------------------------------------------------------|
               |
               v
  +---------------------------+     +---------------------------+
  | WEB FLOW:                 |     | MOBILE BRIDGE FLOW:       |
  | POST /v3/{app}/login/idp  |     | Build deep link:          |
  | { token: IDP_JWT }        |     | {scheme}://auth/callback  |
  | Go backend verifies,      |     | ?access_token={IDP_JWT}   |
  | mints Passport tokens     |     | &auth_type=idp_login      |
  | fetchUserState()          |     | window.location.href =    |
  | User logged in on web     |     |   deepLink                |
  +---------------------------+     +---------------------------+
                                                |
                                                v
                                    Mobile app exchanges via
                                    POST /v3/{app}/login/idp
                                    (same as OTP path)



                                    LOGOUT PATH (from IDP account picker)
                                    ======================================

  MOBILE BROWSER                    IDP SERVICE                         WEB LOGIN PAGE

  +---------------------------+
  | User on IDP account       |
  | picker page               |
  | (id.parler.com or         |
  |  id.dev.parler.com)       |
  +------------+--------------+
               |
               | User taps "Log out"
               v
               | POST /api/v1/auth/logout
               | Body: { client_id: "..." }
               +------------------------------------------------------------>+---------------------------+
                                                                             | Revoke sessions on device |
                                                                             | Clear idp_token cookie    |
                                                                             | Clear idp_device_id cookie|
                                                                             +------------+--------------+
                                                                                          |
                                                                                          | Lookup platform_url from
                                                                                          | platform_settings table
                                                                                          | (key = "platform_url")
                                                                                          v
                                                                             +---------------------------+
                                                                             | Return JSON:              |
                                                                             | { redirect: true,         |
                                                                             |   redirect_url:           |
                                                                             |   "<platform_url>" }      |
                                                                             +------------+--------------+
                                                                                          |
               +<------------------------------------------------------------|
               | IDP frontend appends ?logout=true
               | window.location.href = redirect_url
               v
                                                                             +---------------------------+
                                                                             | Web login page renders    |
                                                                             | (mobile IDP login form)   |
                                                                             | User can sign in again    |
                                                                             +---------------------------+

  NOTE: platform_url must include the full mobile login path with query params:
    Play:   https://play.parler.com/login?platform=mobile&redirect_url=playtv%3A%2F%2Fauth%2Fcallback
    Parler: https://app.parler.com/idp/login?platform=mobile&redirect_url=parlersocial%3A%2F%2Fauth%2Fcallback

  platform_url = where to go AFTER LOGOUT (home base)
  redirect_url = where to go AFTER LOGIN (token callback) — do NOT use for logout

Process Outline

Step 1: Mobile App Opens IDP

  • User taps "Sign in with Parler ID" (or Play equivalent) on the login screen
  • App calls WebBrowser.openAuthSessionAsync() which opens a system browser
    • Android: Chrome Custom Tab
    • iOS: SFSafariViewController
  • URL opened: {IDP_BASE_URL}/session/check?client_id={IDP_CLIENT_ID}&platform=mobile
  • App begins listening for a deep link callback at playtv://auth/callback or parlersocial://auth/callback

Step 2: IDP Checks for Existing Session

  • IDP reads the idp_token cookie (HttpOnly, Secure, SameSite=Strict)
    • This cookie contains an IDP JWT signed with RSA-256
    • Claims include: ulk (user ID), identifier (email/phone), session_id, exp, iat, nbf, jti
  • IDP also reads idp_device_id cookie for trusted device tracking
  • If no cookie or expired session: returns redirect_url with #action=unauthorized (skips to OTP path)
  • If valid session: proceeds to account picker

Step 3: Account Picker (Session Check Path)

  • IDP returns JSON with linked accounts for this platform:
    • linked_accounts[] — each has id, identifier, name, ulk
    • default_identifier, default_ulk, identifier_type
  • The platform=mobile param forces the account picker to show even for single accounts
  • User selects an account
  • Frontend calls POST /api/v1/session/continue with the selected identifier and client_id
  • IDP generates a verify token (JWT with sub set to the selected account's ULK)

Step 4: IDP Encrypts Tokens and Redirects

  • IDP encrypts the token payload using AES-256-GCM:
    • Key derivation: if client_secret is exactly 32 bytes, use raw; otherwise SHA-256 hash it to 32 bytes
    • Nonce: 12 random bytes (standard GCM)
    • Plaintext: JSON { access_token, refresh_token, expires_in, refresh_expires_in }
    • Output: base64(nonce + ciphertext + GCM_tag)
  • IDP redirects browser to the platform's redirect_url from the database:
    • Play: https://play.parler.com/idp/callback (prod) or https://playforte.ws/idp/callback (dev)
    • Parler Social: https://app.parler.com/idp/callback (prod) or https://app.forte.ws/idp/callback (dev)
  • URL fragment: #action=ok&payload={encrypted_base64}
  • IMPORTANT: the + characters in base64 must NOT be corrupted by URLSearchParams parsing

Step 5: Web Callback Bridge Receives Redirect

  • IDPCallback.vue (on Vercel) parses the URL fragment (#) parameters
  • Extracts action and payload from the fragment
  • For action=ok with a payload:
    • Sends POST /v3/{app}/authenticate to the API backend
    • Request body: { "payload": "<base64_encrypted_string>" }
    • For Play: goes through Laravel proxy (play-api.parler.com -> Go backend)
    • For Parler Social: goes through Laravel proxy (api.forte.ws -> Go backend) or direct

Step 6: Go Backend Decrypts Payload

  • IDPAuthenticateHandler receives the encrypted payload
  • Decryption steps:
    • Base64 decode (tries standard encoding, falls back to URL encoding)
    • Split: first 12 bytes = nonce, rest = ciphertext+tag
    • Derive key from IDP_CLIENT_SECRET_{APP} (same SHA-256 logic as IDP)
    • aes.NewCipher(key) -> cipher.NewGCM(block) -> gcm.Open()
  • Extracts decrypted JSON: { access_token, refresh_token, expires_in, refresh_expires_in }
  • The access_token here is an IDP JWT (not yet a Passport token)

Step 7: Go Backend Verifies IDP JWT

  • Decodes JWT header to get kid (key ID)
  • Fetches IDP's JWKS from IDP_JWKS_URL (e.g., https://id.parler.com/.well-known/jwks.json)
    • JWKS is cached per-app with 1-hour TTL
  • Verifies RSA-256 signature using the matching public key
  • Validates claims:
    • iss must match IDP_ISSUER config
    • exp must be in the future
    • nbf must be satisfied
    • aud check is SKIPPED (IDP tokens have no aud claim — do NOT set IDP_AUDIENCE)

Step 8: Go Backend Looks Up / Bootstraps User

  • Extracts ulk from JWT claims (sub for session-check tokens, ulk for OTP tokens)
  • Lookup order:
    1. FindUserByExternalIdentity("idp", ulk) — exact ULK match
    2. If not found, bootstrap by email or phone from JWT claims
    3. If bootstrapping: CreateExternalIdentityLink(user_id, "idp", ulk) to link for future logins
  • If no user found by any method: returns 422 "No account found for this {field}. Please sign up first."
    • This is the create-account gap — IDP creates identities but Go API requires a pre-existing local user
    • See "Account Creation Gap" section below

Step 9: Go Backend Mints Passport Tokens

  • Calls issueTokenForUser(user, "idp")
  • Passport JWT (HS256):
    • Claims: sub (user ID), ulk, email, iat, exp, jti
    • Expiry: 3600 seconds (1 hour)
  • Refresh token: expiry 43200 seconds (12 hours)
  • Response: { access_token, refresh_token, expires_in, refresh_expires_in }

Step 10: Web Bridge Deep Links Back to Mobile App

  • IDPCallback.vue receives the Passport tokens from the backend response
  • Builds a deep link URL:
    • playtv://auth/callback?access_token={PASSPORT}&refresh_token={REFRESH}&expires_in=3600&auth_type=idp_session
    • or parlersocial://auth/callback?... for Parler Social
  • Sets window.location.href to the deep link
  • System browser closes, app receives the URL

Step 11: Mobile App Stores Tokens

  • App parses callback URL query parameters
  • Checks auth_type:
    • idp_session: tokens are already Passport tokens — store directly
    • idp_login: token is IDP JWT — needs exchange first (see OTP path below)
  • Stores tokens via setTokens() in AsyncStorage
  • Fetches user profile: GET /user with Authorization: Bearer {passport_token}
  • User is logged in

OTP Login Path (No Existing Session)

When the user has no idp_token cookie (first login or expired session):

  • IDP returns #action=unauthorized in the redirect
  • Web bridge shows the IDP login form in the browser
  • User enters email or phone number
  • IDP sends a one-time password (OTP) via email/SMS
  • User enters OTP code
  • IDP verifies OTP, creates a session, sets idp_token cookie
  • IDP redirects with the IDP JWT (may or may not be encrypted depending on platform config)
  • Deep link back to app: {scheme}://auth/callback?access_token={IDP_JWT}&auth_type=idp_login
  • Mobile app detects auth_type=idp_login
  • Mobile app calls POST /v3/{app}/login/idp with { "token": "<IDP_JWT>" }
  • Go backend verifies JWT, looks up user, mints Passport tokens (same as steps 7-9)
  • Mobile stores Passport tokens, user is logged in

IDP auto-creates identities: If the email/phone is new to IDP, StartOTP automatically creates an identity and linked_identity before sending the OTP code (otp_service.go:370-374). No separate registration step is needed on the IDP side.


Social Login Path (Google / Apple via IDP)

The web login pages (playtv-vue-ui, social-vue-ui) handle Google and Apple sign-in via the IDP social login endpoint. This works for both direct web login and the mobile bridge flow.

Web Flow

  1. User clicks "Sign in with Google" (GIS SDK renderButton) or "Sign in with Apple" (AppleID.auth popup)
  2. SDK returns an id_token (JWT credential)
  3. Frontend calls POST {IDP_BASE_URL}/api/v1/auth/social/login with { client_id, provider, id_token }
  4. IDP verifies the id_token against provider JWKS, checks aud matches platform's google_client_id or apple_client_id
  5. IDP auto-creates identity if new (creates identity + linked_identity + external_provider + profile)
  6. Returns IDP JWT { access_token, refresh_token, expires_in }
  7. Frontend exchanges via POST /v3/{app}/login/idp { token: IDP_JWT } → Go backend mints Passport tokens
  8. fetchUserState() → user logged in

Mobile Bridge Flow

Same steps 1-6, but instead of exchanging tokens on web: 7. Frontend builds deep link: {scheme}://auth/callback?access_token={IDP_JWT}&auth_type=idp_login 8. window.location.href = deepLink 9. Mobile app exchanges via POST /v3/{app}/login/idp → Passport tokens → logged in

Key Config for Social Login

  • google_client_id in IDP platform_settings must match VITE_GOOGLE_CLIENT_ID in the web app build
  • apple_client_id in IDP platform_settings must match VITE_APPLE_CLIENT_ID in the web app build
  • Apple return URLs must be registered on the Apple Services ID for every domain serving the login page
  • Google JS origins must be registered in GCP for every domain serving the login page
  • IDP CORS must allow every domain that calls the social login endpoint

Logout Path (from IDP Account Picker)

When the user is on the IDP page (e.g., id.parler.com) and taps logout:

  1. IDP frontend calls POST /api/v1/auth/logout with { client_id } (session_check_handler.go:695)
  2. IDP backend revokes all sessions on the device, clears idp_token and idp_device_id cookies
  3. Looks up platform_url from platform_settings table (key = platform_url) (session_check_handler.go:740)
  4. Returns { redirect: true, redirect_url: "<platform_url>" }
  5. IDP frontend appends ?logout=true and redirects: window.location.href = redirect_url

IMPORTANT: platform_url must include the full mobile login path with query parameters so the user lands on the mobile IDP login page, not the web feed:

  • Play: https://play.parler.com/login?platform=mobile&redirect_url=playtv%3A%2F%2Fauth%2Fcallback
  • Parler: https://app.parler.com/idp/login?platform=mobile&redirect_url=parlersocial%3A%2F%2Fauth%2Fcallback

Key distinction:

  • platform_url = where to go after logout (home base for the platform)
  • redirect_url = where to go after login (callback URL for session check) — do NOT change this for logout fixes

Native mobile app logout is completely separate — the app clears tokens, resets state, and the navigator shows the native LoginScreen. No browser involved, no web redirect.


Account Creation Gap

IDP and Go API handle new users differently:

Component New user via Google/Apple New user via email/phone OTP
IDP Auto-creates identity + linked_identity + external_provider Auto-creates identity + linked_identity, sends OTP
Go API Requires existing local user → 422 if not found Requires existing local user → 422 if not found

The gap: IDP happily creates identities for new users, but Go API's IDPVerifyHandler (idp_verify.go:404) returns "No account found for this {field}. Please sign up first." when there's no matching local user in the social-api database.

Options:

  1. Auto-create in Go API — Add a branch at idp_verify.go:404 to create a local user from IDP claims. Challenges: username generation, ToS acceptance, phone wireless check.
  2. Client-side signup flow — Frontend catches the 422, redirects to a signup form pre-filled with IDP claims, user picks a username, normal CreateUser flow, then re-attempts IDP login. Preserves all signup gates.

IDP also has a POST /auth/register endpoint, but it requires client_secret (server-to-server only) and is designed for password-based registration, not the OTP-only flow.


iOS Universal Links / AASA Caveat

The web apps serving the login pages may have Apple App Site Association (AASA) files with wildcard rules that intercept URLs. If /login or /idp/callback is not excluded, iOS will try to open the URL in the native app instead of rendering the web page in the browser. This breaks the IDP flow.

Required AASA exclusions:

{
  "paths": [
    "NOT /idp/callback",
    "NOT /login",
    "*"
  ]
}

Without these, iOS intercepts the logout redirect URL and the OTP callback, preventing the browser from rendering the login page.


Double-Submit / OTP Verify Race

The OTP verify endpoint (POST /api/v1/auth/otp/verify) consumes the challenge on the first successful call. If the frontend fires the request twice (double-tap, slow network retry), the second call returns 422 "challenge not found or expired."

Mitigation: Disable the verify/submit button after the first tap. The IDPLogin.vue components should set loading=true and disabled on the button immediately when clicked.


Token Types Summary

Token Format Signed With Where It Lives Purpose
idp_token cookie JWT RSA-256 (IDP private key) Browser cookie (HttpOnly, Secure) IDP session — proves user authenticated with IDP
IDP verify token JWT RSA-256 (IDP private key) Encrypted in redirect payload Short-lived token for a specific account selection
Encrypted payload AES-256-GCM blob Platform client_secret URL fragment in redirect Wraps IDP JWT for secure transit through browser
Passport access token JWT HS256 (Go backend secret) Mobile app AsyncStorage App session — used for all API calls
Passport refresh token opaque string N/A Mobile app AsyncStorage Used to refresh expired Passport access token
Google id_token JWT RSA-256 (Google) Transient (GIS SDK callback) Proves Google authentication, exchanged via IDP
Apple id_token JWT RSA-256 (Apple) Transient (AppleID.auth callback) Proves Apple authentication, exchanged via IDP

Critical Environment Variables

Component Variable Purpose
Go Backend IDP_CLIENT_SECRET_{APP} Must match IDP platform's client_secret exactly
Go Backend IDP_JWKS_URL Where to fetch IDP public keys for JWT verification
Go Backend IDP_ISSUER Expected iss claim in IDP JWTs
Go Backend IDP_LOGIN_ENABLED_{APP} Must be true to enable IDP login
Go Backend IDP_AUDIENCE_{APP} Must be UNSET — IDP tokens have no aud claim
Laravel GO_API_ACTIVE Must be true or all /v3/* routes return 503
Laravel GO_API_BASE_URL Must point to correct environment's Go backend
Web Bridge VITE_API_HOST API endpoint (baked at Vite build time!)
Web Bridge VITE_IDP_BASE_URL IDP URL (baked at build time)
Web Bridge VITE_IDP_CLIENT_ID Platform client_id (baked at build time)
Web Bridge VITE_GOOGLE_CLIENT_ID Must match IDP platform google_client_id
Web Bridge VITE_APPLE_CLIENT_ID Must match IDP platform apple_client_id
Mobile EXPO_PUBLIC_IDP_BASE_URL IDP URL for session check
Mobile EXPO_PUBLIC_IDP_CLIENT_ID Platform client_id registered in IDP
Mobile EXPO_PUBLIC_PLAY_WEB_URL Web app URL for OTP fallback (defaults to prod if unset!)
Mobile EXPO_PUBLIC_PARLER_WEB_URL Parler web app URL for OTP fallback (defaults to prod if unset!)
IDP DB platforms.client_secret Must match Go backend's IDP_CLIENT_SECRET_{APP}
IDP DB platform_settings.redirect_url Must point to exact /idp/callback route on web bridge
IDP DB platform_settings.platform_url Logout redirect — must include full mobile login path with query params
IDP DB platform_settings.google_client_id Must match VITE_GOOGLE_CLIENT_ID in web app build
IDP DB platform_settings.apple_client_id Must match VITE_APPLE_CLIENT_ID in web app build

IDP CORS Requirements

Every domain that serves a login page calling IDP endpoints must be in IDP's CORS_ALLOWED_ORIGINS:

Dev IDP (id.dev.parler.com):

  • https://app.forte.ws, https://forte.ws (social-vue-ui)
  • https://playforte.ws, https://app.playforte.ws, https://play.forte.ws (playtv-vue-ui)
  • https://play.dev.parler.com (play alias)
  • http://localhost:* (local dev)

Prod IDP (id.parler.com):

  • https://app.parler.com (social-vue-ui)
  • https://play.parler.com, https://playtv.parler.com (playtv-vue-ui)
  • Other platform domains (pay, shop, ads)

CORS is configured via CORS_ALLOWED_ORIGINS env var on the IDP workload. Changes via CPLN UI can silently modify other workload config — always commit to repo and deploy via CI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment