# OAuth Social Login for Partner Authentication

**Last Updated:** 2026-04-08

Google OAuth social login for Ordio Loop partner login and registration. Partners can sign in or register with their Google account; existing email/password accounts are linked when the email matches and is verified.

## Overview

- **Provider:** Google (OpenID Connect)
- **Flow:** Authorization code flow with CSRF state validation
- **Account linking:** Auto-link when email exists and is verified by provider
- **New users:** Created with `oauth_provider`, `oauth_id`; no password required

## GCP Setup (Your Actions)

### 1. Google Cloud Console

1. Go to [console.cloud.google.com](https://console.cloud.google.com)
2. Select or create project (e.g. "Ordio")
3. **OAuth consent screen** (APIs & Services → OAuth consent screen):
   - User type: External
   - App name: Ordio Loop
   - Support email: hady@ordio.com
   - Authorized domains: `ordio.com`, `www.ordio.com`
   - Scopes: `openid`, `email`, `profile` (requested in code)

### 2. Create OAuth Client

1. APIs & Services → Credentials → Create Credentials → OAuth client ID
2. Application type: **Web application**
3. Name: Ordio Loop
4. **Authorized JavaScript origins:**
   - `https://www.ordio.com`
   - `https://ordio.com`
   - `http://localhost:8003` (or your dev URL)
5. **Authorized redirect URIs:**
   - `https://www.ordio.com/partner/oauth/callback`
   - `http://localhost:8003/partner/oauth/callback` (dev)
6. Create → Copy Client ID and Client Secret

### 3. Credentials (Priority Order)

1. **Environment variables** (preferred): `GOOGLE_OAUTH_CLIENT_ID`, `GOOGLE_OAUTH_CLIENT_SECRET`
2. **Config file** `v2/config/oauth-credentials.php` when env vars are not available:

```php
<?php
return [
    'client_id' => 'your-client-id.apps.googleusercontent.com',
    'client_secret' => 'your-client-secret'
];
```

Copy from `oauth-credentials.php.example` if needed. **`oauth-credentials.php` must be in `.gitignore`** – never commit credentials. If the file was ever committed, rotate the client secret in GCP.

**No extra APIs to enable** – OpenID Connect userinfo uses built-in OAuth; no People API required.

## Login UX (repeat visits)

The authorization URL is built in `v2/helpers/oauth-google.php` with **`access_type=online`** and **no `prompt` parameter**.

- **`prompt=consent`** (previously used) tells Google to show the consent / “signing back in to …” screen **every** time. That is appropriate only when you must re-obtain consent or a refresh token on each auth.
- **Partner login** only needs `openid email profile` once per flow to verify identity; the app keeps the user signed in with its **own session**, not a stored Google refresh token. Omitting `prompt` lets Google use a **lighter flow** for users who already approved the app (first visit still shows normal consent if required).

If you later add features that need a **stored Google refresh token** (Calendar, Gmail, etc.), use a dedicated “Connect Google” step with `access_type=offline` and consider `prompt=consent` only on that linking flow—not on every dashboard login.

Optional: add **`prompt=select_account`** only if you want to force the account chooser (e.g. shared workstations); it adds friction for everyone else.

## URLs

| URL | Purpose |
|-----|---------|
| `/partner/oauth/google` | Initiates Google sign-in; redirects to Google |
| `/partner/oauth/callback` | Handles callback; exchanges code, fetches userinfo, links/creates partner |

## Account Linking Rules

- **Existing partner + same verified email:** Auto-link OAuth identity; sign in
- **Existing partner + unverified OAuth email:** Do NOT auto-link (account takeover risk); show error
- **New user:** Create partner with `oauth_provider`, `oauth_id`; `status` = active

## Terms Consent (GDPR)

OAuth registration requires explicit acceptance of Partner-Vereinbarung and Datenschutzerklärung:

- **Register page:** Terms checkbox appears above the Google button; user must check it before "Mit Google registrieren" works
- **OAuth init:** When user clicks Google from register with `?terms=1`, session stores `oauth_terms_accepted`
- **Callback:** For new users, callback validates `oauth_terms_accepted`; if missing, redirects to `/partner/register?error=terms_required`
- **Storage:** New OAuth partners get `terms_accepted_at` (ISO 8601) in partner JSON

Login flow ("Mit Google anmelden") does not require terms – only registration does.

## Welcome Email

New OAuth users receive the same welcome email as traditional users (after verification). Sent in `partner-oauth-callback.php` after partner creation; non-blocking (registration succeeds even if email fails).

## Password Reset (OAuth-Only Users)

OAuth-only partners (no `password_hash`) can request a password reset to **add** a password. They receive a different email copy explaining they can set a password for email login or continue using Google. Traditional users receive the standard reset email.

## Callback Error Codes

Redirected to `/partner/login?error=` (or `/partner/register?error=` for `terms_required`):

| Code | Meaning |
|------|---------|
| `terms_required` | New OAuth user without terms consent; redirects to register |
| `oauth_disabled` | OAuth not configured or disabled |
| `invalid_state` | CSRF state mismatch |
| `access_denied` | User cancelled consent |
| `email_unverified` | Existing account but OAuth email not verified |
| `deactivated` | Partner account deactivated |
| `account_inactive` | Partner account pending verification |
| `token_exchange_failed` | Failed to exchange code for token |
| `userinfo_failed` | Failed to fetch user info |
| `save_failed` | Failed to save partner data |

## Feature Flag

Set `AFFILIATE_OAUTH_GOOGLE_ENABLED` to `false` in `affiliate-config.php` to hide the Google button without removing credentials.

## Validation

Run before deployment:

```bash
php v2/scripts/dev-helpers/validate-oauth-setup.php
```

## Production Deployment (OAuth Not Showing)

**Symptom:** "Mit Google anmelden" appears on localhost but not on ordio.com.

**Root cause:** `isGoogleOAuthEnabled()` returns false when `GOOGLE_OAUTH_CLIENT_ID` or `GOOGLE_OAUTH_CLIENT_SECRET` is empty. Production typically has neither env vars nor `oauth-credentials.php` (that file is gitignored).

### Your Actions

1. **GCP Console – OAuth Client (not API key)**
   - OAuth uses **Client ID + Client Secret**, not an API key.
   - Go to [Google Cloud Console → Credentials](https://console.cloud.google.com/apis/credentials)
   - Create **OAuth client ID** (type: **Web application**)
   - **Authorized JavaScript origins:** `https://ordio.com`, `https://www.ordio.com`
   - **Authorized redirect URIs:** `https://ordio.com/partner/oauth/callback`, `https://www.ordio.com/partner/oauth/callback`
   - Copy Client ID and Client Secret

2. **Set credentials in production** (choose one):

   **Option A – Environment variables (recommended)**
   - `GOOGLE_OAUTH_CLIENT_ID` = your Client ID (e.g. `xxx.apps.googleusercontent.com`)
   - `GOOGLE_OAUTH_CLIENT_SECRET` = your Client Secret
   - Set via Apache `SetEnv`, PHP-FPM `env[]`, cPanel env vars, or your hosting panel.

   **Option B – Config file**
   - Copy `v2/config/oauth-credentials.php.example` to `v2/config/oauth-credentials.php` on the production server.
   - Fill in `client_id` and `client_secret`. Do not commit this file.

3. **Verify**
   - Run on production: `php v2/scripts/dev-helpers/validate-oauth-setup.php`
   - Reload `/partner/login` – the Google button should appear.

## Troubleshooting

- **Redirect URI mismatch:** Ensure Google Console redirect URI exactly matches `/partner/oauth/callback` (with trailing slash optional per .htaccess).
- **Button not shown:** Check `GOOGLE_OAUTH_CLIENT_ID` and `GOOGLE_OAUTH_CLIENT_SECRET` are set; check `AFFILIATE_OAUTH_GOOGLE_ENABLED`.
- **Token exchange fails:** Verify client secret; check logs for details (errors sent to hady@ordio.com).

## Related Documentation

- [PARTNER_AUTH_PAGES.md](PARTNER_AUTH_PAGES.md) – Auth page design
- [SECURITY.md](SECURITY.md) – OAuth credentials handling
- [DATA_GLOSSARY.md](DATA_GLOSSARY.md) – OAuth partner fields
