{
    "info": {
        "title": "SSO BPS API Documentation",
        "description": "Single Sign-On API for BPS Jayawijaya applications with OAuth 2.0 authentication and employee data access",
        "version": "1.0.0",
        "contact": {
            "name": "BPS Jayawijaya IT Team",
            "email": "it@bps9702.com"
        }
    },
    "oauth_flow": {
        "description": "OAuth 2.0 Authorization Code Flow for SSO authentication",
        "flow_type": "authorization_code",
        "overview": "Three-step process: client redirects user to SSO login, user authenticates, SSO redirects back with authorization code, client exchanges code for user data",
        "steps": [
            {
                "step": 1,
                "name": "Authorization Request",
                "description": "Client application redirects user to SSO authorization endpoint to initiate login",
                "endpoint": "/sso/authorize",
                "method": "GET",
                "redirect_required": true,
                "parameters": {
                    "client_id": {
                        "type": "string",
                        "required": true,
                        "description": "Your registered application's unique client ID",
                        "example": "my-app-client-id"
                    },
                    "state": {
                        "type": "string",
                        "required": false,
                        "description": "Random string for CSRF protection, returned unchanged in callback",
                        "example": "random-state-xyz123"
                    }
                },
                "example_url": "https://sso.bps9702.com/sso/authorize?client_id=my-app-client-id&state=random-state-xyz123",
                "response": {
                    "type": "redirect",
                    "description": "User is redirected to SSO login page (or forced logout if already logged in, then to login page)"
                },
                "implementation_notes": [
                    "User will be logged out if already authenticated to ensure fresh login",
                    "Only works with registered and active client applications",
                    "Callback URL is pre-configured in client app registration",
                    "State parameter is optional but recommended for security (CSRF protection)"
                ]
            },
            {
                "step": 2,
                "name": "User Authentication",
                "description": "User enters BPS credentials (NIL/Password) and is authenticated by SSO",
                "note": "This step is handled automatically by the SSO system. Only active users (is_active = true) can successfully authenticate.",
                "validation": [
                    "User must be registered in the system",
                    "User must be active (is_active = true)",
                    "Username/password must be correct",
                    "Account must not be suspended"
                ]
            },
            {
                "step": 3,
                "name": "Authorization Callback",
                "description": "SSO redirects back to your application with authorization code",
                "redirect_target": "Your application's pre-configured callback URL",
                "callback_parameters": {
                    "code": {
                        "type": "string",
                        "description": "Authorization code for token exchange (single-use, valid 10 minutes)",
                        "example": "abc123xyz789def456",
                        "properties": {
                            "length": "40 characters",
                            "expiration": "10 minutes",
                            "usage": "single-use only"
                        }
                    },
                    "state": {
                        "type": "string",
                        "description": "Same state value from step 1 (if provided). Always validate this matches!",
                        "example": "random-state-xyz123",
                        "validation_note": "CSRF protection: verify state matches your stored value"
                    }
                },
                "example_callback": "https://your-app.com/callback?code=abc123xyz789def456&state=random-state-xyz123",
                "error_scenarios": [
                    {
                        "scenario": "User authentication failed",
                        "result": "User is shown login error, callback is not called"
                    },
                    {
                        "scenario": "User inactive (is_active = false)",
                        "result": "User cannot complete authentication, callback is not called"
                    },
                    {
                        "scenario": "Client app is inactive",
                        "result": "Authorization fails at step 1, user cannot proceed"
                    }
                ]
            },
            {
                "step": 4,
                "name": "Token Exchange",
                "description": "Client application exchanges authorization code for user profile data",
                "endpoint": "/sso/token",
                "method": "POST",
                "backend_operation": true,
                "backend_note": "This step should be performed on your backend server, not in browser JavaScript",
                "request": {
                    "content_type": "application/x-www-form-urlencoded",
                    "parameters": {
                        "code": {
                            "type": "string",
                            "required": true,
                            "description": "Authorization code from callback (step 3)",
                            "example": "abc123xyz789def456"
                        },
                        "client_id": {
                            "type": "string",
                            "required": true,
                            "description": "Your registered application's client ID",
                            "example": "my-app-client-id"
                        },
                        "client_secret": {
                            "type": "string",
                            "required": true,
                            "description": "Your registered application's client secret (KEEP CONFIDENTIAL)",
                            "example": "super-secret-key-xyz789",
                            "security_note": "NEVER expose this in browser JavaScript or public code"
                        }
                    }
                },
                "response": {
                    "content_type": "application/json",
                    "status_code": 200,
                    "structure": {
                        "status": "success",
                        "data": {
                            "user_id": "string - unique user identifier",
                            "name": "string - full name",
                            "nip_9": "string - 9-digit employee ID",
                            "nip_18": "string - 18-digit national ID",
                            "email": "string - official email",
                            "gmail": "string - personal gmail (if available)",
                            "roles": "array - assigned role names"
                        }
                    },
                    "example": {
                        "status": "success",
                        "data": {
                            "user_id": "123456789",
                            "name": "John Doe",
                            "nip_9": "123456789",
                            "nip_18": "199001011234567890",
                            "email": "john@example.com",
                            "gmail": "john.doe@gmail.com",
                            "roles": [
                                "admin",
                                "user"
                            ]
                        }
                    }
                }
            }
        ],
        "security_notes": [
            "Authorization codes are single-use - they become invalid immediately after successful exchange",
            "Codes expire after 10 minutes if not used",
            "Each code is bound to a specific client_id",
            "Client secret must never be exposed in browser or public code",
            "Always validate state parameter to prevent CSRF attacks",
            "User must be active (is_active = true) to complete OAuth flow",
            "Client apps must be registered and active in the system",
            "All OAuth operations are logged for security auditing"
        ],
        "common_errors": [
            {
                "error_code": "MISSING_CLIENT_ID",
                "step": 1,
                "cause": "client_id parameter not provided",
                "solution": "Include client_id in authorize URL"
            },
            {
                "error_code": "INVALID_CLIENT",
                "step": 1,
                "cause": "client_id not registered or client app is inactive",
                "solution": "Register your application and ensure it is active"
            },
            {
                "error_code": "INVALID_GRANT",
                "step": 4,
                "cause": "Authorization code invalid, expired, or already used",
                "solution": "Restart OAuth flow to get a new authorization code"
            },
            {
                "error_code": "INVALID_CLIENT_SECRET",
                "step": 4,
                "cause": "client_secret does not match registered value",
                "solution": "Verify client_secret is correct and keep it confidential"
            }
        ]
    },
    "endpoints": {
        "oauth": [
            {
                "endpoint_id": "authorize",
                "name": "Authorize (Start OAuth Flow)",
                "description": "Initiates OAuth 2.0 authorization code flow. Redirects user to SSO login page.",
                "path": "/sso/authorize",
                "method": "GET",
                "authentication": "none",
                "public": true,
                "parameters": {
                    "client_id": {
                        "location": "query",
                        "type": "string",
                        "required": true,
                        "description": "Your registered application's client ID",
                        "example": "abc-12345"
                    },
                    "state": {
                        "location": "query",
                        "type": "string",
                        "required": false,
                        "description": "Random string for CSRF protection (recommended)",
                        "example": "random-state-xyz"
                    }
                },
                "response": {
                    "type": "redirect",
                    "description": "Redirects user to SSO login page, then to your callback URL with authorization code",
                    "success_callback": "https://your-app.com/callback?code=CODE&state=STATE"
                },
                "errors": [
                    {
                        "error_code": "MISSING_CLIENT_ID",
                        "http_status": 400,
                        "description": "client_id parameter is required but not provided",
                        "example": {
                            "status": "error",
                            "message": "Parameter client_id diperlukan",
                            "error_code": "MISSING_CLIENT_ID"
                        }
                    },
                    {
                        "error_code": "INVALID_CLIENT",
                        "http_status": 400,
                        "description": "Client ID not registered or client app is inactive",
                        "example": {
                            "status": "error",
                            "message": "Client ID tidak valid atau aplikasi tidak aktif",
                            "error_code": "INVALID_CLIENT"
                        }
                    }
                ],
                "curl_example": "curl -X GET 'https://sso.bps9702.com/sso/authorize?client_id=your-client-id&state=xyz123'",
                "implementation_notes": [
                    "User will be logged out if already authenticated to ensure fresh login",
                    "Only works with registered and active client applications",
                    "Callback URL must be pre-configured in client app registration",
                    "This is a GET endpoint - the client redirects the user directly",
                    "No authentication or client_secret needed for this step"
                ],
                "rate_limit": "None",
                "related_endpoints": [
                    "token",
                    "check"
                ],
                "full_url": "https://sso.bps9702.com/sso/sso/authorize"
            },
            {
                "endpoint_id": "token",
                "name": "Exchange Authorization Code for User Data",
                "description": "Exchange authorization code received from /authorize callback for user profile data",
                "path": "/sso/token",
                "method": "POST",
                "authentication": "client_secret",
                "public": false,
                "backend_only": true,
                "parameters": {
                    "code": {
                        "location": "body",
                        "type": "string",
                        "required": true,
                        "description": "Authorization code from /authorize callback",
                        "example": "abc123xyz789def456",
                        "properties": {
                            "expiration": "10 minutes",
                            "usage": "single-use only",
                            "scope": "tied to specific client_id"
                        }
                    },
                    "client_id": {
                        "location": "body",
                        "type": "string",
                        "required": true,
                        "description": "Your registered application's client ID",
                        "example": "my-app-client-id"
                    },
                    "client_secret": {
                        "location": "body",
                        "type": "string",
                        "required": true,
                        "description": "Your registered application's client secret (KEEP CONFIDENTIAL)",
                        "example": "super-secret-key-xyz789",
                        "security": "NEVER expose in browser or public code"
                    }
                },
                "request_format": "application/x-www-form-urlencoded",
                "response": {
                    "success": {
                        "status_code": 200,
                        "content_type": "application/json",
                        "structure": {
                            "status": "success",
                            "data": {
                                "user_id": "string - unique user identifier",
                                "name": "string - full name",
                                "nip_9": "string - 9-digit employee ID",
                                "nip_18": "string - 18-digit national ID",
                                "email": "string - official email",
                                "gmail": "string - personal gmail (if available)",
                                "roles": "array - assigned role names"
                            }
                        },
                        "example": {
                            "status": "success",
                            "data": {
                                "user_id": "123456789",
                                "name": "John Doe",
                                "nip_9": "123456789",
                                "nip_18": "199001011234567890",
                                "email": "john@example.com",
                                "gmail": "john.doe@gmail.com",
                                "roles": [
                                    "admin",
                                    "user"
                                ]
                            }
                        }
                    }
                },
                "errors": [
                    {
                        "error_code": "INVALID_REQUEST",
                        "http_status": 400,
                        "description": "Missing required parameters",
                        "example": {
                            "status": "error",
                            "message": "Parameter tidak lengkap atau tidak valid",
                            "errors": {
                                "code": [
                                    "The code field is required."
                                ]
                            },
                            "error_code": "INVALID_REQUEST"
                        }
                    },
                    {
                        "error_code": "INVALID_CLIENT",
                        "http_status": 401,
                        "description": "Client ID not found or client app is inactive",
                        "example": {
                            "status": "error",
                            "message": "Client ID tidak valid atau aplikasi tidak aktif",
                            "error_code": "INVALID_CLIENT"
                        }
                    },
                    {
                        "error_code": "INVALID_CLIENT_SECRET",
                        "http_status": 401,
                        "description": "Client secret does not match registered value",
                        "example": {
                            "status": "error",
                            "message": "Client Secret tidak valid",
                            "error_code": "INVALID_CLIENT_SECRET"
                        }
                    },
                    {
                        "error_code": "INVALID_GRANT",
                        "http_status": 400,
                        "description": "Authorization code invalid, expired, or already used",
                        "example": {
                            "status": "error",
                            "message": "Authorization code tidak valid atau expired",
                            "error_code": "INVALID_GRANT"
                        }
                    },
                    {
                        "error_code": "INTERNAL_SERVER_ERROR",
                        "http_status": 500,
                        "description": "Unexpected server error occurred",
                        "example": {
                            "status": "error",
                            "message": "Terjadi kesalahan internal server",
                            "error_code": "INTERNAL_SERVER_ERROR"
                        }
                    }
                ],
                "curl_example": "curl -X POST 'https://sso.bps9702.com/sso/token' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  -H 'Accept: application/json' \\\n  -d 'code=abc123xyz789&client_id=my-app-id&client_secret=my-secret'",
                "implementation_notes": [
                    "This step MUST be performed on your backend server",
                    "Authorization codes are single-use - immediately invalidated after use",
                    "Codes expire 10 minutes after creation",
                    "User must be active (is_active = true) or authentication fails",
                    "Returns user data directly - no separate access token needed",
                    "Each code is bound to a specific client_id",
                    "All token operations are logged for security auditing"
                ],
                "rate_limit": "None (but check authorization code validity)",
                "related_endpoints": [
                    "authorize",
                    "check"
                ],
                "full_url": "https://sso.bps9702.com/sso/sso/token"
            },
            {
                "endpoint_id": "check",
                "name": "Alternative Token Validation",
                "description": "Alternative endpoint similar to /token but with different parameter handling",
                "path": "/sso/check",
                "method": "POST",
                "authentication": "code only (client_secret optional)",
                "public": false,
                "backend_only": true,
                "note": "Less common endpoint - use /token for standard OAuth flow",
                "parameters": {
                    "code": {
                        "location": "body",
                        "type": "string",
                        "required": true,
                        "description": "Authorization code to validate",
                        "example": "abc123xyz789def456"
                    }
                },
                "response": {
                    "success": {
                        "status_code": 200,
                        "description": "Same format as /token endpoint"
                    }
                },
                "curl_example": "curl -X POST 'https://sso.bps9702.com/sso/check' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  -H 'Accept: application/json' \\\n  -d 'code=abc123xyz789def456'",
                "implementation_notes": [
                    "Alternative to /token endpoint",
                    "Minimal parameters required",
                    "Returns same user data as /token",
                    "Less commonly used - prefer /token for standard OAuth"
                ],
                "related_endpoints": [
                    "authorize",
                    "token"
                ],
                "full_url": "https://sso.bps9702.com/sso/sso/check"
            }
        ],
        "data_api": [
            {
                "endpoint_id": "employees",
                "name": "Get All Active Employees",
                "description": "Retrieve data for all active employees in the system",
                "path": "/api/employees",
                "method": "POST",
                "authentication": "client_secret",
                "public": false,
                "note": "Returns only users with is_active = true",
                "parameters": {
                    "client_secret": {
                        "location": "body",
                        "type": "string",
                        "required": true,
                        "description": "Client secret from a registered active application",
                        "example": "your-app-secret-key",
                        "security": "NEVER expose in browser code"
                    }
                },
                "request_format": "application/x-www-form-urlencoded",
                "response": {
                    "success": {
                        "status_code": 200,
                        "content_type": "application/json",
                        "structure": {
                            "status": "success",
                            "message": "string - success message",
                            "data": "array - employee objects",
                            "total": "integer - number of employees",
                            "requested_by": "string - client app name"
                        },
                        "example": {
                            "status": "success",
                            "message": "Data pegawai berhasil diambil",
                            "data": [
                                {
                                    "nip_9": "123456789",
                                    "nip_18": "199001011234567890",
                                    "name": "John Doe",
                                    "email": "john@example.com",
                                    "gmail": "john.doe@gmail.com",
                                    "roles": [
                                        "admin",
                                        "user"
                                    ]
                                },
                                {
                                    "nip_9": "987654321",
                                    "nip_18": "199203151112223334",
                                    "name": "Jane Smith",
                                    "email": "jane@example.com",
                                    "gmail": "jane.smith@gmail.com",
                                    "roles": [
                                        "user"
                                    ]
                                }
                            ],
                            "total": 2,
                            "requested_by": "My Application"
                        }
                    }
                },
                "errors": [
                    {
                        "error_code": "MISSING_CLIENT_SECRET",
                        "http_status": 400,
                        "description": "client_secret parameter is required",
                        "example": {
                            "status": "error",
                            "message": "Client secret diperlukan",
                            "errors": {
                                "client_secret": [
                                    "The client secret field is required."
                                ]
                            },
                            "error_code": "MISSING_CLIENT_SECRET"
                        }
                    },
                    {
                        "error_code": "INVALID_CLIENT_SECRET",
                        "http_status": 401,
                        "description": "Client secret is invalid or application is inactive",
                        "example": {
                            "status": "error",
                            "message": "Client secret tidak valid atau aplikasi tidak aktif",
                            "error_code": "INVALID_CLIENT_SECRET"
                        }
                    },
                    {
                        "error_code": "INTERNAL_SERVER_ERROR",
                        "http_status": 500,
                        "description": "Unexpected server error occurred",
                        "example": {
                            "status": "error",
                            "message": "Terjadi kesalahan internal server",
                            "error_code": "INTERNAL_SERVER_ERROR"
                        }
                    }
                ],
                "curl_example": "curl -X POST 'https://sso.bps9702.com/api/employees' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  -H 'Accept: application/json' \\\n  -d 'client_secret=your-app-secret'",
                "implementation_notes": [
                    "Returns only active users (is_active = true)",
                    "Requires valid client_secret from registered application",
                    "Client application must be active (is_active = true)",
                    "Total field shows count of returned employees",
                    "requested_by field shows which app made the request",
                    "No pagination - returns all employees"
                ],
                "rate_limit": "60 requests per minute (api middleware)",
                "related_endpoints": [
                    "employees/by-role",
                    "roles",
                    "role-names"
                ],
                "full_url": "https://sso.bps9702.com/api/api/employees"
            },
            {
                "endpoint_id": "employees/by-role",
                "name": "Get Employees by Role",
                "description": "Retrieve employees that have a specific role assigned",
                "path": "/api/employees/by-role",
                "method": "POST",
                "authentication": "client_secret",
                "public": false,
                "note": "Returns only users with is_active = true. Role names are case-insensitive.",
                "parameters": {
                    "client_secret": {
                        "location": "body",
                        "type": "string",
                        "required": true,
                        "description": "Client secret from a registered active application",
                        "example": "your-app-secret-key",
                        "security": "NEVER expose in browser code"
                    },
                    "role": {
                        "location": "body",
                        "type": "string",
                        "required": true,
                        "description": "Role name to filter by (case-insensitive)",
                        "example": "admin",
                        "note": "Can use lowercase or uppercase"
                    }
                },
                "request_format": "application/x-www-form-urlencoded",
                "response": {
                    "success": {
                        "status_code": 200,
                        "content_type": "application/json",
                        "structure": {
                            "status": "success",
                            "message": "string - success message",
                            "data": "array - employee objects with the specified role",
                            "role_info": "object - information about the role",
                            "total": "integer - number of employees with this role",
                            "requested_by": "string - client app name"
                        },
                        "example": {
                            "status": "success",
                            "message": "Data pegawai dengan role 'admin' berhasil diambil",
                            "data": [
                                {
                                    "nip_9": "123456789",
                                    "nip_18": "199001011234567890",
                                    "name": "John Doe",
                                    "email": "john@example.com",
                                    "gmail": "john.doe@gmail.com",
                                    "roles": [
                                        "admin",
                                        "user"
                                    ]
                                },
                                {
                                    "nip_9": "111222333",
                                    "nip_18": "199203151112223334",
                                    "name": "Admin User",
                                    "email": "admin@example.com",
                                    "gmail": "admin.user@gmail.com",
                                    "roles": [
                                        "admin"
                                    ]
                                }
                            ],
                            "role_info": {
                                "name": "admin",
                                "description": "Administrator sistem"
                            },
                            "total": 2,
                            "requested_by": "My Application"
                        }
                    }
                },
                "errors": [
                    {
                        "error_code": "MISSING_CLIENT_SECRET",
                        "http_status": 400,
                        "description": "client_secret parameter is required",
                        "example": {
                            "status": "error",
                            "message": "Client secret diperlukan",
                            "error_code": "MISSING_CLIENT_SECRET"
                        }
                    },
                    {
                        "error_code": "INVALID_CLIENT_SECRET",
                        "http_status": 401,
                        "description": "Client secret is invalid or application is inactive",
                        "example": {
                            "status": "error",
                            "message": "Client secret tidak valid atau aplikasi tidak aktif",
                            "error_code": "INVALID_CLIENT_SECRET"
                        }
                    },
                    {
                        "error_code": "INVALID_REQUEST",
                        "http_status": 400,
                        "description": "Role parameter is missing",
                        "example": {
                            "status": "error",
                            "message": "Parameter tidak valid",
                            "errors": {
                                "role": [
                                    "The role field is required."
                                ]
                            },
                            "error_code": "INVALID_REQUEST"
                        }
                    },
                    {
                        "error_code": "ROLE_NOT_FOUND",
                        "http_status": 404,
                        "description": "The specified role does not exist",
                        "example": {
                            "status": "error",
                            "message": "Role tidak ditemukan",
                            "error_code": "ROLE_NOT_FOUND"
                        }
                    },
                    {
                        "error_code": "INTERNAL_SERVER_ERROR",
                        "http_status": 500,
                        "description": "Unexpected server error occurred",
                        "example": {
                            "status": "error",
                            "message": "Terjadi kesalahan internal server",
                            "error_code": "INTERNAL_SERVER_ERROR"
                        }
                    }
                ],
                "curl_examples": [
                    "curl -X POST 'https://sso.bps9702.com/api/employees/by-role' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  -H 'Accept: application/json' \\\n  -d 'client_secret=your-app-secret&role=admin'",
                    "curl -X POST 'https://sso.bps9702.com/api/employees/by-role' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  -H 'Accept: application/json' \\\n  -d 'client_secret=your-app-secret&role=user'"
                ],
                "implementation_notes": [
                    "Returns only active users (is_active = true)",
                    "Role names are case-insensitive (admin, Admin, ADMIN all work)",
                    "Role must exist in the system, or ROLE_NOT_FOUND error returned",
                    "Employees can have multiple roles; this returns those with the specified role",
                    "Each employee's roles array shows all roles they have",
                    "No pagination - returns all employees with the role"
                ],
                "rate_limit": "60 requests per minute (api middleware)",
                "related_endpoints": [
                    "employees",
                    "roles",
                    "role-names"
                ],
                "full_url": "https://sso.bps9702.com/api/api/employees/by-role"
            },
            {
                "endpoint_id": "roles",
                "name": "Get All Roles",
                "description": "Retrieve information about all roles in the system with active user count",
                "path": "/api/roles",
                "method": "GET",
                "authentication": "none",
                "public": true,
                "note": "Public endpoint - no authentication required",
                "parameters": [],
                "response": {
                    "success": {
                        "status_code": 200,
                        "content_type": "application/json",
                        "structure": {
                            "status": "success",
                            "message": "string - success message",
                            "data": "array - role objects",
                            "total": "integer - number of roles"
                        },
                        "example": {
                            "status": "success",
                            "message": "Data role berhasil diambil",
                            "data": [
                                {
                                    "name": "admin",
                                    "description": "Administrator sistem",
                                    "user_count": 5
                                },
                                {
                                    "name": "user",
                                    "description": "User biasa",
                                    "user_count": 45
                                },
                                {
                                    "name": "umum",
                                    "description": "User umum",
                                    "user_count": 20
                                }
                            ],
                            "total": 3
                        }
                    }
                },
                "curl_example": "curl -X GET 'https://sso.bps9702.com/api/roles' \\\n  -H 'Accept: application/json'",
                "implementation_notes": [
                    "Public endpoint - anyone can access without authentication",
                    "user_count includes only active users (is_active = true)",
                    "Useful for populating role dropdowns or understanding role structure",
                    "Role descriptions are in Indonesian",
                    "No pagination - returns all roles"
                ],
                "rate_limit": "60 requests per minute (api middleware)",
                "related_endpoints": [
                    "role-names",
                    "employees/by-role",
                    "employees"
                ],
                "full_url": "https://sso.bps9702.com/api/api/roles"
            },
            {
                "endpoint_id": "role-names",
                "name": "Get Role Names Only",
                "description": "Retrieve a simple list of all role names (no descriptions or user counts)",
                "path": "/api/role-names",
                "method": "GET",
                "authentication": "none",
                "public": true,
                "note": "Public endpoint - no authentication required. Lightweight alternative to /roles.",
                "parameters": [],
                "response": {
                    "success": {
                        "status_code": 200,
                        "content_type": "application/json",
                        "structure": {
                            "status": "success",
                            "message": "string - success message",
                            "data": "array of strings - role names only",
                            "total": "integer - number of roles"
                        },
                        "example": {
                            "status": "success",
                            "message": "Daftar nama role berhasil diambil",
                            "data": [
                                "admin",
                                "user",
                                "umum"
                            ],
                            "total": 3
                        }
                    }
                },
                "curl_example": "curl -X GET 'https://sso.bps9702.com/api/role-names' \\\n  -H 'Accept: application/json'",
                "implementation_notes": [
                    "Public endpoint - lightweight, perfect for caching",
                    "Returns only role names as a simple array",
                    "Ideal for populating dropdowns/select lists in frontend",
                    "Much smaller response than /roles endpoint",
                    "No pagination - returns all role names"
                ],
                "rate_limit": "60 requests per minute (api middleware)",
                "related_endpoints": [
                    "roles",
                    "employees/by-role"
                ],
                "full_url": "https://sso.bps9702.com/api/api/role-names"
            }
        ]
    },
    "data_models": {
        "user": {
            "description": "Employee/User data model returned from OAuth and Data API endpoints",
            "fields": {
                "user_id": {
                    "type": "string",
                    "description": "Unique user identifier in the system",
                    "example": "123456789"
                },
                "name": {
                    "type": "string",
                    "description": "Full name of the employee",
                    "example": "John Doe"
                },
                "nip_9": {
                    "type": "string",
                    "description": "9-digit employee identification number",
                    "example": "123456789",
                    "note": "Indonesian: Nomor Induk Pegawai (9 digits)"
                },
                "nip_18": {
                    "type": "string",
                    "description": "18-digit national employee identification number",
                    "example": "199001011234567890",
                    "format": "YYMMDDNNNNNNNNNNN",
                    "note": "Longer identifier including birth date"
                },
                "email": {
                    "type": "string",
                    "description": "Official BPS email address",
                    "example": "john@bps9702.com",
                    "format": "email"
                },
                "gmail": {
                    "type": "string|null",
                    "description": "Personal Gmail address (if available)",
                    "example": "john.doe@gmail.com",
                    "note": "Optional field - may be null"
                },
                "roles": {
                    "type": "array of strings",
                    "description": "List of role names assigned to the user",
                    "example": [
                        "admin",
                        "user"
                    ],
                    "note": "User can have zero or multiple roles"
                }
            }
        },
        "role": {
            "description": "Role/Permission group model",
            "fields": {
                "name": {
                    "type": "string",
                    "description": "Unique role identifier/name",
                    "example": "admin",
                    "note": "Used for role-based access control (RBAC)"
                },
                "description": {
                    "type": "string",
                    "description": "Human-readable description of the role",
                    "example": "Administrator sistem",
                    "language": "Indonesian"
                },
                "user_count": {
                    "type": "integer",
                    "description": "Number of active users with this role",
                    "example": 5,
                    "note": "Only counts users where is_active = true"
                }
            }
        },
        "authorization_code": {
            "description": "Authorization code returned in OAuth /authorize callback",
            "properties": {
                "length": "40 characters - alphanumeric",
                "expiration": "10 minutes after creation",
                "usage": "single-use only - invalid after first successful exchange",
                "binding": "tied to specific client_id",
                "created_at": "timestamp when code was issued"
            },
            "example": {
                "code_value": "abc123xyz789def456ghi789jkl123mno456pqr",
                "valid_for": "10 minutes",
                "created_at": "2024-02-17T14:30:00Z",
                "expires_at": "2024-02-17T14:40:00Z"
            }
        }
    },
    "error_codes": {
        "MISSING_CLIENT_ID": {
            "http_status": 400,
            "description": "Required client_id parameter is missing",
            "affected_endpoints": [
                "/sso/authorize"
            ],
            "solution": "Include client_id parameter in the request"
        },
        "INVALID_CLIENT": {
            "http_status": 400,
            "description": "Client ID is not registered or the client application is inactive",
            "affected_endpoints": [
                "/sso/authorize",
                "/sso/token",
                "/sso/check"
            ],
            "solution": "Register your application in the SSO admin panel and ensure it is active"
        },
        "MISSING_CLIENT_SECRET": {
            "http_status": 400,
            "description": "Required client_secret parameter is missing",
            "affected_endpoints": [
                "/sso/token",
                "/api/employees",
                "/api/employees/by-role"
            ],
            "solution": "Include client_secret in the request body"
        },
        "INVALID_CLIENT_SECRET": {
            "http_status": 401,
            "description": "Client secret does not match or client application is inactive",
            "affected_endpoints": [
                "/sso/token",
                "/api/employees",
                "/api/employees/by-role"
            ],
            "solution": "Verify your client_secret is correct and the application is active"
        },
        "INVALID_REQUEST": {
            "http_status": 400,
            "description": "Request parameters are invalid or incomplete",
            "affected_endpoints": [
                "All endpoints with parameters"
            ],
            "solution": "Check request parameters match the documentation and are properly formatted"
        },
        "INVALID_GRANT": {
            "http_status": 400,
            "description": "Authorization code is invalid, expired (>10 minutes), or already used",
            "affected_endpoints": [
                "/sso/token",
                "/sso/check"
            ],
            "solution": "Start a new OAuth flow to get a fresh authorization code"
        },
        "ROLE_NOT_FOUND": {
            "http_status": 404,
            "description": "Specified role does not exist in the system",
            "affected_endpoints": [
                "/api/employees/by-role"
            ],
            "solution": "Verify the role name is correct - get valid roles from /api/roles or /api/role-names"
        },
        "METHOD_NOT_ALLOWED": {
            "http_status": 405,
            "description": "HTTP method (GET, POST, etc.) is not allowed for this endpoint",
            "affected_endpoints": [
                "All endpoints"
            ],
            "solution": "Use the correct HTTP method as specified in the endpoint documentation"
        },
        "INTERNAL_SERVER_ERROR": {
            "http_status": 500,
            "description": "Unexpected server error occurred",
            "affected_endpoints": [
                "Any endpoint"
            ],
            "solution": "Try again later. If problem persists, contact the IT team."
        }
    },
    "important_notes": [
        "All Data Endpoints return only active users (is_active = true)",
        "Protected Endpoints require valid client_secret from a registered and active application",
        "Public Endpoints (/roles, /role-names, /documentation) can be accessed without authentication",
        "OAuth endpoints use /sso/ prefix, Data API endpoints use /api/ prefix",
        "All responses use consistent JSON format with status, message, and data fields",
        "Authorization code parameters are case-sensitive",
        "Role names are case-insensitive (admin, Admin, ADMIN are treated the same)",
        "Authorization codes are single-use and expire after 10 minutes",
        "Client secret must be kept confidential and never exposed in browser code",
        "State parameter in OAuth flow provides CSRF protection - always validate it",
        "Users are forced to re-authenticate even if already logged in (forces logout first)",
        "API responses use JSON format with UTF-8 encoding",
        "Timestamps in responses are in ISO 8601 format",
        "All requests are logged for security auditing purposes"
    ],
    "usage_examples": {
        "oauth_complete_flow_php": {
            "language": "PHP",
            "description": "Complete OAuth 2.0 flow implementation",
            "code": "<?php\n// Step 1: Redirect user to SSO login\n$clientId = \"my-app-client-id\";\n$state = bin2hex(random_bytes(16));\n$_SESSION[\"oauth_state\"] = $state;\n\n$authorizeUrl = \"https://sso.bps9702.com/sso/authorize?\" . http_build_query([\n    \"client_id\" => $clientId,\n    \"state\" => $state,\n]);\n\nheader(\"Location: \" . $authorizeUrl);\n\n// Step 2: Handle callback (on your app's callback URL)\nif (!isset($_GET[\"code\"]) || !isset($_GET[\"state\"])) {\n    die(\"Missing authorization code or state\");\n}\n\nif ($_GET[\"state\"] !== $_SESSION[\"oauth_state\"]) {\n    die(\"State mismatch - possible CSRF attack\");\n}\n\n$code = $_GET[\"code\"];\n\n// Step 3: Exchange code for user data (on backend)\n$curl = curl_init();\ncurl_setopt_array($curl, [\n    CURLOPT_URL => \"https://sso.bps9702.com/sso/token\",\n    CURLOPT_POST => true,\n    CURLOPT_POSTFIELDS => http_build_query([\n        \"code\" => $code,\n        \"client_id\" => $clientId,\n        \"client_secret\" => \"super-secret-key\",\n    ]),\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_HTTPHEADER => [\"Accept: application/json\"],\n]);\n\n$response = curl_exec($curl);\n$userData = json_decode($response, true);\n\nif ($userData[\"status\"] === \"success\") {\n    $_SESSION[\"user\"] = $userData[\"data\"];\n    header(\"Location: /dashboard\");\n}\n?>"
        },
        "fetch_employees_javascript": {
            "language": "JavaScript",
            "description": "Fetch all employees using client_secret",
            "code": "// NOTE: This is for server-side code only!\n// NEVER put client_secret in browser-side JavaScript\n\n// On your backend:\nasync function getAllEmployees(clientSecret) {\n    try {\n        const response = await fetch(\"https://sso.bps9702.com/api/employees\", {\n            method: \"POST\",\n            headers: {\n                \"Content-Type\": \"application/x-www-form-urlencoded\",\n                \"Accept\": \"application/json\",\n            },\n            body: new URLSearchParams({\n                client_secret: clientSecret,\n            }),\n        });\n\n        const data = await response.json();\n\n        if (data.status === \"success\") {\n            console.log(`Retrieved ${data.total} employees`);\n            console.log(\"Employees:\", data.data);\n            return data.data;\n        } else {\n            console.error(\"Error:\", data.message);\n            throw new Error(data.error_code);\n        }\n    } catch (error) {\n        console.error(\"Request failed:\", error);\n        throw error;\n    }\n}\n\n// Usage\ngetAllEmployees(\"your-secret\").then(employees => {\n    // Process employees\n});\n"
        },
        "fetch_roles_javascript": {
            "language": "JavaScript",
            "description": "Fetch all roles (public endpoint)",
            "code": "// This CAN be called from browser - no secret needed\nasync function getAllRoles() {\n    try {\n        const response = await fetch(\"https://sso.bps9702.com/api/roles\", {\n            method: \"GET\",\n            headers: {\n                \"Accept\": \"application/json\",\n            },\n        });\n\n        const data = await response.json();\n\n        if (data.status === \"success\") {\n            console.log(`Retrieved ${data.total} roles`);\n            data.data.forEach(role => {\n                console.log(`${role.name}: ${role.description} (${role.user_count} users)`);\n            });\n            return data.data;\n        } else {\n            throw new Error(data.message);\n        }\n    } catch (error) {\n        console.error(\"Failed to fetch roles:\", error);\n        throw error;\n    }\n}\n\n// Usage\ngetAllRoles().then(roles => {\n    // Populate role dropdown\n    const select = document.getElementById(\"roleSelect\");\n    roles.forEach(role => {\n        const option = document.createElement(\"option\");\n        option.value = role.name;\n        option.textContent = role.description;\n        select.appendChild(option);\n    });\n});\n"
        },
        "get_employees_by_role_curl": {
            "language": "cURL",
            "description": "Get all employees with admin role",
            "code": "#!/bin/bash\n\n# Get all admin users\ncurl -X POST \"https://sso.bps9702.com/api/employees/by-role\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -H \"Accept: application/json\" \\\n  -d \"client_secret=your-app-secret&role=admin\" | jq .\n\n# Or for user role\ncurl -X POST \"https://sso.bps9702.com/api/employees/by-role\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -H \"Accept: application/json\" \\\n  -d \"client_secret=your-app-secret&role=user\" | jq .\n"
        }
    },
    "api_info": {
        "base_urls": {
            "oauth": "https://sso.bps9702.com/sso",
            "data_api": "https://sso.bps9702.com/api"
        },
        "environment": "production",
        "last_updated": "2026-05-31T16:21:29+00:00"
    },
    "meta": {
        "documentation_endpoint": "https://sso.bps9702.com/api/documentation",
        "html_documentation": "https://sso.bps9702.com/docs",
        "api_version": "1.0.0",
        "generation_timestamp": "2026-05-31T16:21:29+00:00",
        "generator": "SSO Documentation Controller v1.0"
    }
}