# WorkPulse SSO API — "Login with WorkPulse"

WorkPulse is the identity provider (IdP) for Weststar internal systems. Integrate
these endpoints so your app lets staff sign in with their WorkPulse account — no
separate passwords, one central directory. The flow is standard **OAuth 2.0
Authorization Code** (PKCE recommended).

**Base URL:** `https://workpulse.weststar-dev.com`

---

## 1. Get your client credentials

Each connected app needs a `client_id`, `client_secret` and a registered
`redirect_uri`. These are issued by a WorkPulse **superadmin**
(Identity & SSO → Connected Apps, or `php artisan passport:client`). Send the
superadmin your callback URL to register.

---

## 2. The sign-in flow (Authorization Code)

1. Your app redirects the user to **/oauth/authorize** with your `client_id`,
   `redirect_uri`, `response_type=code` and the scopes you need.
2. The user signs in to WorkPulse (and passes 2FA if their policy requires it)
   and approves access.
3. WorkPulse redirects back to your `redirect_uri` with a one-time `code` and
   your `state`.
4. Your app's backend exchanges that `code` at **/oauth/token** for an
   `access_token` (+ `refresh_token`).
5. Your app calls **/oauth/userinfo** with the access token to read the user's
   identity, then creates/locates the local session.

---

## 3. Endpoints

### GET https://workpulse.weststar-dev.com/oauth/authorize

Starts the login. Redirect the user's browser here. On success WorkPulse
redirects back to your `redirect_uri` with `?code=...&state=...`.

| Query param | Required | Description |
|---|---|---|
| `client_id` | Yes | Your app's client ID. |
| `redirect_uri` | Yes | Must exactly match a registered callback URL. |
| `response_type` | Yes | Always `code`. |
| `scope` | Yes | Space-separated, e.g. `openid profile email role`. |
| `state` | Recommended | Random anti-CSRF value; returned unchanged. |
| `code_challenge` | Recommended | PKCE challenge (base64url SHA-256 of a verifier). |
| `code_challenge_method` | With PKCE | Always `S256`. |

```
# Redirect the user's browser to:
https://workpulse.weststar-dev.com/oauth/authorize?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yourapp.weststar-dev.com/auth/workpulse/callback
  &response_type=code
  &scope=openid profile email role
  &state=RANDOM_STATE
```

### POST https://workpulse.weststar-dev.com/oauth/token

Exchanges the authorization `code` for tokens. Call this server-to-server.

| Body param | Required | Description |
|---|---|---|
| `grant_type` | Yes | `authorization_code` |
| `client_id` / `client_secret` | Yes | Your app credentials. |
| `redirect_uri` | Yes | Same value used in the authorize step. |
| `code` | Yes | The one-time code from the callback. |
| `code_verifier` | With PKCE | The original verifier for your `code_challenge`. |

```
curl -X POST https://workpulse.weststar-dev.com/oauth/token \
  -H "Accept: application/json" \
  -d grant_type=authorization_code \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d redirect_uri=https://yourapp.weststar-dev.com/auth/workpulse/callback \
  -d code=AUTH_CODE

# Response
{
  "token_type": "Bearer",
  "expires_in": 3600,
  "access_token": "eyJ0eXAi...",
  "refresh_token": "def502..."
}
```

### POST https://workpulse.weststar-dev.com/oauth/token (refresh)

Get a fresh access token when the old one expires (access tokens last 60 min;
refresh tokens 30 days).

```
curl -X POST https://workpulse.weststar-dev.com/oauth/token \
  -d grant_type=refresh_token \
  -d refresh_token=YOUR_REFRESH_TOKEN \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d scope="openid profile email role"
```

### GET https://workpulse.weststar-dev.com/oauth/userinfo

Returns the signed-in user's identity claims, taken directly from their
WorkPulse `users` record. Send the access token as a Bearer header. Each claim
maps 1:1 to a real `users` column, and **a claim is only present when that
column exists and has a value** — there are no derived or empty fields. `sub`
(the user id) is always returned.

```
curl https://workpulse.weststar-dev.com/oauth/userinfo \
  -H "Authorization: Bearer ACCESS_TOKEN"

# Example response — illustrative values; real values come from the user's record.
# These keys reflect the columns that actually exist on this WorkPulse users
# table. Only `sub` is guaranteed; each other key appears only when its column
# has a value.
{
    "sub": "42",
    "first_name": "Nadia",
    "last_name": "Rahman",
    "employee_code": "WSE1042",
    "job_title": "Engineer",
    "department": "Maintenance",
    "phone": "+60123456789",
    "email": "nadia@weststar-dev.com",
    "role": "employee"
}
```

---

## 4. Scopes

Request only what you need; `/oauth/userinfo` filters its response by the
granted scopes.

| Scope | Grants |
|---|---|
| `openid` | Authenticate via WorkPulse SSO (always include). Returns `sub`. |
| `profile` | Profile columns when set: `name`, `first_name`, `last_name`, `employee_code`, `job_title`, `department`, `phone`. |
| `email` | The user's email address. |
| `role` | WorkPulse role (`employee`, `manager`, `hr`, `finance`, `ceo`, `admin`, `superadmin`). |

---

## 5. Integrating from your app

Your app is the OAuth *client* — it does not need Laravel Passport. Use any
standard OAuth2/OIDC client:

- **Laravel** — Laravel Socialite with a generic OAuth2 provider (or
  `league/oauth2-client`), pointed at the authorize/token/userinfo URLs above.
- **Node / SPA** — any OpenID/OAuth2 library; Authorization Code + PKCE.
- **Mobile** — AppAuth (iOS/Android) with PKCE.

**Endpoint summary**
- authorize: `https://workpulse.weststar-dev.com/oauth/authorize`
- token: `https://workpulse.weststar-dev.com/oauth/token`
- userinfo: `https://workpulse.weststar-dev.com/oauth/userinfo`

---

© 2026 Weststar Engineering · WorkPulse SSO — one identity for every internal system.