Appearance
IAM Service API
iam-service is the renamed and expanded identity boundary for the Go platform.
Core endpoints:
POST /v1/auth/registerPOST /v1/auth/loginPOST /v1/auth/logoutPOST /v1/auth/refreshPOST /v1/auth/forgot-passwordPOST /v1/auth/reset-passwordGET /v1/auth/google/configPOST /v1/auth/googleGET /v1/auth/mePOST /v1/auth/me/passwordGET /v1/.well-known/jwks.jsonGET /.well-known/jwks.jsonGET /v1/admin/identity/summaryPOST /v1/accountsGET /v1/accounts/{accountId}PATCH /v1/accounts/{accountId}DELETE /v1/accounts/{accountId}GET /v1/rolesPOST /v1/rolesPATCH /v1/roles/{roleId}DELETE /v1/roles/{roleId}GET /v1/permissionsPOST /v1/permissionsPOST /v1/roles/{roleId}/permissionsPOST /v1/accounts/{accountId}/rolesDELETE /v1/accounts/{accountId}/roles/{roleId}GET /v1/accounts/{accountId}/permissions/{permissionCode}/checkGET /v1/organizationsPOST /v1/organizationsGET /v1/organizations/{organizationId}/membersPOST /v1/organizations/{organizationId}/membersDELETE /v1/organizations/{organizationId}/members/{accountId}GET /v1/internal/organizations/{organizationId}/members/{accountId}/checkGET /v1/accounts/{accountId}/parent-student-linksPOST /v1/accounts/{accountId}/parent-student-linksDELETE /v1/accounts/{accountId}/parent-student-links/{linkId}GET /v1/accounts/{accountId}/teacher-subject-scopesPOST /v1/accounts/{accountId}/teacher-subject-scopesDELETE /v1/accounts/{accountId}/teacher-subject-scopes/{scopeId}GET /v1/accounts/{accountId}/staff-permission-scopesPOST /v1/accounts/{accountId}/staff-permission-scopesDELETE /v1/accounts/{accountId}/staff-permission-scopes/{scopeId}POST /v1/invitesPOST /v1/invites/acceptPOST /v1/invites/{inviteId}/revokeGET /v1/admin/auth-settings/googlePATCH /v1/admin/auth-settings/googleGET /v1/audit/loginGET /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/meandPOST /v1/auth/me/passwordrequire 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
ADMINaccount. Service-side HTTP tests cover unauthenticated401, non-admin403, and admin200behavior for management routes before any default gateway exposure. - Internal service-to-service:
GET /v1/internal/organizations/{organizationId}/members/{accountId}/checkrequiresX-Internal-Serviceand the sharedX-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/summaryrequires 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-serviceidentity.countssource 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.jsonroutes only add/remove organization membership writes throughorganization_membership_adapter. The adapter translates legacy publicuserIdbodies to IAMaccountId; 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 legacyfullName, andstatus.- 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.jsondeploy/gateway/routes.auth-native-localhost-example.jsonmake test-auth-routesAUTH_ROUTE_LIVE_CONFIRM=auth-native make test-auth-routes-livefor 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-browserfor browser-originated login page proof against the same non-default route table.