Skip to main content

Identity, Access & Security (IAM)

Module code IAM · Release R0 (core), R1 (OIDC SSO, device approvals), R2 (SAML, API clients) · Packaging Core (SSO on Standard+, SAML on Enterprise) · Requirements FR-IAM-*, BR-IAM-*, NFR-SEC-* in the PRD and BRD · Related RBAC permission matrix, architecture section 6

1. Overview and scope​

IAM answers who is this, and what may they do on whose data. It owns tenant users, authentication, roles and permissions with data scopes, sessions, devices and the audit log. It replaces the reference product's manual credential hand-off and default password with invite activation.

In scope: user accounts linked to employees; invite links (email, SMS, WhatsApp); password policy (typed, per tenant); argon2id hashing and breached-password checks; TOTP MFA (mandatory for admin roles) with recovery codes; OIDC SSO (Google Workspace, Microsoft Entra ID); SAML (Enterprise, R2); role templates, cloning and custom roles; role assignments with scope and optional location or department filters; field-level protection for sensitive data; segregation-of-duties rules; sessions and revocation; mobile device binding and approvals; kiosk device tokens; API clients (R2); the append-only audit log; support-access sessions from PLT.

Out of scope: the reporting-manager relationship (EMP job assignment), platform staff identity (PLT).

2. Learned from the reference product​

Source: Spryple analysis, §4, §11, §17 and §21.2.

  • [Observed] The admin sets a user ID per employee and the password defaults to "Welcome1!"; the credentials email contains the plaintext password (SP-IAM-01; §21.2).
  • [Observed] Credentials are created manually after onboarding, when the admin is emailed (SP-IAM-05).
  • [Observed] The "Roles & Permissions" help page duplicates "Users Login", so the permission model is undocumented (SP-IAM-02; defect D1).
  • [Observed] HR Policy rules 1–9 cover password complexity (SP-IAM-03); a password reset screen lets admins reset username, password or both (SP-IAM-04).
  • [Observed] A new phone needs manager approval in the CRM app (§11, Device Approvals).
  • [Observed] Login always needs a Company Code (SP-X-10).

3. Our design decisions​

IDDecision [Proposed]Reason
D-IAM-01No default or emailed passwords. A user activates through a single-use invite link (72 h) and sets their own password or signs in with SSO.Removes the confirmed security weakness.
D-IAM-02The user is created automatically when an employee reaches Joined (or earlier if HR chooses "invite now").Removes the manual hand-off step.
D-IAM-03Authorisation is (permission, scope). A role is a set of permissions; a role assignment adds the scope and optional filters. The reporting-manager scope (TEAM) is derived from EMP job assignments.Matches CONVENTIONS §4; one guard for every module.
D-IAM-04Sensitive fields (salary, bank, Aadhaar, PAN, health, biometrics) need explicit field permissions (emp.sensitive.read, pay.compensation.read) and are masked by default.DPDP minimisation.
D-IAM-05MFA is mandatory for org_owner, hr_admin, finance_admin, payroll_manager; step-up MFA for high-risk actions (payroll reversal, role edits, bank detail approval).High-value targets.
D-IAM-06Device binding is a tenant policy: off, record only, or approval required (by line manager).Parity with the reference CRM; configurable for office staff.
D-IAM-07Every deny and every access to a sensitive resource is written to audit_event, which is append-only and exportable.Accountability and investigations.

4. Domain model​

iam_permission is global reference data (no tenant_id), seeded from code. iam_device.kind is PERSONAL or KIOSK; kiosk devices hold a location-scoped, punch-only token.

5. State machines​

5.1 User account​

5.2 Device approval​

6. Key business logic​

6.1 Authorisation guard​

can(user, permission, target):
assignments = active role assignments of user whose role contains permission
for a in assignments:
scope = min(a.scope, rolePermission.max_scope)
if inScope(user, target.employee_id, scope, a.filters): allow and audit if sensitive
deny and audit

inScope(user, employee, scope, filters):
SELF -> employee == user.employee_id
TEAM -> employee in reportingTree(user.employee_id) # direct and indirect
DEPARTMENT -> department(employee) in departments managed or filtered
LOCATION -> location(employee) in filters.locations or user's location
ORG -> true
# Org-hierarchy lookups use EMP job assignments valid today, cached per request.

Self-approval is always blocked: the approval engine never resolves the requester as an approver.

6.2 Login with risk checks​

login(identifier, password, device):
resolve tenant from host or company code (never from the body)
user = find by username or email ; constant-time compare argon2id
on failure: increment counter ; lock after policy.max_attempts within window
if device unknown and policy.device_binding == APPROVAL_REQUIRED:
create iam_device Pending ; notify manager ; return limited session (punch disabled)
if mfa required: challenge TOTP
issue access JWT (15 min, claims sub, tid, sid, roles_hash) and rotating refresh token
refresh reuse detected -> revoke the whole session family

7. Configuration​

Password and session policy (typed policy replacing HR Policy rules 1–9):

SettingTypeDefaultDescription
password.min_lengthinteger10Minimum length
password.max_lengthinteger128Maximum length
password.require_classesinteger3Of upper, lower, digit, symbol
password.breach_checkbooleantrueReject known-breached passwords
password.historyinteger5Previous passwords that cannot be reused
password.max_age_daysinteger00 means no forced rotation
login.max_attemptsinteger5Before lock
login.lock_minutesinteger15Lock duration
session.idle_timeout_minutesinteger30Web idle timeout
session.mobile_refresh_daysinteger30Mobile refresh lifetime
invite.expiry_hoursinteger72Invite link validity
invite.channelsarray of string[EMAIL, SMS]Allowed channels
mfa.required_rolesarray of stringadmin rolesRoles that must enrol
device_bindingenumRECORDOFF, RECORD, APPROVAL_REQUIRED
sso.enforcebooleanfalseDisable passwords for SSO domains
sod.rulesarray of objectpayroll SoDConflicting permission pairs

8. Screens by persona​

PersonaScreens
Any userActivate account, sign in (web subdomain or company code on mobile), MFA enrolment, forgot password, my sessions and devices
hr_adminUsers (status, resend invite, suspend), password policy, device policy, SSO setup, bulk invite
org_ownerRoles (clone, edit permissions and max scopes), role assignments, SoD rules, audit log viewer and export, support-access consent
line_managerDevice approvals for the team
auditorAudit log (read-only)

9. Events​

EventConsumers
iam.user.invitedNTF (send invite)
iam.user.activatedEMP (onboarding checklist item), NTF
iam.user.lockedNTF (user and admin)
iam.user.deactivatedMOB (wipe tokens), NTF
iam.role.changedPermission cache invalidation, audit
iam.role_assignment.changedPermission cache invalidation, audit
iam.device.approval_requestedNTF (manager task)
iam.device.approved, iam.device.revokedMOB
iam.session.revokedMOB, web

Consumed: emp.employee.joined (create user, send invite), emp.job.changed (team-scope cache), ext.lwd.reached (deactivate at 23:59 local time), plt.support_access.granted (temporary session).

10. API surface​

ResourceMain operations
/v1/iam/auth/login, /refresh, /logout, /mfa/verifyAuthentication
/v1/iam/invites/:token/acceptActivation
/v1/iam/usersGET, POST /:id/invite, POST /:id/suspend, POST /:id/reset-mfa
/v1/iam/roles and /v1/iam/roles/:id/permissionsCRUD, POST /:id/clone
/v1/iam/role-assignmentsCRUD
/v1/iam/permissionsGET catalogue
/v1/iam/sessions, /v1/iam/devicesGET, DELETE, POST /:id/approve
/v1/iam/sso-connectionsCRUD
/v1/iam/audit-eventsGET, POST /export
/v1/iam/me/permissionsEffective permissions for navigation

Detailed in API endpoints.

11. Permissions​

PermissionScopesDefault roles
iam.user.manageORG, LOCATIONhr_admin
iam.role.manageORGorg_owner
iam.role_assignment.manageORGorg_owner, hr_admin
iam.policy.manageORGhr_admin
iam.sso.manageORGorg_owner
iam.device.approveTEAMline_manager, sales_manager
iam.audit.readORGorg_owner, auditor
iam.audit.exportORGorg_owner

The full role-by-permission grid is in the RBAC permission matrix.

12. Reports​

ReportContents
Users and activationInvited, active, expired, locked; time to activate
MFA coverageAdmin roles without MFA (must be zero)
Role assignmentsWho holds which role and scope
Access reviewQuarterly list for owners to confirm
Audit logFilterable, exportable
Device registerApproved and pending devices per team

13. Release and priority​

CapabilityReleaseMoSCoW
Invite activation, passwords, MFA, sessions, roles with scopes, audit logR0Must
Automatic user creation on join; deactivation at LWDR1Must
OIDC SSOR1Should
Device binding and approvalsR1Must
Field-level masking and SoDR1Must
Kiosk device tokensR2Should
SAML, API clients, access reviewsR2Should

14. Open questions​

  1. Should employees without an email or smartphone (factory staff) log in with mobile OTP only? This is likely, with kiosk punching for attendance.
  2. Do we allow one person to have users in two tenants (e.g. a consultant)? The default is separate users per tenant.
  3. Is quarterly access review a Professional feature or Enterprise only?