UNPKG

@autobe/agent

Version:

AI backend server code generator

23 lines 46.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.transformRealizeWriteMembershipHistory = void 0; const uuid_1 = require("uuid"); const transformRealizeWriteMembershipHistory = (operation, payload) => { if (operation.authorizationType === null) return []; const text = PROMPTS[operation.authorizationType].replace("{{PAYLOAD}}", JSON.stringify(payload)); const history = { id: (0, uuid_1.v7)(), created_at: new Date().toISOString(), type: "systemMessage", text, }; return [history]; }; exports.transformRealizeWriteMembershipHistory = transformRealizeWriteMembershipHistory; const PROMPTS = { login: "<!--\nfilename: REALIZE_MEMBERSHIP_LOGIN.md\n-->\n# Authorization Type: Login\n\nThis is a **login** operation that authenticates users.\n\n## Implementation Guidelines for Login\n\n### Login Operation Requirements\n- This is a login endpoint that authenticates users\n- Must validate credentials (username/email and password)\n- Must verify password using PasswordUtil\n- Must create a new session record for this login\n- Must generate JWT tokens with correct payload structure\n- Should return authentication tokens (access and refresh tokens)\n- May include additional business logic as required by the API specification (e.g., updating last login timestamp, creating audit logs, checking account status)\n- Must NOT require authentication decorator (this endpoint creates authentication)\n\n**IMPORTANT**: While the core requirements (credential validation, session creation, JWT generation) are mandatory, you should implement any additional business logic specified in the API requirements. The examples below show the mandatory flow, but your implementation may include additional steps before, between, or after these core operations.\n\n## Session Management Architecture\n\n### Conceptual Foundation: Actor and Session Separation\n\nIn production authentication systems, we separate **Actor** (the persistent user identity) from **Session** (the temporary authentication state). This architectural pattern provides several critical benefits:\n\n1. **Security**: Sessions can be independently revoked without deleting the user account\n2. **Multi-device support**: One actor can maintain multiple concurrent sessions across different devices\n3. **Audit trail**: Session records track when and where authentication occurred\n4. **Token rotation**: Sessions enable secure refresh token rotation strategies\n\n### Implementation Requirements for Login Operation\n\nWhen implementing a login operation, you MUST include these core phases. Additional business logic may be inserted at any point as needed:\n\n#### Phase 1: Validate Actor Credentials\nFirst, verify the actor's credentials and retrieve the actor record. This is **mandatory**:\n\n```typescript\n// Example: Validating seller credentials\nconst seller = await MyGlobal.prisma.shopping_sellers.findFirst({\n where: { email: props.body.email }\n});\nif (!seller) {\n throw new HttpException(\"Invalid credentials\", 401);\n}\n\n// Verify password using PasswordUtil\nconst isValid = await PasswordUtil.verify(\n props.body.password, // plain password from request\n seller.password_hash // hashed password from database\n);\nif (!isValid) {\n throw new HttpException(\"Invalid credentials\", 401);\n}\n```\n\n#### Phase 2: Create Session Record\nAfter successful authentication, create a NEW session record for this login. This is **mandatory**:\n\n```typescript\n// Example: Creating a new session for the authenticated seller\nconst accessExpires: Date = new Date(Date.now() + 60 * 60 * 1000);\nconst refreshExpires: Date = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);\nconst session = await MyGlobal.prisma.shopping_seller_sessions.create({\n data: {\n id: v4(),\n shopping_seller_id: seller.id, // Foreign key to actor\n ip: props.body.ip ?? props.ip, // IP is optional - use client-provided (SSR case) or server-extracted\n href: props.body.href,\n referrer: props.body.referrer,\n created_at: new Date().toISOString(),\n expired_at: toISOStringSafe(accessExpires),\n }\n});\n```\n\n**CRITICAL**: Each login creates a NEW session. Both the actor ID and session ID will be embedded in the JWT token payload (see JWT Token Generation section below).\n\n#### Additional Business Logic (Optional)\nBetween or after the mandatory phases above, you may implement additional business logic as specified in the API requirements. Examples include:\n- Updating last login timestamp on the actor record\n- Creating audit logs or login history records\n- Checking account status (e.g., banned, suspended, email verified)\n- Enforcing rate limiting or login attempt tracking\n- Invalidating old sessions if needed (e.g., single device policy)\n- Sending login notification emails or SMS\n- Tracking login analytics or metrics\n- Any other domain-specific operations required by the business\n\n**The key principle**: The mandatory phases (credential validation, session creation, JWT generation) must always be present, but you have complete flexibility to add necessary business logic around them.\n\n### Database Schema Pattern\n\nLogin operations interact with two related tables:\n\n1. **Actor Table** (e.g., `shopping_sellers`): Stores persistent user identity\n - Primary key: `id` (UUID)\n - Contains: email, password_hash, profile information\n - Represents: \"Who the user is\"\n\n2. **Session Table** (e.g., `shopping_seller_sessions`): Stores authentication sessions\n - Primary key: `id` (UUID)\n - Foreign key: `shopping_seller_id` (references actor)\n - Represents: \"An active authentication instance for this user\"\n\nRefer to **REALIZE_AUTHORIZATION.md** for detailed session architecture and relationship patterns.\n\n## MANDATORY: Use PasswordUtil for Password Verification\n\n**CRITICAL**: You MUST use PasswordUtil utilities for password verification to ensure consistency with the join operation:\n\n```typescript\n// Example: Password verification in login\nconst isValid = await PasswordUtil.verify(\n props.body.password, // plain password from request\n user.password_hash // hashed password from database\n);\nif (!isValid) {\n throw new HttpException(\"Invalid credentials\", 401);\n}\n```\n\n## JWT Token Generation\n\n### Conceptual Foundation: Token Payload Structure\n\nThe JWT token payload serves as a **cryptographically signed credential** that identifies both the actor and their specific authentication session. This dual identification enables:\n\n1. **Actor identification**: `id` field identifies which user is authenticated\n2. **Session identification**: `session_id` field identifies which authentication instance is active\n3. **Role-based access**: `type` field enables discriminated union patterns for authorization\n\n### Token Payload Structure\n\n**CRITICAL**: Use the predefined payload structures for consistency:\n\n```json\n{{PAYLOAD}}\n```\n\n**NOTE**: The jsonwebtoken library is automatically imported as jwt. Use it to generate tokens with the EXACT payload structure:\n\n```typescript\ninterface IJwtSignIn {\n type: string; // Actor type name (e.g., \"seller\", \"user\", \"admin\")\n id: string & tags.Format<\"uuid\">; // Actor's primary ID\n session_id: string & tags.Format<\"uuid\">; // Session's primary ID\n created_at: string & tags.Format<\"date-time\">; // Token creation timestamp\n}\n```\n\n### Implementation Example\n\n```typescript\n// JWT is already imported: import jwt from \"jsonwebtoken\";\n\n// After validating credentials and creating a NEW session:\n// Phase 1: Validate actor\nconst seller = await MyGlobal.prisma.shopping_sellers.findFirst({\n where: { email: props.body.email }\n});\nconst isValid = await PasswordUtil.verify(props.body.password, seller.password_hash);\nif (!isValid) {\n throw new HttpException(\"Invalid credentials\", 401);\n}\n\n// Phase 2: Create NEW session\nconst accessExpires: Date = new Date(Date.now() + 60 * 60 * 1000);\nconst refreshExpires: Date = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);\nconst session = await MyGlobal.prisma.shopping_seller_sessions.create({\n data: {\n id: v4(),\n shopping_seller_id: seller.id,\n ip: props.body.ip ?? props.ip,\n href: props.body.href,\n referrer: props.body.referrer,\n created_at: new Date().toISOString(),\n expired_at: toISOStringSafe(accessExpires),\n }\n});\n\n// Phase 3: Generate JWT token with EXACT payload structure\n// DO NOT use type annotations like: const payload: IJwtSignIn = {...}\n// Just create the payload object directly in jwt.sign()\nconst token = {\n access: jwt.sign(\n {\n type: \"seller\", // Actor type discriminator\n id: seller.id, // Actor's ID (NOT session.id!)\n session_id: session.id, // Session's ID\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"1h\",\n issuer: \"autobe\", // MUST use 'autobe' as issuer\n }\n ),\n refresh: jwt.sign(\n {\n type: \"seller\",\n id: seller.id,\n session_id: session.id,\n tokenType: \"refresh\",\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"7d\",\n issuer: \"autobe\", // MUST use 'autobe' as issuer\n },\n ),\n expired_at: toISOStringSafe(accessExpires),\n refreshable_until: toISOStringSafe(refreshExpires),\n};\n```\n\n### Critical Rules for Token Generation\n\n1. **Payload Structure**: Use the exact structure shown above - `type`, `id`, `session_id`, `created_at`\n2. **Actor ID**: The `id` field MUST contain the actor's primary key (e.g., `seller.id`), NOT the session's ID\n3. **Session ID**: The `session_id` field MUST contain the session's primary key (e.g., `session.id`)\n4. **Type Discriminator**: The `type` field MUST match the actor type (e.g., \"seller\", \"user\", \"admin\")\n5. **No Type Annotations**: Do NOT use TypeScript type annotations in the payload object passed to `jwt.sign()`\n6. **Issuer**: MUST use 'autobe' as the issuer for all tokens\n\n**DO NOT**:\n- Implement your own password hashing logic\n- Use bcrypt, argon2, or any other hashing library directly\n- Try to hash and compare manually\n\n## Token Decoding and Verification\n\n```typescript\n// Decode tokens if needed (e.g., for verification)\nconst decoded = jwt.verify(token, MyGlobal.env.JWT_SECRET_KEY, {\n issuer: 'autobe' // Verify issuer is 'autobe'\n});\n```\n\n## Complete Login Flow Examples\n\n### Example 1: Basic Login (Minimal)\n\n```typescript\n// Minimal example showing only mandatory phases\nexport async function postAuthSellerLogin(props: {\n body: IShoppingSeller.ILogin\n}): Promise<IShoppingSeller.ILoginOutput> {\n // 1. Find actor by credentials (MANDATORY)\n const seller = await MyGlobal.prisma.shopping_sellers.findFirst({\n where: { email: props.body.email }\n });\n if (!seller) {\n throw new HttpException(\"Invalid credentials\", 401);\n }\n\n // 2. Verify password (MANDATORY)\n const isValid = await PasswordUtil.verify(\n props.body.password,\n seller.password_hash\n );\n if (!isValid) {\n throw new HttpException(\"Invalid credentials\", 401);\n }\n\n // 3. Create NEW session record (MANDATORY)\n const accessExpires: Date = new Date(Date.now() + 60 * 60 * 1000);\n const refreshExpires: Date = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);\n const session = await MyGlobal.prisma.shopping_seller_sessions.create({\n data: {\n id: v4(),\n shopping_seller_id: seller.id,\n ip: props.body.ip ?? props.ip,\n href: props.body.href,\n referrer: props.body.referrer,\n created_at: new Date().toISOString(),\n expired_at: toISOStringSafe(accessExpires),\n },\n });\n\n // 4. Generate JWT tokens (MANDATORY)\n const token = {\n accessToken: jwt.sign(\n {\n type: \"seller\",\n id: seller.id,\n session_id: session.id,\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"1h\",\n issuer: \"autobe\",\n }\n ),\n refreshToken: jwt.sign(\n {\n type: \"seller\",\n id: seller.id,\n session_id: session.id,\n tokenType: \"refresh\",\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"7d\",\n issuer: \"autobe\",\n }\n ),\n expired_at: toISOStringSafe(accessExpires),\n refreshable_until: toISOStringSafe(refreshExpires),\n };\n\n // 5. Return with authorization token\n return {\n id: seller.id,\n email: seller.email,\n // ... other fields\n token,\n } satisfies IShoppingSeller.IAuthorized;\n}\n```\n\n### Example 2: Login with Additional Business Logic\n\n```typescript\n// Example showing additional business logic integrated with mandatory phases\nexport async function postAuthUserLogin(props: {\n ip: string;\n body: IUser.ILogin\n}): Promise<IUser.ILoginOutput> {\n // 1. Find actor by credentials (MANDATORY)\n const user = await MyGlobal.prisma.users.findFirst({\n where: { email: props.body.email }\n });\n if (!user) {\n throw new HttpException(\"Invalid credentials\", 401);\n }\n\n // 2. ADDITIONAL BUSINESS LOGIC: Check account status\n if (user.status === 'banned') {\n throw new HttpException(\"Account has been banned\", 403);\n }\n if (user.status === 'suspended') {\n throw new HttpException(\"Account is temporarily suspended\", 403);\n }\n if (!user.email_verified) {\n throw new HttpException(\"Please verify your email first\", 403);\n }\n\n // 3. Verify password (MANDATORY)\n const isValid = await PasswordUtil.verify(\n props.body.password,\n user.password_hash\n );\n if (!isValid) {\n // ADDITIONAL BUSINESS LOGIC: Track failed login attempt\n await MyGlobal.prisma.login_attempts.create({\n data: {\n id: v4(),\n user_id: user.id,\n success: false,\n ip: props.body.ip ?? props.ip,\n created_at: new Date().toISOString(),\n }\n });\n throw new HttpException(\"Invalid credentials\", 401);\n }\n\n // 4. ADDITIONAL BUSINESS LOGIC: Update last login timestamp\n await MyGlobal.prisma.users.update({\n where: { id: user.id },\n data: {\n last_login_at: new Date().toISOString(),\n last_login_ip: props.body.ip,\n }\n });\n\n // 5. ADDITIONAL BUSINESS LOGIC: Invalidate old sessions (single device policy)\n if (props.body.single_device_only) {\n await MyGlobal.prisma.user_sessions.updateMany({\n where: {\n user_id: user.id,\n expired_at: { gt: new Date().toISOString() }\n },\n data: {\n expired_at: new Date().toISOString() // Expire immediately\n }\n });\n }\n\n // 6. Create NEW session record (MANDATORY)\n const accessExpires: Date = new Date(Date.now() + 60 * 60 * 1000);\n const refreshExpires: Date = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);\n const session = await MyGlobal.prisma.user_sessions.create({\n data: {\n id: v4(),\n user_id: user.id,\n ip: props.body.ip ?? props.ip,\n href: props.body.href,\n referrer: props.body.referrer,\n user_agent: props.body.user_agent,\n created_at: new Date().toISOString(),\n expired_at: toISOStringSafe(accessExpires),\n },\n });\n\n // 7. ADDITIONAL BUSINESS LOGIC: Create audit log\n await MyGlobal.prisma.audit_logs.create({\n data: {\n id: v4(),\n user_id: user.id,\n session_id: session.id,\n action: 'USER_LOGIN',\n ip: props.body.ip ?? props.ip,\n created_at: new Date().toISOString(),\n }\n });\n\n // 8. ADDITIONAL BUSINESS LOGIC: Track successful login attempt\n await MyGlobal.prisma.login_attempts.create({\n data: {\n id: v4(),\n user_id: user.id,\n success: true,\n ip: props.body.ip ?? props.ip,\n session_id: session.id,\n created_at: new Date().toISOString(),\n }\n });\n\n // 9. Generate JWT tokens (MANDATORY)\n const token = {\n accessToken: jwt.sign(\n {\n type: \"user\",\n id: user.id,\n session_id: session.id,\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"1h\",\n issuer: \"autobe\",\n }\n ),\n refreshToken: jwt.sign(\n {\n type: \"user\",\n id: user.id,\n session_id: session.id,\n tokenType: \"refresh\",\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"7d\",\n issuer: \"autobe\",\n }\n ),\n expired_at: toISOStringSafe(accessExpires),\n refreshable_until: toISOStringSafe(refreshExpires),\n };\n\n // 10. ADDITIONAL BUSINESS LOGIC: Send login notification (async, don't await)\n // NotificationService.sendLoginAlert(user.email, props.body.ip).catch(console.error);\n\n // 11. Return with authorization token\n return {\n id: user.id,\n email: user.email,\n last_login_at: user.last_login_at,\n // ... other fields\n token,\n } satisfies IUser.IAuthorized;\n}\n```\n\n**IMPORTANT**:\n- The mandatory phases (credential validation, password verification, session creation, JWT generation) must always be present\n- Additional business logic can be inserted at any appropriate point in the flow\n- Consider security implications of additional logic (e.g., rate limiting, account status checks)\n- Consider transaction boundaries if multiple database operations must succeed or fail together\n- Since this is a login operation, it must be publicly accessible without authentication" /* AutoBeSystemPromptConstant.REALIZE_MEMBERSHIP_LOGIN */, join: "<!--\nfilename: REALIZE_MEMBERSHIP_JOIN.md\n-->\n# Authorization Type: Join (Registration)\n\nThis is a **join** operation for user registration.\n\n## Implementation Guidelines for Join\n\n### Join (Registration) Operation Requirements\n- This is a user registration endpoint\n- Must validate all required user information\n- Should check for duplicate accounts (email, username, etc.)\n- Must hash passwords before storing (NEVER store plain passwords)\n- Must create the actor record (user/member) in the database\n- Must create a session record for the newly registered actor\n- Must generate JWT tokens with correct payload structure\n- May include additional business logic as required by the API specification (e.g., creating related records, sending welcome emails, initializing user preferences)\n- Must NOT require authentication decorator (public endpoint)\n\n**IMPORTANT**: While the core requirements (actor creation, session creation, JWT generation) are mandatory, you should implement any additional business logic specified in the API requirements. The examples below show the mandatory flow, but your implementation may include additional steps before, between, or after these core operations.\n\n## Session Management Architecture\n\n### Conceptual Foundation: Actor and Session Separation\n\nIn production authentication systems, we separate **Actor** (the persistent user identity) from **Session** (the temporary authentication state). This architectural pattern provides several critical benefits:\n\n1. **Security**: Sessions can be independently revoked without deleting the user account\n2. **Multi-device support**: One actor can maintain multiple concurrent sessions across different devices\n3. **Audit trail**: Session records track when and where authentication occurred\n4. **Token rotation**: Sessions enable secure refresh token rotation strategies\n\n### Implementation Requirements for Join Operation\n\nWhen implementing a join (registration) operation, you MUST include these core phases. Additional business logic may be inserted at any point as needed:\n\n#### Phase 1: Create Actor Record\nFirst, create the primary actor record (e.g., `shopping_sellers`, `users`, `admins`). This is **mandatory**:\n\n```typescript\n// Example: Creating a seller actor\nconst hashedPassword: string = await PasswordUtil.hash(props.body.password);\nconst seller = await MyGlobal.prisma.shopping_sellers.create({\n data: {\n id: v4(),\n email: props.body.email,\n password_hash: hashedPassword, // Never store plain passwords\n created_at: toISOStringSafe(new Date()),\n // ... other actor-specific fields\n }\n});\n```\n\n#### Phase 2: Create Session Record\nAfter creating the actor, create an associated session record (e.g., `shopping_seller_sessions`). This is **mandatory**:\n\n```typescript\n// Example: Creating a session for the newly registered seller\nconst accessExpires: Date = new Date(Date.now() + 60 * 60 * 1000);\nconst refreshExpires: Date = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);\nconst session = await MyGlobal.prisma.shopping_seller_sessions.create({\n data: {\n id: v4(),\n shopping_seller_id: seller.id, // Foreign key to actor\n ip: props.body.ip ?? props.ip, // IP is optional - use client-provided (SSR case) or server-extracted\n href: props.body.href,\n referrer: props.body.referrer,\n created_at: toISOStringSafe(new Date()),\n expired_at: toISOStringSafe(accessExpires),\n }\n});\n```\n\n**CRITICAL**: Both the actor ID and session ID will be embedded in the JWT token payload (see JWT Token Generation section below).\n\n#### Additional Business Logic (Optional)\nBetween or after the mandatory phases above, you may implement additional business logic as specified in the API requirements. Examples include:\n- Creating related records in other tables (e.g., user profiles, preferences, initial data)\n- Sending notification emails or SMS\n- Initializing default settings or configurations\n- Creating audit logs or tracking records\n- Integrating with external services\n- Any other domain-specific operations required by the business\n\n**The key principle**: The mandatory phases (actor creation, session creation, JWT generation) must always be present, but you have complete flexibility to add necessary business logic around them.\n\n### Database Schema Pattern\n\nRegistration operations typically involve two related tables:\n\n1. **Actor Table** (e.g., `shopping_sellers`): Stores persistent user identity\n - Primary key: `id` (UUID)\n - Contains: email, password_hash, profile information\n - Represents: \"Who the user is\"\n\n2. **Session Table** (e.g., `shopping_seller_sessions`): Stores authentication sessions\n - Primary key: `id` (UUID)\n - Foreign key: `shopping_seller_id` (references actor)\n - Represents: \"An active authentication instance for this user\"\n\nRefer to **REALIZE_AUTHORIZATION.md** for detailed session architecture and relationship patterns.\n\n## MANDATORY: Use PasswordUtil for Password Hashing\n\n**CRITICAL**: You MUST use PasswordUtil utilities for password hashing to ensure consistency across all authentication operations:\n\n```typescript\n// Example: Password hashing in join/registration\nconst hashedPassword: string = await PasswordUtil.hash(props.body.password);\n\n// Store the hashed password in database\nawait MyGlobal.prisma.users.create({\n data: {\n id: v4(),\n email: props.body.email,\n password_hash: hashedPassword, // Store the hash, never plain password\n created_at: toISOStringSafe(new Date()),\n // ... other fields\n }\n});\n```\n\n**DO NOT**:\n- Store plain passwords in the database\n- Use bcrypt, argon2, or any other hashing library directly\n- Implement your own hashing logic\n\n## JWT Token Generation After Registration\n\n### Conceptual Foundation: Token Payload Structure\n\nThe JWT token payload serves as a **cryptographically signed credential** that identifies both the actor and their specific authentication session. This dual identification enables:\n\n1. **Actor identification**: `id` field identifies which user is authenticated\n2. **Session identification**: `session_id` field identifies which authentication instance is active\n3. **Role-based access**: `type` field enables discriminated union patterns for authorization\n\n### Token Payload Structure\n\n**CRITICAL**: Use the predefined payload structures for consistency:\n\n```json\n{{PAYLOAD}}\n```\n\n**NOTE**: The jsonwebtoken library is automatically imported as jwt. After successful registration, generate tokens with the EXACT payload structure:\n\n```typescript\ninterface IJwtSignIn {\n type: string; // Actor type name (e.g., \"seller\", \"user\", \"admin\")\n id: string & tags.Format<\"uuid\">; // Actor's primary ID\n session_id: string & tags.Format<\"uuid\">; // Session's primary ID\n created_at: string & tags.Format<\"date-time\">; // Token creation timestamp\n}\n```\n\n### Implementation Example\n\n```typescript\n// JWT is already imported: import jwt from \"jsonwebtoken\";\n\n// After creating BOTH the actor and session records:\n// Phase 1: Create actor\nconst hashedPassword: string = await PasswordUtil.hash(props.body.password);\nconst seller = await MyGlobal.prisma.shopping_sellers.create({\n data: {\n id: v4(),\n email: props.body.email,\n password_hash: hashedPassword,\n created_at: new Date().toISOString(),\n // ... other fields\n }\n});\n\n// Phase 2: Create session\nconst accessExpires: Date = new Date(Date.now() + 60 * 60 * 1000);\nconst refreshExpires: Date = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);\nconst session = await MyGlobal.prisma.shopping_seller_sessions.create({\n data: {\n id: v4(),\n shopping_seller_id: seller.id,\n ip: props.body.ip ?? props.ip,\n href: props.body.href,\n referrer: props.body.referrer,\n created_at: new Date().toISOString(),\n expired_at: toISOStringSafe(accessExpires),\n }\n});\n\n// Phase 3: Generate JWT token with EXACT payload structure\n// DO NOT use type annotations like: const payload: IJwtSignIn = {...}\n// Just create the payload object directly in jwt.sign()\nconst token = {\n access: jwt.sign(\n {\n type: \"seller\", // Actor type discriminator\n id: seller.id, // Actor's ID (NOT session.id!)\n session_id: session.id, // Session's ID\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"1h\",\n issuer: \"autobe\", // MUST use 'autobe' as issuer\n }\n ),\n refresh: jwt.sign(\n {\n type: \"seller\",\n id: seller.id,\n session_id: session.id,\n tokenType: \"refresh\",\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"7d\",\n issuer: \"autobe\", // MUST use 'autobe' as issuer\n },\n ),\n expired_at: toISOStringSafe(accessExpires),\n refreshable_until: toISOStringSafe(refreshExpires),\n};\n```\n\n### Critical Rules for Token Generation\n\n1. **Payload Structure**: Use the exact structure shown above - `type`, `id`, `session_id`, `created_at`\n2. **Actor ID**: The `id` field MUST contain the actor's primary key (e.g., `seller.id`), NOT the session's ID\n3. **Session ID**: The `session_id` field MUST contain the session's primary key (e.g., `session.id`)\n4. **Type Discriminator**: The `type` field MUST match the actor type (e.g., \"seller\", \"user\", \"admin\")\n5. **No Type Annotations**: Do NOT use TypeScript type annotations in the payload object passed to `jwt.sign()`\n6. **Issuer**: MUST use 'autobe' as the issuer for all tokens\n\n## Complete Registration Flow Examples\n\n### Example 1: Basic Registration (Minimal)\n\n```typescript\n// Minimal example showing only mandatory phases\nexport async function postAuthSellerJoin(props: {\n ip: string;\n body: IShoppingSeller.IJoin;\n}): Promise<IShoppingSeller.IJoinOutput> {\n // 1. Check for duplicate account\n const existing = await MyGlobal.prisma.shopping_sellers.findFirst({\n where: { email: props.body.email }\n });\n if (existing) {\n throw new HttpException(\"Email already registered\", 409);\n }\n\n // 2. Hash password (MANDATORY)\n const hashedPassword = await PasswordUtil.hash(props.body.password);\n\n // 3. Create actor record (MANDATORY)\n const seller = await MyGlobal.prisma.shopping_sellers.create({\n data: {\n id: v4(),\n email: props.body.email,\n password_hash: hashedPassword,\n created_at: new Date().toISOString(),\n // ... other fields\n }\n });\n\n // 4. Create session record (MANDATORY)\n const accessExpires = new Date(Date.now() + 60 * 60 * 1000);\n const refreshExpires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);\n const session = await MyGlobal.prisma.shopping_seller_sessions.create({\n data: {\n id: v4(),\n shopping_seller_id: seller.id,\n ip: props.body.ip ?? props.ip,\n href: props.body.href,\n referrer: props.body.referrer,\n created_at: toISOStringSafe(new Date()),\n expired_at: toISOStringSafe(accessExpires),\n }\n });\n\n // 5. Generate JWT tokens (MANDATORY)\n const token = {\n accessToken: jwt.sign(\n {\n type: \"seller\",\n id: seller.id,\n session_id: session.id,\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"1h\",\n issuer: \"autobe\",\n }\n ),\n refreshToken: jwt.sign(\n {\n type: \"seller\",\n id: seller.id,\n session_id: session.id,\n tokenType: \"refresh\",\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"7d\",\n issuer: \"autobe\",\n }\n ),\n expired_at: toISOStringSafe(accessExpires),\n refreshable_until: toISOStringSafe(refreshExpires),\n };\n\n // 6. Return with authorization token\n return {\n id: seller.id,\n email: seller.email,\n // ... other fields\n token,\n } satisfies IShoppingSeller.IAuthorized;\n}\n```\n\n### Example 2: Registration with Additional Business Logic\n\n```typescript\n// Example showing additional business logic integrated with mandatory phases\nexport async function postAuthUserJoin(props: {\n ip: string;\n body: IUser.IJoin\n}): Promise<IUser.IJoinOutput> {\n // 1. Validation and duplicate check\n const existing = await MyGlobal.prisma.users.findFirst({\n where: { email: props.body.email }\n });\n if (existing) {\n throw new HttpException(\"Email already registered\", 409);\n }\n\n // 2. Hash password (MANDATORY)\n const hashedPassword = await PasswordUtil.hash(props.body.password);\n\n // 3. Create actor record (MANDATORY)\n const user = await MyGlobal.prisma.users.create({\n data: {\n id: v4(),\n email: props.body.email,\n password_hash: hashedPassword,\n created_at: new Date().toISOString(),\n // ... other fields\n }\n });\n\n // 4. ADDITIONAL BUSINESS LOGIC: Create user profile\n const profile = await MyGlobal.prisma.user_profiles.create({\n data: {\n id: v4(),\n user_id: user.id,\n nickname: props.body.nickname,\n avatar_url: props.body.avatar_url,\n created_at: new Date().toISOString(),\n }\n });\n\n // 5. ADDITIONAL BUSINESS LOGIC: Initialize user preferences\n await MyGlobal.prisma.user_preferences.create({\n data: {\n id: v4(),\n user_id: user.id,\n language: props.body.preferred_language ?? 'en',\n theme: 'light',\n notifications_enabled: true,\n }\n });\n\n // 6. ADDITIONAL BUSINESS LOGIC: Create audit log\n await MyGlobal.prisma.audit_logs.create({\n data: {\n id: v4(),\n user_id: user.id,\n action: 'USER_REGISTERED',\n ip_address: props.body.ip ?? props.ip,\n created_at: new Date().toISOString(),\n }\n });\n\n // 7. Create session record (MANDATORY)\n const accessExpires = new Date(Date.now() + 60 * 60 * 1000);\n const refreshExpires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);\n const session = await MyGlobal.prisma.user_sessions.create({\n data: {\n id: v4(),\n user_id: user.id,\n ip: props.body.ip ?? props.ip,\n href: props.body.href,\n referrer: props.body.referrer,\n created_at: toISOStringSafe(new Date()),\n expired_at: toISOStringSafe(accessExpires),\n }\n });\n\n // 8. Generate JWT tokens (MANDATORY)\n const token = {\n accessToken: jwt.sign(\n {\n type: \"user\",\n id: user.id,\n session_id: session.id,\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"1h\",\n issuer: \"autobe\",\n }\n ),\n refreshToken: jwt.sign(\n {\n type: \"user\",\n id: user.id,\n session_id: session.id,\n tokenType: \"refresh\",\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: \"7d\",\n issuer: \"autobe\",\n }\n ),\n expired_at: toISOStringSafe(accessExpires),\n refreshable_until: toISOStringSafe(refreshExpires),\n };\n\n // 9. ADDITIONAL BUSINESS LOGIC: Send welcome email (async, don't await)\n // EmailService.sendWelcomeEmail(user.email, user.nickname).catch(console.error);\n\n // 10. Return with authorization token and additional data\n return {\n id: user.id,\n email: user.email,\n profile: {\n nickname: profile.nickname,\n avatar_url: profile.avatar_url,\n },\n token,\n } satisfies IUser.IAuthorized;\n}\n```\n\n**IMPORTANT**:\n- The mandatory phases (password hashing, actor creation, session creation, JWT generation) must always be present\n- Additional business logic can be inserted at any appropriate point in the flow\n- Consider transaction boundaries if multiple database operations must succeed or fail together\n- Since this is a registration operation, it must be publicly accessible\n- Always hash passwords before storing" /* AutoBeSystemPromptConstant.REALIZE_MEMBERSHIP_JOIN */, refresh: "<!--\nfilename: REALIZE_MEMBERSHIP_REFRESH.md\n-->\n# Authorization Type: Refresh Token\n\nThis is a **refresh** token operation for renewing expired access tokens.\n\n## Implementation Guidelines for Refresh\n\n### Refresh Token Operation Requirements\n- This endpoint refreshes expired access tokens\n- Must validate the refresh token first\n- Should check if refresh token is not expired or revoked\n- Must generate a new access token with THE SAME payload structure\n- May also rotate the refresh token for security\n- Should handle invalid/expired refresh tokens gracefully\n- Typically requires the refresh token in request body or headers\n- Must NOT require standard authentication (uses refresh token instead)\n\n## Session Management Architecture\n\n### Conceptual Foundation: Session Continuity and Security\n\nThe refresh token operation maintains **session continuity** while enhancing security through token rotation. This architectural pattern provides:\n\n1. **Uninterrupted access**: Users remain authenticated without re-entering credentials\n2. **Token rotation**: Minimizes risk by regularly replacing tokens\n3. **Session validation**: Verifies the session is still valid and not revoked\n4. **Attack detection**: Identifies potential token theft through session tracking\n\n### Implementation Requirements for Refresh Operation\n\nWhen implementing a refresh token operation, you MUST follow this validation and regeneration process:\n\n#### Phase 1: Verify Refresh Token and Session\nFirst, decode and verify the refresh token, then validate the associated session:\n\n```typescript\n// Example: Verify refresh token\nconst decoded = jwt.verify(\n props.body.refreshToken,\n MyGlobal.env.JWT_SECRET_KEY,\n { issuer: 'autobe' }\n) as {\n id: string;\n session_id: string;\n type: \"seller\";\n};\n\n// Validate the session still exists and is active\nconst session = await MyGlobal.prisma.shopping_seller_sessions.findFirst({\n where: {\n id: decoded.session_id,\n shopping_seller_id: decoded.id,\n // Check session validity (e.g., not revoked, not expired)\n },\n include: {\n seller: true, // Include actor for validation\n // Add other relations if needed\n }\n});\nif (!session) {\n throw new HttpException(\"Session expired or revoked\", 401);\n} else if (session.seller.deleted_at !== null) {\n throw new HttpException(\"Account has been deleted\", 403);\n}\n```\n\n#### Phase 2: Generate New Tokens (Same Session)\nAfter validation, generate NEW tokens using the **SAME session ID**:\n\n```typescript\n// Example: Generate new tokens maintaining the same session\nconst accessExpires: Date = new Date(Date.now() + 60 * 60 * 1000);\nconst refreshExpires: Date = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);\nconst token = {\n access: jwt.sign(\n {\n type: decoded.type,\n id: decoded.id,\n session_id: decoded.session_id, // SAME session ID\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: '1h',\n issuer: 'autobe'\n }\n ),\n refresh: jwt.sign(\n {\n type: decoded.type,\n id: decoded.id,\n session_id: decoded.session_id, // SAME session ID\n tokenType: 'refresh',\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: '7d',\n issuer: 'autobe'\n }\n ),\n expired_at: toISOStringSafe(accessExpires),\n refreshable_until: toISOStringSafe(refreshExpires),\n};\n```\n\n#### Phase 3: Update Session Expiration\nAfter generating new tokens, update the session's `expired_at` timestamp:\n\n```typescript\n// Update session expiration time\nawait MyGlobal.prisma.shopping_seller_sessions.update({\n where: {\n id: decoded.session_id,\n },\n data: {\n expired_at: refreshExpires, // Update to new refresh token expiration\n },\n});\n```\n\n**CRITICAL**: The refresh operation does NOT create a new session. It reuses the existing `session_id` from the decoded token. However, the session's `expired_at` field MUST be updated to reflect the new refresh token expiration time.\n\n### Database Schema Pattern\n\nRefresh operations interact with session and actor tables:\n\n1. **Session Table** (e.g., `shopping_seller_sessions`): Validates session is active\n - Primary key: `id` (UUID) - This ID is maintained across token refreshes\n - Foreign key: `shopping_seller_id` (references actor)\n - Represents: \"The ongoing authentication session\"\n\n2. **Actor Table** (e.g., `shopping_sellers`): Validates actor is still active\n - Primary key: `id` (UUID)\n - Contains: email, password_hash, profile information\n - Represents: \"Who the user is\"\n\nRefer to **REALIZE_AUTHORIZATION.md** for detailed session architecture and relationship patterns.\n\n## CRITICAL: Refresh Token Implementation\n\n### Conceptual Foundation: Token Payload Structure\n\nThe JWT token payload serves as a **cryptographically signed credential** that identifies both the actor and their specific authentication session. During refresh operations:\n\n1. **Session continuity**: The `session_id` remains unchanged across refreshes\n2. **Actor identification**: The `id` field continues to identify the same user\n3. **Token rotation**: New cryptographic signatures are generated\n4. **Timestamp update**: The `created_at` field reflects the refresh time\n\n### Token Payload Structure\n\n**IMPORTANT**: When refreshing tokens, you MUST:\n1. Decode and verify the refresh token\n2. Extract the user information from the decoded token\n3. Generate a new access token with THE SAME payload structure as the original\n\n**CRITICAL**: Use the predefined payload structures for consistency:\n\n```json\n{{PAYLOAD}}\n```\n\n```typescript\ninterface IJwtSignIn {\n type: string; // Actor type name (e.g., \"seller\", \"user\", \"admin\")\n id: string & tags.Format<\"uuid\">; // Actor's primary ID\n session_id: string & tags.Format<\"uuid\">; // Session's primary ID (UNCHANGED)\n created_at: string & tags.Format<\"date-time\">; // Token creation timestamp (UPDATED)\n}\n```\n\n### Implementation Example\n\n```typescript\n// JWT is already imported: import jwt from \"jsonwebtoken\";\n\n// Step 1: Verify and decode the refresh token\nconst decoded = jwt.verify(\n props.body.refreshToken,\n MyGlobal.env.JWT_SECRET_KEY,\n { issuer: 'autobe' }\n) as {\n id: string;\n session_id: string;\n type: \"seller\";\n};\n\n// Step 2: Validate session and get actor data\nconst session = await MyGlobal.prisma.shopping_seller_sessions.findFirst({\n where: {\n id: decoded.session_id,\n shopping_seller_id: decoded.id,\n },\n include: {\n shopping_seller: true,\n // Add other relations if needed\n }\n});\nif (!session) {\n throw new HttpException(\"Session expired or revoked\", 401);\n} else if (session.shopping_seller.deleted_at !== null) {\n throw new HttpException(\"Account has been deleted\", 403);\n}\n\n// Step 3: Generate new access token with SAME session_id\n// DO NOT use type annotations like: const payload: IJwtSignIn = {...}\n// Just create the payload object directly in jwt.sign()\nconst accessExpires: Date = new Date(Date.now() + 60 * 60 * 1000);\nconst refreshExpires: Date = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);\nconst access = {\n access: jwt.sign(\n {\n type: decoded.type,\n id: decoded.id,\n session_id: decoded.session_id, // CRITICAL: Reuse same session_id\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: '1h',\n issuer: 'autobe'\n }\n ),\n refresh: jwt.sign(\n {\n type: decoded.type,\n id: decoded.id,\n session_id: decoded.session_id, // CRITICAL: Reuse same session_id\n tokenType: 'refresh',\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: '7d',\n issuer: 'autobe'\n }\n ),\n expired_at: toISOStringSafe(accessExpires),\n refreshable_until: toISOStringSafe(refreshExpires),\n};\n\n// Step 4: Update session expiration time\nawait MyGlobal.prisma.shopping_seller_sessions.update({\n where: {\n id: decoded.session_id,\n },\n data: {\n expired_at: refreshExpires,\n },\n});\n```\n\n### Critical Rules for Token Refresh\n\n1. **Session Preservation**: The `session_id` MUST remain the same as in the original token\n2. **Payload Structure**: Use the exact structure - `type`, `id`, `session_id`, `created_at`\n3. **Actor ID**: The `id` field MUST match the original token's actor ID\n4. **Type Consistency**: The `type` field MUST match the original token's type\n5. **No Type Annotations**: Do NOT use TypeScript type annotations in the payload object\n6. **Issuer**: MUST use 'autobe' as the issuer for all tokens\n\n**DO NOT**:\n- Generate new access tokens with different payload structures\n- Use random IDs like v4() in the payload\n- Create tokens without verifying the refresh token first\n- Use type annotations like: const payload: UserPayload = {...}\n- Create a NEW session (this is NOT a login operation)\n- Change the `session_id` value (this breaks session continuity)\n\n## Complete Refresh Flow Example\n\n```typescript\n// Complete example for shopping_sellers token refresh\nexport async function postAuthSellerRefresh(props: {\n body: IShoppingSeller.IRefresh\n}): Promise<IShoppingSeller.IRefreshOutput> {\n // 1. Verify and decode refresh token\n let decoded: {\n id: string;\n session_id: string;\n type: \"seller\";\n };\n try {\n decoded = jwt.verify(\n props.body.refreshToken,\n MyGlobal.env.JWT_SECRET_KEY,\n { issuer: 'autobe' }\n ) as {\n id: string;\n session_id: string;\n type: \"seller\";\n };\n } catch (error) {\n throw new UnauthorizedException(\"Invalid or expired refresh token\");\n }\n\n // 2. Validate type matches expected actor type\n if (decoded.type !== \"seller\") {\n throw new ForbiddenException(\"Invalid token type\");\n }\n\n // 3. Validate session exists and is active\n const session = await MyGlobal.prisma.shopping_seller_sessions.findFirst({\n where: {\n id: decoded.session_id,\n shopping_seller_id: decoded.id,\n },\n include: {\n seller: true,\n // Add other relations if needed\n }\n });\n if (!session) {\n throw new HttpException(\"Session expired or revoked\", 401);\n } else if (session.shopping_seller.deleted_at !== null) {\n throw new HttpException(\"Account has been deleted\", 403);\n }\n\n // 5. Generate new access token (SAME session_id)\n const accessExpires: Date = new Date(Date.now() + 60 * 60 * 1000);\n const refreshExpires: Date = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);\n const token = {\n access: jwt.sign(\n {\n type: decoded.type,\n id: decoded.id,\n session_id: decoded.session_id, // Reuse existing session\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: '1h',\n issuer: 'autobe'\n }\n ),\n refresh: jwt.sign(\n {\n type: decoded.type,\n id: decoded.id,\n session_id: decoded.session_id, // Reuse existing session\n tokenType: 'refresh',\n created_at: new Date().toISOString(),\n },\n MyGlobal.env.JWT_SECRET_KEY,\n {\n expiresIn: '7d',\n issuer: 'autobe'\n }\n ),\n expired_at: toISOStringSafe(accessExpires),\n refreshable_until: toISOStringSafe(refreshExpires),\n };\n\n // 6. Update session expiration time\n await MyGlobal.prisma.shopping_seller_sessions.update({\n where: {\n id: decoded.session_id,\n },\n data: {\n expired_at: refreshExpires,\n },\n });\n\n // 7. Return new tokens\n return {\n accessToken: token.access,\n refreshToken: token.refresh,\n };\n}\n```\n\n**IMPORTANT**: The new access token MUST have the same payload structure as the original token from login/join operations, with the SAME `session_id` value to maintain session continuity." /* AutoBeSystemPromptConstant.REALIZE_MEMBERSHIP_REFRESH */, }; //# sourceMappingURL=transformRealizeWriteMembershipHistory.js.map