Skip to content

IAM Service API

iam-service is the renamed and expanded identity boundary for the Go platform.

Core endpoints:

  • POST /v1/auth/register
  • POST /v1/auth/login
  • POST /v1/auth/logout
  • POST /v1/auth/refresh
  • POST /v1/auth/forgot-password
  • POST /v1/auth/reset-password
  • GET /v1/auth/google/config
  • POST /v1/auth/google
  • GET /v1/auth/me
  • POST /v1/auth/me/password
  • GET /v1/.well-known/jwks.json
  • GET /.well-known/jwks.json
  • GET /v1/admin/identity/summary
  • POST /v1/accounts
  • GET /v1/accounts/{accountId}
  • PATCH /v1/accounts/{accountId}
  • DELETE /v1/accounts/{accountId}
  • GET /v1/roles
  • POST /v1/roles
  • PATCH /v1/roles/{roleId}
  • DELETE /v1/roles/{roleId}
  • GET /v1/permissions
  • POST /v1/permissions
  • POST /v1/roles/{roleId}/permissions
  • POST /v1/accounts/{accountId}/roles
  • DELETE /v1/accounts/{accountId}/roles/{roleId}
  • GET /v1/accounts/{accountId}/permissions/{permissionCode}/check
  • GET /v1/organizations
  • POST /v1/organizations
  • GET /v1/organizations/{organizationId}/members
  • POST /v1/organizations/{organizationId}/members
  • DELETE /v1/organizations/{organizationId}/members/{accountId}
  • GET /v1/internal/organizations/{organizationId}/members/{accountId}/check
  • GET /v1/accounts/{accountId}/parent-student-links
  • POST /v1/accounts/{accountId}/parent-student-links
  • DELETE /v1/accounts/{accountId}/parent-student-links/{linkId}
  • GET /v1/accounts/{accountId}/teacher-subject-scopes
  • POST /v1/accounts/{accountId}/teacher-subject-scopes
  • DELETE /v1/accounts/{accountId}/teacher-subject-scopes/{scopeId}
  • GET /v1/accounts/{accountId}/staff-permission-scopes
  • POST /v1/accounts/{accountId}/staff-permission-scopes
  • DELETE /v1/accounts/{accountId}/staff-permission-scopes/{scopeId}
  • POST /v1/invites
  • POST /v1/invites/accept
  • POST /v1/invites/{inviteId}/revoke
  • GET /v1/admin/auth-settings/google
  • PATCH /v1/admin/auth-settings/google
  • GET /v1/audit/login
  • GET /v1/audit/security-events

Authorization model:

  • Public: health/ready/service marker, JWKS, register, login, refresh, logout, forgot/reset password, Google config/auth, and POST /v1/invites/accept.
  • Authenticated account: GET /v1/auth/me and POST /v1/auth/me/password require a valid IAM access token.
  • Admin management: account directory, roles, permissions, organization membership, access-scope, invite create/revoke, and audit list routes require a bearer token for an ADMIN account. Service-side HTTP tests cover unauthenticated 401, non-admin 403, and admin 200 behavior for management routes before any default gateway exposure.
  • Internal service-to-service: GET /v1/internal/organizations/{organizationId}/members/{accountId}/check requires X-Internal-Service and the shared X-Internal-Token, then returns only {allowed, role, status, unitId} for one account. It is used for access-control decisions such as exam runtime starts and must not be exposed through default public gateway routes.
  • Owner dashboard source: GET /v1/admin/identity/summary requires the same internal service headers and returns IAM-owned account, role, permission, organization membership, parent-student access link, teacher subject scope, and staff permission scope counts for admin-service identity.counts source snapshots. It does not return profile display rows, credentials, tokens, or subscription/wallet state.
  • Non-default gateway rehearsal: deploy/gateway/routes.organizations-membership-write-native-example.json routes only add/remove organization membership writes through organization_membership_adapter. The adapter translates legacy public userId bodies to IAM accountId; default /api/organizations* writes and PATCH member updates remain legacy-proxied until browser and rollback proof.

Account/profile boundary:

  • PATCH /v1/accounts/{accountId} accepts only IAM directory fields: email, phone, temporary legacy fullName, and status.
  • Avatar, school metadata, role-specific profile fields, and public profile display data belong to profile-service; IAM rejects these as unknown JSON fields and does not store them as account ownership.

JWT access tokens keep current gateway-compatible claims and add the new IAM claims: roles, org_ids, permissions_version, session_id, and token_type. If JWT_PRIVATE_KEY_PATH or JWT_PRIVATE_KEY_PEM is configured, IAM signs tokens with RS256 and publishes the RSA public key through JWKS. Local development can still fall back to AUTH_JWT_SECRET/HS256.

Database: hoctapaz_iam_db.

Register, login, refresh, and logout write login_audit_logs without storing passwords or raw tokens. Issued sessions are persisted with their refresh-token record and revoked when refresh/logout consumes that token.

Password change and reset are IAM-owned credential lifecycle flows. POST /v1/auth/me/password validates the current password, rejects unchanged or confirmation-mismatched new passwords, revokes existing refresh/session state, and returns a fresh session envelope. POST /v1/auth/forgot-password returns neutral success for known and unknown email addresses; known active accounts get one active 24-hour reset token while older reset tokens are consumed. POST /v1/auth/reset-password consumes a single-use reset token, writes the new bcrypt password hash, revokes active refresh/session state, and records login/security audit events without raw passwords or raw reset tokens. Reset token and URL preview are disabled unless INCLUDE_PASSWORD_RESET_LINK_IN_RESPONSE=true or INCLUDE_PASSWORD_RESET_TOKEN_IN_RESPONSE=true, with links based on PASSWORD_RESET_BASE_URL.

Google Auth is also IAM-owned. GET /v1/auth/google/config returns public provider readiness and client-id state. POST /v1/auth/google verifies Google ID tokens with RS256/JWKS, enforces issuer/audience/expiry/email verification and allowed domains, then logs in an existing provider link, links a matching email account, or creates a new TEACHER/STUDENT account only when Google registration and accepted legal terms are present. Provider settings live in IAM storage and override env defaults; admin-only GET/PATCH /v1/admin/auth-settings/google manages the database settings. Audit/security metadata never includes the raw Google credential or client id.

Internal event names emitted by the usecase publisher are: iam.account.created, iam.account.updated, iam.account.disabled, iam.role.assigned, iam.organization.member_added, and iam.organization.member_removed. Add events include userId, accountId, organizationId, role, status, optional unitId, and joinedAt; remove events only require userId or accountId plus organizationId. These events let school-service keep a read model through X-IAM-Membership-Sync without owning access-control membership writes. The default publisher is noop so NATS/Kafka can be attached later without changing usecase contracts. Runtime deployments can set SCHOOL_SERVICE_URL or SCHOOL_SERVICE_BASE_URL to enable the built-in school-service sync publisher; it maps member add events to PUT /v1/organizations/{organizationId}/members/{userId} and remove events to DELETE /v1/organizations/{organizationId}/members/{userId} with X-IAM-Membership-Sync: iam-service, X-Internal-Service: iam-service, and the shared X-Internal-Token. The publisher maps IAM access-control roles that school-service does not own (ADMIN, STAFF, CENTER_MANAGER) to the school read-model role ORG_ADMIN, and retries transient network, 429, and 5xx failures using SCHOOL_SERVICE_SYNC_MAX_ATTEMPTS and SCHOOL_SERVICE_SYNC_RETRY_DELAY_MS. IAM also persists membership sync events in iam_membership_outbox before post-commit delivery; the background dispatcher is controlled by IAM_MEMBERSHIP_OUTBOX_POLL_INTERVAL_MS, IAM_MEMBERSHIP_OUTBOX_BATCH_SIZE, IAM_MEMBERSHIP_OUTBOX_MAX_ATTEMPTS, and IAM_MEMBERSHIP_OUTBOX_RETRY_DELAY_MS. Default public member-write routes stay legacy until school-service event ordering and IAM-vs-read-model owner invariant semantics are safe for durable retries.

The internal membership check above reads IAM's canonical membership store directly and considers only status=ACTIVE as allowed. School-service remains a read model for tenant validation; it is not the source of truth for permission bearing organization membership.

Invites store only token hashes. The raw invite token is returned once when the invite is created; accept requires the target account email to match the invite email before IAM creates the organization membership. Revoke rejects already accepted, already revoked, or expired invites. Create, accept, and revoke write IAM security_events without storing raw invite tokens.

Parent-student links, teacher subject scopes, and staff permission scopes are IAM access-control data. They are account-scoped, write security_events on mutation, and do not create profile, classroom, learning-progress, wallet, or subscription records.

If AUDIT_SERVICE_URL or AUDIT_SERVICE_BASE_URL is configured, IAM also publishes a best-effort redacted projection of local security_events to audit-service POST /v1/security-events. The projection keeps sourceService=iam-service, uses the IAM security event id as sourceEventId for replay idempotency, sets metadataSchemaVersion=1, and strips password, credential, token, refresh, private-key, and secret metadata keys. Local IAM security-event writes remain the source of truth for IAM, and audit-service projection failures do not block auth/access workflows.

Local gateway rehearsal for public /api/auth/* still uses:

  • deploy/gateway/routes.auth-native-example.json
  • deploy/gateway/routes.auth-native-localhost-example.json
  • make test-auth-routes
  • AUTH_ROUTE_LIVE_CONFIRM=auth-native make test-auth-routes-live for an opt-in read-only gateway smoke against a running non-default auth route table.
  • AUTH_ROUTE_BROWSER_CONFIRM=auth-native make test-auth-routes-browser for browser-originated login page proof against the same non-default route table.

Go-platform documentation is generated from repository Markdown.