SA
Sanchalan Docs
Parichay SSO

Parichay (sb-iam) SSO

sb-iam is the SDS-deployed Laravel-Passport instance that backs all setu apps' authentication. We refer to it as "Parichay" externally — same OAuth2 + PKCE protocol. Sanchalan supports both the web (session) flow and the mobile (token) flow. Wired in code; flipping AUTH_MODE=sso in the env activates it.

What's shipped

  • SsoController with five endpoints: web redirect + callback, mobile mobileRedirect + mobileExchange, public mode probe.
  • User auto-link by email match against attendance_employee_register at SSO callback time.
  • Sanctum personal-access-tokens minted on mobile exchange.
  • Allowlist for mobile_redirect_uris (CSV in env) — exact-match required.

Web SSO

sequenceDiagram participant U as Browser participant Be as backend participant IAM as sb-iam (Parichay) U->>Be: GET /admin (no session) Be-->>U: 302 /admin/login U->>Be: GET /admin/login (sso_enabled=true) Be-->>U: 302 /api/auth/sso/redirect Be->>Be: store state + code_verifier in session Be-->>U: 302 sb-iam /oauth/authorize?… U->>IAM: login (Parichay UI) IAM-->>U: 302 /api/auth/sso/callback?code=…&state=… U->>Be: GET /api/auth/sso/callback?code=…&state=… Be->>Be: pull state + verifier from session, verify state matches Be->>IAM: POST /oauth/token (client_id, code, code_verifier) IAM-->>Be: {access_token} Be->>IAM: GET /api/auth/user (Bearer) IAM-->>Be: {email, name, role, parichay_id} Be->>Be: User::firstOrCreate, link to employee_register by email Be->>Be: Auth::guard('web')->login(user) Be-->>U: 302 / (lands on /admin)

Mobile SSO

sequenceDiagram participant App as Sanchalan app participant Be as backend participant IAM as sb-iam App->>App: PKCE generate (verifier, challenge, state) App->>Be: POST /api/auth/sso/mobile/redirect
{code_challenge, state, redirect_uri} Be->>Be: validate redirect_uri allowlist Be-->>App: {authorization_url} App->>IAM: open authorization_url in flutter_web_auth_2 IAM-->>App: redirect sanchalan-attendance://auth/callback?code=…&state=… App->>Be: POST /api/auth/sso/mobile/exchange
{code, code_verifier, redirect_uri} Be->>IAM: POST /oauth/token (server-to-server) IAM-->>Be: {access_token} Be->>IAM: GET /api/auth/user IAM-->>Be: {email, name, role} Be->>Be: User::firstOrCreate + employee_id link Be->>Be: createToken('mobile', [scopes]) — Sanctum PAT Be-->>App: {access_token, user} App->>App: TokenStore.save (flutter_secure_storage)

Operator setup

  1. In sb-iam admin, create two OAuth clients:
    • Web (confidential) — redirect https://attendance.rajyasabha.digital/api/auth/sso/callback
    • Mobile (public PKCE) — redirect sanchalan-attendance://auth/callback
  2. Populate Sanchalan .env:
    AUTH_MODE=sso
    SBIAM_PUBLIC_URL=https://ft.rajyasabha.digital/iam
    SBIAM_BASE_URL=http://sb-iam-app:80                     # docker-internal
    SBIAM_CLIENT_ID=...                                      # web
    SBIAM_REDIRECT_URI=https://attendance.rajyasabha.digital/api/auth/sso/callback
    SBIAM_USERINFO_PATH=/api/auth/user
    SBIAM_SCOPES=openid profile attendance.punch attendance.leave
    SBIAM_MOBILE_CLIENT_ID=...                              # mobile PKCE
    SBIAM_MOBILE_REDIRECT_URIS=sanchalan-attendance://auth/callback
  3. Recreate the app container.
  4. Verify GET /api/auth/mode{"data":{"auth_mode":"sso","sso_enabled":true,"redirect_url":"…/api/auth/sso/redirect"}}.
  5. Visit /admin — should bounce through sb-iam and land back as the SSO'd user.

What sb-iam returns

The userinfo endpoint contract is:

GET /api/auth/user                 (Bearer access_token)
=>
{
  "user": {
    "id":          "uuid",         // Parichay subject id
    "email":       "you@rajyasabha.digital",
    "name":        "Kushal Pathak",
    "role":        "admin",        // Sanchalan reads this for RBAC
    "parichay_id": "…"             // optional; mirrors id
  }
}

SsoController's mapUser():

  • Looks up existing User by parichay_id first (handles email change).
  • Falls back to email lookup.
  • If no User exists, creates one with a random password (never used).
  • Auto-links to attendance_employee_register by email match.
  • Sets the role from the IDP claim (or config/sso.email_role override).

RBAC handoff

The role string from sb-iam is the contract. config/permissions.php maps roles to permission keys; route middleware uses keys, not roles directly. Common mappings:

sb-iam rolePermissions granted
admineverything (admin-attendance, admin-reports, leave-approver, can_enroll_face, can_manage_*)
hr_adminadmin-attendance, admin-reports, can_enroll_face, can_manage_holidays
attendance_adminadmin-attendance, can_manage_geo_fences
reports_vieweradmin-reports only
reporting_officerleave-approver only
punch_usercan_punch, can_apply_leave, can_view_own_attendance

Smoke test path (when SSO is enabled)

# 1) Confirm endpoint visibility
curl /api/auth/mode
=> {"data":{"auth_mode":"sso","sso_enabled":true,"redirect_url":"…"}}

# 2) Hit the redirect — should 302 to sb-iam
curl -I /api/auth/sso/redirect
=> HTTP/1.1 302 Found
   Location: https://ft.rajyasabha.digital/iam/oauth/authorize?response_type=code&…

# 3) After SSO complete, browser session is set; subsequent /admin requests succeed.

# 4) Mobile flow:
curl -X POST -H "Content-Type: application/json" -d '{
  "code_challenge":"…",
  "state":"…",
  "redirect_uri":"sanchalan-attendance://auth/callback"
}' /api/auth/sso/mobile/redirect
=> {"authorization_url":"https://…/oauth/authorize?…"}

Failure modes + audit

  • sso_state_mismatch — CSRF protection. Logged + redirect to /login?sso_error=….
  • sso_token_exchange_failed — sb-iam rejected the code. Audit'd as auth.sso.token_exchange_failed.
  • sso_local_user_inactive — User exists locally but active=false. Operator must reactivate or delete.
  • All successful SSO logins emit auth.login with via: sso or via: sso_mobile in the audit chain.