Appearance
Auth Service API
Current Endpoints
GET /healthzGET /readyzGET /v1POST /v1/auth/registerPOST /v1/auth/loginPOST /v1/auth/refreshPOST /v1/auth/logoutGET /v1/auth/me
Native Auth Foundation
Phase 8 starts with a native auth credential/session foundation. This is an internal /v1 service contract, not a public /api/auth/* cutover.
Legacy evidence:
apps/api/src/modules/auth/auth.controller.ts:31-88maps local accounts, register, login, Google auth, test login, refresh, and logout routes.apps/api/src/modules/auth/auth.controller.ts:189-245mapsGET /api/auth/me, profile update, and password change underJwtAuthGuard.apps/api/src/modules/auth/auth.service.ts:58-83normalizes login email, validatesloginSchema, and returnsauthPayload.apps/api/src/modules/auth/auth.service.ts:686-737builds the session response withaccessToken,refreshToken,tokenType,expiresIn, user memberships, anddefaultOrganizationId.apps/api/src/modules/app-data/app-data.identity-core.ts:92-188creates users, verifies bcrypt password hashes, stores bcrypt-hashed refresh tokens, consumes refresh tokens by revoking them, and revokes logout tokens.apps/api/src/common/auth.guard.ts:41-78accepts access tokens from the Bearer header orhoctapaz.accessTokencookie and requirestype=access,sub,role, andfullName.apps/api/prisma/schema.prisma:10-109definesUsercredential/session fields andRefreshToken.packages/shared/src/index.ts:971-982definesregisterSchemaandloginSchema.apps/web/components/auth/login-form.tsx:63-83andapps/web/components/auth/register-form.tsx:83-104show the frontend expects{ success: true, data: session }.
Native contract:
POST /v1/auth/registercreates a native auth identity, hashes the password with bcrypt, creates a default local membership, stores a refresh-token hash, and returns a legacy-compatible session envelope.POST /v1/auth/loginlowercases and trims the email, verifies the bcrypt password hash, stores a refresh-token hash, and returns the same session envelope.POST /v1/auth/refreshacceptsrefreshTokenfrom the JSON body orAuthorization: Bearer ..., verifies the token signature andjti, consumes the matching stored refresh token exactly once, and returns a new session with a rotated refresh token.POST /v1/auth/logoutaccepts the same refresh-token sources and returns{ "revoked": false }when the token is missing, invalid, already revoked, or does not match the stored hash.GET /v1/auth/meacceptsAuthorization: Bearer <accessToken>and returns the current native identity in the same envelope shape.
Session envelope:
json
{
"success": true,
"data": {
"user": {
"id": "usr_123",
"accountCode": "HTA12345678",
"email": "[email protected]",
"fullName": "Student One",
"role": "STUDENT",
"status": "ACTIVE",
"createdAt": "2026-07-04T00:00:00Z",
"updatedAt": "2026-07-04T00:00:00Z",
"memberships": [
{
"organizationId": "org_local_center",
"organizationName": "HOCTAPAZ Local Center",
"role": "STUDENT",
"status": "ACTIVE"
}
],
"defaultOrganizationId": "org_local_center"
},
"accessToken": "<token>",
"refreshToken": "<token>",
"tokenType": "Bearer",
"expiresIn": 86400
},
"message": "Logged in"
}Native token notes:
- Access token payload keeps the legacy fields needed by the frontend and guards:
sub,type=access,role,accountCode,fullName,email, anddefaultOrganizationId. - Refresh token payload keeps
sub,type=refresh, andjti. - Refresh tokens are SHA-256 digested before bcrypt hashing so long JWT strings are never stored raw and do not hit Go bcrypt's 72-byte input limit.
- Native JWT signing uses the auth-service secret configuration; production must provide a non-default secret before public cutover.
- Local compose reads
AUTH_JWT_SECRETwith a development default; local K8s reads it fromplatform-secrets.
Database:
services/auth-service/migrations/000002_auth_identity.sqlcreatesidentities,identity_memberships, andrefresh_tokens.legacy_idis nullable during native-only local creation and required during backfill/migration reports.identities.emailand non-emptyidentities.phoneare unique.refresh_tokens.token_idis unique and stores a bcrypt hash, not the raw token.
Validation queries:
sql
SELECT id, legacy_id, account_code, email, role, status
FROM identities
ORDER BY created_at DESC
LIMIT 20;
SELECT user_id, token_id, expires_at, revoked_at
FROM refresh_tokens
ORDER BY created_at DESC
LIMIT 20;
SELECT user_id, organization_id, role, status
FROM identity_memberships
ORDER BY joined_at DESC
LIMIT 20;Rollback for this native slice:
- Keep public
/api/auth/*routed to legacy. - Disable gateway/native callers for
/v1/auth/*. - Drop auth-service local identity tables with the migration down step if local test data must be reset.
Non-goals for P8-001:
- Google auth provider settings and account linking.
- Forgot/reset password email flow.
- Profile editing, role-specific profiles, teacher KYC, and email-change verification.
- Cross-service school/user ownership finalization.
- Public gateway adapter/cutover for
/api/auth/*.
Gateway Rehearsal
deploy/gateway/routes.auth-native-example.json and deploy/gateway/routes.auth-native-localhost-example.json provide non-default route-table rehearsals for the native session foundation. They route only POST /api/auth/register, POST /api/auth/login, POST /api/auth/refresh, POST /api/auth/logout, and GET /api/auth/me to auth-service; unsupported auth siblings stay legacy-proxied.
Run make test-auth-routes before any live or browser session rehearsal.