[FEAT]: Simplified IDIR Account Onboarding & Team Provisioning
- Dominant language
- No language data
- Stars
- 0
- Forks
- 4
- Avg merge
- 1m
- Merged PRs (30d)
- 2
Description
# Technical Specification: Simplified IDIR Account Onboarding & Team Provisioning
## 1. Overview & Context
In the ServiceBC Connect G2G Portal ecosystem, authenticated IDIR users represent provincial public servants, digital product leads, and partner ministry staff. Currently, when an IDIR user signs in:
1. They may not have any affiliated accounts/organizations (or "teams"), preventing them from accessing tenant-scoped APIs or managing digital services.
2. Even when they intend to provision a team or manage services, the legacy multi-tenant onboarding flow is manual, staff-mediated, or requires complex multi-step forms.
3. Users need the ability to:
- **First-time onboarding**: Automatically provision a dedicated default team/account upon initial login with their institutional identity.
- **Multi-team creation**: Create additional accounts/teams (e.g., "Transportation Core API Team", "Health Digital Services") at any time.
- **Institutional Access & Member Invitation**: Provision as a `GOVM` (Government Ministry) account, allowing free Business Search and corporate billing options, and automatically invite the user themselves (or additional team members) via email to establish the account connection.
This document specifies the technical architecture, data contracts, API boundaries, security controls, and UI/UX journey for a streamlined, self-serve IDIR account onboarding system.
---
## 2. Business Requirements & User Stories
### User Story 1: First-Time IDIR Onboarding (Zero to One)
> **As an** IDIR government user signing in for the first time without an existing team account,
> **I want** the system to detect that I have no active account and guide me through a lightweight, 1-step team initialization,
> **So that** a `GOVM` account is created, my user profile is linked, and I am dropped into my team dashboard ready to register services.
### User Story 2: Creating Additional Team Accounts
> **As an** existing IDIR user who belongs to one or more teams,
> **I want** to create a new team account directly from the header/intent/dashboard switchers,
> **So that** I can isolate project credentials, SKUs, and service registrations for different ministry initiatives without filing a manual ServiceBC support ticket.
### User Story 3: Automated Connection & Self-Invitation
> **As the** account creator,
> **I want** the system to provision the `GOVM` org via a secure server-side service account and automatically dispatch an admin invite to my verified IDIR email,
> **So that** I am bound as the authorized administrator of the new team account.
---
## 3. Architecture & Security Model
```
┌────────────────────────────────────────────────────────────────────────────────────────┐
│ CLIENT (Nuxt 4 SPA) │
│ useConnectAuth() [User JWT] ──► Intent / Onboarding Modal ──► $fetch('/api/accounts') │
└───────────────────────────────────────────┬────────────────────────────────────────────┘
│ POST /api/accounts
│ Headers: Bearer
▼
┌────────────────────────────────────────────────────────────────────────────────────────┐
│ NUXT NITRO SERVER LAYER │
│ │
│ 1. validateUserAccess(event) (Validate IDIR JWT, claims, user GUID, email) │
│ 2. Service Account Token Exchange (Keycloak Client Credentials: sbc-auth-admin) │
│ 3. POST https://{AUTH_API_URL}/api/v1/orgs (Create GOVM Account) │
│ 4. POST https://{AUTH_API_URL}/api/v1/orgs/{org_id}/members/invite/{email} │
│ 5. Return Created Account Summary & Active Context │
└───────────────────────────────────────────┬────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────────────────────────────┐
│ UPSTREAM CONNECT AUTH API │
│ │
│ • Creates Organization (accessType: GOVM, subscriptions: [BUSINESS_SEARCH]) │
│ • Generates Organization Invitation with ADMIN role for target email │
│ • Dispatches Email Notification with Activation Link │
└────────────────────────────────────────────────────────────────────────────────────────┘
```
### Security Guardrails
1. **Zero Client Secret Exposure**: The client credentials for `KC_CLIENT_ID` (`sbc-auth-admin`) and `API_KEY` are stored strictly on the server in environment secrets (`KEYCLOAK_SERVICE_ACCOUNT_ID`, `KEYCLOAK_SERVICE_ACCOUNT_SECRET`, `API_GW_KEY`). The browser client never touches service account tokens.
2. **Strict Identity Binding**: The user's name, IDIR GUID, and email address are extracted directly from the verified Keycloak JWT (`sub`, `email`, `name`). A caller cannot spoof the invite target or impersonate another government employee.
3. **Multi-Tenant Constraint Enforcement**: In accordance with Project Constraints Rule 3, no hardcoded account numbers (e.g., `3139`) or ministry names exist in the onboarding flow; all IDs are resolved dynamically.
---
## 4. API Endpoints Specification
### 4.1. Server Route: `POST /api/accounts`
Provisions a new `GOVM` account on behalf of the authenticated IDIR user and issues the self-invitation.
#### Request Headers
| Header | Type | Description |
|---|---|---|
| `Authorization` | `string` | `Bearer ` (Required) |
#### Request Body
```json
{
"name": "Ministry of Citizen Services - Cloud Architecture",
"branchName": "Digital Platforms",
"mailingAddress": {
"street": "1 Test Street",
"streetAdditional": "Suite 300",
"city": "Victoria",
"region": "BC",
"postalCode": "V8V 1V1",
"country": "CA"
}
}
```
*Note*: `mailingAddress` fields are pre-filled with sensible Victoria BC government defaults or prompted in a compact collapsible section.
#### Processing Steps
1. **Authentication Check**:
- Call `validateUserAccess(event)`. Verify user has IDIR login source (`identity_provider === 'idir'`).
- Extract user identity: `keycloakGuid = payload.sub`, `userEmail = payload.email`, `userName = payload.name`.
2. **Obtain Upstream Service Account Token**:
- Request OAuth2 client credentials token from `${KEYCLOAK_ISSUER}/protocol/openid-connect/token` using `sbc-auth-admin` credentials.
- Cache token in-memory until expiration (`expires_in - 30` seconds).
3. **Provision Organization**:
- Issue `POST ${AUTH_API_URL}/api/v1/orgs` with:
```json
{
"name": body.name,
"branchName": body.branchName || "Digital Services",
"accessType": "GOVM",
"productSubscriptions": [
{ "productCode": "BUSINESS_SEARCH" }
],
"mailingAddress": body.mailingAddress
}
```
- Headers: `Authorization: Bearer ${SA_TOKEN}`, `x-apikey: ${API_GW_KEY}`.
4. **Dispatch Self-Invitation**:
- Issue `POST ${AUTH_API_URL}/api/v1/orgs/{org_id}/members/invite/${encodeURIComponent(userEmail)}`
- Payload specifies role:
```json
{
"recipientEmail": userEmail,
"sentBy": userName,
"role": "ADMIN"
}
```
5. **Response**:
```json
{
"success": true,
"data": {
"accountId": 12345,
"name": "Ministry of Citizen Services - Cloud Architecture",
"accessType": "GOVM",
"status": "ACTIVE",
"invitationSentTo": "user@gov.bc.ca",
"requiresActivation": true
}
}
```
---
## 5. Client Flow & User Experience
### 5.1. Entry Points
1. **First-Time Login (Zero Accounts Guard)**:
- On navigation to `/intent`, if `accountStore.userAccounts.length === 0`:
- Present a welcome onboarding card:
> *"Welcome to ServiceBC Connect! You do not currently have an active team or ministry account. Create your team to begin registering and managing services."*
2. **Team Switcher / Intent Header**:
- A button in the Account Switcher dropdown: `+ Create New Team Account`.
### 5.2. Connect Layer Component Conformance
In accordance with **Corporate Nuxt 4 Architecture Rules** (`@/.agents/plugins/connect-layer/rules/nuxt-constraints.md`):
1. **No Custom/Duplicate Form Controls or Address Logic**:
- The application extends `@sbc-connect/nuxt-pay` which inherits `@sbc-connect/nuxt-auth`, `@sbc-connect/nuxt-forms`, and `@sbc-connect/nuxt-base`.
- The modal MUST directly use the official connect-layer form components rather than custom inputs or unvetted address widgets:
- `` (from `@sbc-connect/nuxt-forms`): Fully featured address autocomplete and provincial/postal validation. Includes `ConnectFormAddressCountry`, `ConnectFormAddressStreet`, `ConnectFormAddressCity`, `ConnectFormAddressRegion`, `ConnectFormAddressPostalCode`.
- `` & ``: Standardized layout wrappers with error handling and accessible labels.
- ``: Name input component integrated with live validation against `/orgs?validateName=true`.
2. **Schema & Validation**:
- Leverages Zod schemas directly exported by `@sbc-connect/nuxt-auth`:
```ts
import { getAccountNameSchema } from '#imports' // or from nuxt-auth schemas
import { getRequiredAddressSchema } from '#forms/app/utils'
```
- Matches the official `ConnectAddress` and `ConnectCreateAccount` types defined in `@sbc-connect/nuxt-auth/app/interfaces`.
### 5.3. Streamlined Onboarding Dialog (`ConnectModalCreateTeam.vue`)
* Built using `UModal` and connect-layer primitives.
* Fields:
- **Account/Team Name**: Uses `` (or ``) with live verification.
- **Creator Email**: Displayed as read-only badge (locked to `authUser.email`).
- **Branch / Division Name**: e.g. "Digital Platforms & Architecture".
- **Mailing Address**: Uses `` with BC Victoria defaults (`country: 'CA', region: 'BC', city: 'Victoria'`).
* On Submit:
- Invokes `POST /api/accounts`.
- Shows success notification / modal explaining:
> *"Your team account has been provisioned! We have dispatched an invitation to **user@gov.bc.ca**. Please click the link in your email to finalize the connection, or refresh your accounts."*
- Automatically triggers `accountStore.loadUserAccounts(true)` and selects the new account.
---
## 6. Implementation Plan & Agent Tasks
| Phase | Description | Deliverables |
|---|---|---|
| **Phase 1: Server Service Token Utility** | Helper to acquire and cache Keycloak service account token (`sbc-auth-admin`) securely on the server. | `hub/server/utils/service-token.ts` |
| **Phase 2: Accounts Nitro Endpoint** | Backend route `POST /api/accounts` supporting account creation and invite dispatch. | `hub/server/api/accounts.post.ts` |
| **Phase 3: Connect Layer UI Integration** | Modal component utilizing `` and connect-layer form primitives; integration with `/intent`. | `hub/app/components/ModalCreateTeam.vue`, `hub/app/pages/intent.vue` |
| **Phase 4: E2E Verification** | Playwright test verifying account creation payload validation, address formatting, IDIR extraction, and mock rejection. | `hub/e2e/account-creation.spec.ts` |
Contributor guide
Research direction
Start with @/.agents/plugins/connect-layer/rules/nuxt-constraints.md, then trace the planned files hub/server/utils/service-token.ts, hub/server/api/accounts.post.ts, hub/app/components/ModalCreateTeam.vue, and hub/app/pages/intent.vue. Run or build the checks described in hub/e2e/account-creation.spec.ts. Done means IDIR users can create GOVM accounts, receive the self-invitation, see the success state, and have the new account loaded and selected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- nuxtjs, playwright, typescript
- Domain
- authentication, backend-api-design, full-stack, testing
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100