{"id":28565659,"url":"https://github.com/jmaister/taronja-gateway","last_synced_at":"2026-03-06T01:11:40.197Z","repository":{"id":290262529,"uuid":"973851976","full_name":"jmaister/taronja-gateway","owner":"jmaister","description":"Application and API Gateway","archived":false,"fork":false,"pushed_at":"2026-02-26T22:47:58.000Z","size":1200,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-27T01:12:22.926Z","etag":null,"topics":["authentication","gateway-api","gateway-rest","gateway-services","http","jwt","oauth","oauth2","rest"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jmaister.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-04-27T21:39:29.000Z","updated_at":"2026-02-26T22:48:02.000Z","dependencies_parsed_at":"2025-04-27T22:26:58.460Z","dependency_job_id":"31abf27d-f7e1-4822-badd-a2fcac40e06e","html_url":"https://github.com/jmaister/taronja-gateway","commit_stats":null,"previous_names":["jmaister/taronja-gateway"],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/jmaister/taronja-gateway","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmaister%2Ftaronja-gateway","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmaister%2Ftaronja-gateway/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmaister%2Ftaronja-gateway/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmaister%2Ftaronja-gateway/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jmaister","download_url":"https://codeload.github.com/jmaister/taronja-gateway/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmaister%2Ftaronja-gateway/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30157087,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T22:39:40.138Z","status":"ssl_error","status_checked_at":"2026-03-05T22:39:24.771Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","gateway-api","gateway-rest","gateway-services","http","jwt","oauth","oauth2","rest"],"created_at":"2025-06-10T14:30:53.438Z","updated_at":"2026-03-06T01:11:40.187Z","avatar_url":"https://github.com/jmaister.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Taronja Gateway\n\nTaronja Gateway is an API and application gateway.\n\nIt serves as an entry point for your API server and your frontend application, handling routing, authentication, sessions, and many more features, leaving your application code clean and focused on business logic.\n\n## Table of Contents\n\n- [Features](#features)\n- [Installation](#installation)\n- [Commands](#commands)\n- [Configuration](#configuration)\n- [Building and Releasing](#building-and-releasing)\n- [Authentication on the APIs](#authentication-on-the-apis)\n- [Getting the Current User from the Frontend](#getting-the-current-user-from-the-frontend)\n- [Login and Logout Links from a Web Page](#login-and-logout-links-from-a-web-page)\n\n# Features\n\nFeatures table, shows what is implemented and what is planned.\n\n| Feature                       | Status   |\n|-------------------------------|----------|\n| API Gateway                   | ✅       |\n| Application Gateway           | ✅       |\n| Management Dashboard          | ✅       |\n| Logging                       | ✅       |\n| Analytics and Traffic metrics | ✅       |\n| - User Geo-location           | ✅       |\n| - User fingerprint (JA4)      | ✅       |\n| Sessions (Persistent)         | ✅       |\n| User management               | ✅       |\n| Authentication                | ✅       |\n| Authentication: Basic         | ✅       |\n| Authentication: OAuth2        | ✅       |\n| - OAuth2: GitHub              | ✅       |\n| - OAuth2: Google              | ✅       |\n| Authentication: Token         | ✅       |\n| Authentication: JWT           | 🚧       |\n| Authorization using RBAC      | 🚧       |\n| HTTP Cache Control            | ✅       |\n| Feature Flags                 | 🚧       |\n| Rate Limiting                 | 🚧       |\n| Circuit breaker               | 🚧       |\n| Caching                       | 🚧       |\n| Load Balancing                | 🚧       |\n| robots.txt                    | 🚧       |\n| more...                       | 🚧       |\n\n# Installation\n\n### Quick Install (All Platforms)\n\n```bash\ncurl -fsSL https://github.com/jmaister/taronja-gateway/raw/main/scripts/install.sh | bash\n```\n\nThis script detects your OS and architecture, downloads the latest release, and installs it to your system path.\n\n### Windows Installation\n\n```bat\npowershell -Command \"Invoke-WebRequest -Uri 'https://github.com/jmaister/taronja-gateway/raw/main/scripts/install.bat' -OutFile 'install.bat'\" \u0026\u0026 install.bat\n```\n\nThe Windows installer places the binary in `%USERPROFILE%\\bin`. Add this directory to your PATH to use `tg` from anywhere.\n\n# Commands\n\nThe Taronja Gateway CLI provides the following commands:\n\n*   **Run the Gateway:**\n    ```bash\n    ./tg run --config ./sample/config.yaml\n    ```\n    This command starts the Taronja API Gateway using the configuration file specified by the `--config` flag.\n\n*   **Add a new user:**\n    ```bash\n    ./tg adduser \u003cusername\u003e \u003cemail\u003e \u003cpassword\u003e\n    ```\n    This command creates a new user in the database with the provided username, email, and password.\n\n*   **Show the current version:**\n    ```bash\n    ./tg version\n    ```\n\n# Configuration\n\nTaronja Gateway uses a YAML configuration file to define server settings, routes, authentication providers, and other features. The configuration file can reference environment variables using the `${VARIABLE_NAME}` syntax.\n\n## Basic Structure\n\n```yaml\nname: Example Gateway Configuration\n\nserver:\n  host: 0.0.0.0 # Bind to all interfaces, 127.0.0.1 for localhost only\n  port: 8080\n  url: http://localhost:8080\n\nmanagement:\n  prefix: _\n  logging: true\n  analytics: true\n  session:\n    secondsDuration: 86400  # Session duration in seconds (24 hours)\n  admin:\n    enabled: true\n    username: admin\n    password: admin123  # Automatically hashed for security\n\nroutes:\n  - name: API Route\n    from: /api/v1/*\n    removeFromPath: \"/api/v1/\"\n    to: https://api.example.com\n    authentication:\n      enabled: false\n    options:\n      cacheControlSeconds: 3600\n\nauthenticationProviders:\n  basic:\n    enabled: true\n  google:\n    clientId: ${GOOGLE_CLIENT_ID}\n    clientSecret: ${GOOGLE_CLIENT_SECRET}\n  github:\n    clientId: ${GITHUB_CLIENT_ID}\n    clientSecret: ${GITHUB_CLIENT_SECRET}\n\nbranding:\n  logoUrl: /static/logo.png\n\ngeolocation:\n  iplocateApiKey: ${IPLOCATE_IO_API_KEY}\n\nnotification:\n  email:\n    enabled: true\n    smtp:\n      host: smtp.example.com\n      port: 587\n      username: ${SMTP_USERNAME}\n      password: ${SMTP_PASSWORD}\n      from: ${SMTP_FROM}\n      fromName: ${SMTP_FROM_NAME}\n```\n\n## Configuration Sections\n\n### Server\n\nDefines the gateway server settings.\n\n- `host`: The host address to bind to (default: 127.0.0.1)\n- `port`: The port number to listen on (default: 8080)\n- `url`: The full URL where the gateway is accessible\n\n### Management\n\nControls the management dashboard and gateway features.\n\n- `prefix`: URL prefix for management endpoints (default: `_`)\n- `logging`: Enable/disable request logging\n- `analytics`: Enable/disable traffic analytics and metrics\n- `session.secondsDuration`: Session timeout in seconds (e.g., 86400 = 24 hours)\n- `admin.enabled`: Enable the admin dashboard\n- `admin.username`: Username for dashboard access\n- `admin.password`: Password for dashboard access (automatically hashed)\n\n### Routes\n\nDefine routing rules for incoming requests. Each route can:\n\n- Proxy requests to backend services\n- Serve static files\n- Require authentication\n- Control caching behavior\n\n**Route Properties:**\n\n- `name`: Human-readable route identifier\n- `from`: URL path pattern to match (supports wildcards with `*`)\n- `to`: Backend URL to proxy requests to\n- `toFile`: Serve a single static file\n- `toFolder`: Serve files from a directory\n- `static`: Set to `true` for static file serving\n- `removeFromPath`: Remove prefix before forwarding to backend\n- `authentication.enabled`: Require authentication for this route\n- `options.cacheControlSeconds`: Cache duration in seconds (0 = no-cache)\n\n**Example Routes:**\n\n```yaml\nroutes:\n  # Serve a single file\n  - name: Favicon\n    from: /favicon.ico\n    toFile: ./sample/webfiles/favicon.ico\n    static: true\n\n  # Public API - no authentication required\n  - name: Public API v1\n    from: /api/v1/*\n    removeFromPath: \"/api/v1/\"\n    to: https://jsonplaceholder.typicode.com\n    authentication:\n      enabled: false\n    options:\n      cacheControlSeconds: 300  # Cache for 5 minutes\n\n  # Authenticated API route\n  - name: Private API v2\n    from: /api/v2/*\n    removeFromPath: \"/api/v2/\"\n    to: https://api.example.com\n    authentication:\n      enabled: true\n    options:\n      cacheControlSeconds: 0  # No cache\n\n  # Static files folder - public\n  - name: CSS and JavaScript\n    from: /assets/*\n    toFolder: ./static/assets\n    static: true\n    options:\n      cacheControlSeconds: 604800  # Cache for 1 week\n\n  # Another static folder - requires authentication\n  - name: Protected Documents\n    from: /documents/*\n    toFolder: ./static/private-docs\n    static: true\n    authentication:\n      enabled: true\n    options:\n      cacheControlSeconds: 3600  # Cache for 1 hour\n\n  # Frontend application - no authentication\n  - name: Public Frontend\n    from: /\n    toFolder: ./static/public\n    static: true\n    options:\n      cacheControlSeconds: 86400  # Cache for 1 day\n\n  # Admin panel - requires authentication\n  - name: Admin Dashboard\n    from: /admin/*\n    toFolder: ./static/admin\n    static: true\n    authentication:\n      enabled: true\n    options:\n      cacheControlSeconds: 0  # No cache for dashboard\n```\n\n### Authentication Providers\n\nConfigure authentication methods for your gateway.\n\n**Basic Authentication:**\n```yaml\nauthenticationProviders:\n  basic:\n    enabled: true\n```\n\n**OAuth2 Providers:**\n```yaml\nauthenticationProviders:\n  google:\n    clientId: ${GOOGLE_CLIENT_ID}\n    clientSecret: ${GOOGLE_CLIENT_SECRET}\n  github:\n    clientId: ${GITHUB_CLIENT_ID}\n    clientSecret: ${GITHUB_CLIENT_SECRET}\n```\n\nTo obtain OAuth2 credentials:\n- **Google**: [Google Cloud Console](https://console.cloud.google.com/)\n- **GitHub**: [GitHub OAuth Apps](https://github.com/settings/developers)\n\n#### Google OAuth2 sample\n\nAuthorized origin: `http://localhost:8080`\nAuthorized redirect URI: `http://localhost:8080/_/auth/google/callback`\n\n#### GitHub OAuth2 sample\n\nAuthorized origin: `http://localhost:8080`\nAuthorized callback URL: `http://localhost:8080/_/auth/github/callback`\n\n### Branding\n\nCustomize the login page and dashboard appearance.\n\n```yaml\nbranding:\n  logoUrl: /static/logo.png\n```\n\n### Geolocation\n\nConfigure IP geolocation services for analytics.\n\n```yaml\ngeolocation:\n  iplocateApiKey: ${IPLOCATE_IO_API_KEY}\n```\n\n- With API key: Uses [iplocate.io](https://www.iplocate.io) for accurate results\n- Without API key: Falls back to [freeipapi.com](https://freeipapi.com)\n\n### Notifications\n\nConfigure email notifications for user actions.\n\n```yaml\nnotification:\n  email:\n    enabled: true\n    smtp:\n      host: smtp.example.com\n      port: 587\n      username: ${SMTP_USERNAME}\n      password: ${SMTP_PASSWORD}\n      from: noreply@example.com\n      fromName: Taronja Gateway\n```\n\n## Environment Variables\n\nUse environment variables to keep sensitive data out of your config file:\n\n```yaml\ngoogle:\n  clientId: ${GOOGLE_CLIENT_ID}\n  clientSecret: ${GOOGLE_CLIENT_SECRET}\n```\n\nSet environment variables before running the gateway:\n\n```bash\nexport GOOGLE_CLIENT_ID=\"your-client-id\"\nexport GOOGLE_CLIENT_SECRET=\"your-client-secret\"\n./tg run --config ./config.yaml\n```\n\n## Example Configuration\n\nSee the complete example configuration in `sample/config.yaml`.\n\n# Building and Releasing\n\n## Development Builds\n\n```bash\n# Build the binary\nmake build\n\n# Run tests\nmake test\n\n# Generate test coverage report\nmake cover\n\n# Run in development mode with automatic restart on file changes\nmake dev\n```\n\n## Release Process\n\nTaronja Gateway uses [GoReleaser](https://goreleaser.com/) for building and publishing releases.\n\n```bash\n# Install GoReleaser\nmake setup-goreleaser\n\n# Check GoReleaser configuration\nmake release-check\n\n# Create a local snapshot release (for testing)\nmake release-local\n\n# Build Docker image locally\nmake release-docker\n```\n\n## GitHub Releases\n\nWhen a new version is ready to be released:\n\n1. Tag the commit with a semantic version:\n   ```bash\n   git tag -a v1.0.0 -m \"Release v1.0.0\"\n   git push origin v1.0.0\n   ```\n\n2. Create a new release on GitHub, pointing to the created tag.\n\n3. The GitHub action will automatically:\n   - Build binaries for multiple platforms\n   - Create Docker images\n   - Generate coverage reports\n   - Publish all artifacts to the GitHub release\n\n## Geolocation Configuration\n\nConfigure IP geolocation services in your `config.yaml`:\n\n```yaml\ngeolocation:\n  iplocateApiKey: ${IPLOCATE_IO_API_KEY}  # Optional: Use iplocate.io\n```\n\n- **With API key**: Uses [iplocate.io](https://www.iplocate.io) (more accurate, requires API key)\n- **Without API key**: Uses [freeipapi.com](https://freeipapi.com) (free, basic accuracy)\n\nGeolocation data is cached for 7 days to optimize performance and reduce API calls.\n\n\n# Authentication on the APIs\n\nWhen a request is proxied to a backend route that has `authentication.enabled: true`, Taronja Gateway injects HTTP headers into the request so the backend service can identify the authenticated user. These headers are only set when a valid session exists.\n\n## Headers Sent to Backend Routes\n\n### Standard Proxy Headers\n\nEvery proxied request (authenticated or not) includes the following standard headers:\n\n| Header              | Type     | Description                                                    |\n|---------------------|----------|----------------------------------------------------------------|\n| `X-Forwarded-Host`  | `string` | The original `Host` header from the client request.            |\n| `X-Forwarded-Proto` | `string` | The protocol used by the client (`http` or `https`).           |\n| `X-Forwarded-For`   | `string` | The client's IP address. Appended to existing values if present. |\n\n### Authentication Headers\n\nThese headers are added only on routes with `authentication.enabled: true` and when the user has a valid session:\n\n| Header        | Type     | Description                                                                 |\n|---------------|----------|-----------------------------------------------------------------------------|\n| `X-User-Id`   | `string` | The unique user ID (CUID) of the authenticated user.                        |\n| `X-User-Data` | `string` | A JSON-serialized object containing the full session data (see structure below). |\n\n## `X-User-Data` JSON Structure\n\nThe `X-User-Data` header contains a JSON-encoded session object with the following fields:\n\n```json\n{\n  \"token\": \"string\",\n  \"userId\": \"string\",\n  \"username\": \"string\",\n  \"email\": \"string\",\n  \"isAuthenticated\": true,\n  \"isAdmin\": false,\n  \"validUntil\": \"2026-02-28T12:00:00Z\",\n  \"provider\": \"string\",\n  \"closedOn\": null,\n  \"lastActivity\": \"2026-02-27T10:30:00Z\",\n  \"sessionName\": \"string\",\n  \"createdFrom\": \"string\",\n  \"ipAddress\": \"string\",\n  \"userAgent\": \"string\",\n  \"referrer\": \"string\",\n  \"browserFamily\": \"string\",\n  \"browserVersion\": \"string\",\n  \"osFamily\": \"string\",\n  \"osVersion\": \"string\",\n  \"deviceFamily\": \"string\",\n  \"deviceBrand\": \"string\",\n  \"deviceModel\": \"string\",\n  \"geoLocation\": \"string\",\n  \"latitude\": 0.0,\n  \"longitude\": 0.0,\n  \"city\": \"string\",\n  \"zipCode\": \"string\",\n  \"country\": \"string\",\n  \"countryCode\": \"string\",\n  \"region\": \"string\",\n  \"continent\": \"string\",\n  \"ja4Fingerprint\": \"string\"\n}\n```\n\n### Field Reference\n\n| Field              | Type      | Description                                                      |\n|--------------------|-----------|------------------------------------------------------------------|\n| `token`            | `string`  | The session token identifier.                                    |\n| `userId`           | `string`  | Unique user ID (CUID format).                                    |\n| `username`         | `string`  | Username of the authenticated user.                              |\n| `email`            | `string`  | Email address of the user.                                       |\n| `isAuthenticated`  | `bool`    | Whether the session is authenticated.                            |\n| `isAdmin`          | `bool`    | Whether the user has admin privileges.                           |\n| `validUntil`       | `string`  | Session expiration timestamp (RFC 3339 / ISO 8601).              |\n| `provider`         | `string`  | Authentication provider used (`basic`, `google`, `github`, etc). |\n| `closedOn`         | `string?` | Timestamp when the session was closed, or `null` if active.      |\n| `lastActivity`     | `string`  | Timestamp of the last user activity in this session.             |\n| `sessionName`      | `string`  | Optional name assigned to the session.                           |\n| `createdFrom`      | `string`  | How the session was created (e.g. `cookie`, `token`).            |\n| `ipAddress`        | `string`  | Client IP address.                                               |\n| `userAgent`        | `string`  | Client's User-Agent string.                                      |\n| `referrer`         | `string`  | HTTP referrer.                                                   |\n| `browserFamily`    | `string`  | Browser name (e.g. `Chrome`, `Firefox`).                         |\n| `browserVersion`   | `string`  | Browser version string.                                          |\n| `osFamily`         | `string`  | Operating system name.                                           |\n| `osVersion`        | `string`  | Operating system version.                                        |\n| `deviceFamily`     | `string`  | Device type (e.g. `desktop`, `mobile`).                          |\n| `deviceBrand`      | `string`  | Device manufacturer.                                             |\n| `deviceModel`      | `string`  | Device model name.                                               |\n| `geoLocation`      | `string`  | General geolocation description.                                 |\n| `latitude`         | `float`   | GPS latitude coordinate.                                         |\n| `longitude`        | `float`   | GPS longitude coordinate.                                        |\n| `city`             | `string`  | City name from geolocation.                                      |\n| `zipCode`          | `string`  | Postal / ZIP code.                                               |\n| `country`          | `string`  | Country name.                                                    |\n| `countryCode`      | `string`  | ISO country code (2-3 characters).                               |\n| `region`           | `string`  | State, province, or region.                                      |\n| `continent`        | `string`  | Continent name.                                                  |\n| `ja4Fingerprint`   | `string`  | JA4H HTTP fingerprint of the client.                             |\n\n## Authentication Methods\n\nBackend routes can receive authenticated requests via two methods:\n\n1. **Session cookie** — The user logs in through the gateway (Basic auth or OAuth2), and a `tg_session_token` cookie is set. The gateway validates the cookie on each request and injects the headers above.\n\n2. **Bearer token** — API clients can authenticate using a token in the `Authorization` header:\n   ```\n   Authorization: Bearer \u003ctoken\u003e\n   ```\n   The gateway validates the token, creates a session-like object, and injects the same `X-User-Id` and `X-User-Data` headers.\n\n## Example: Reading Headers in a Backend Service\n\n**Node.js / Express:**\n```js\napp.get('/api/resource', (req, res) =\u003e {\n    const userId = req.headers['x-user-id'];\n    const userData = JSON.parse(req.headers['x-user-data']);\n  console.log(`User: ${userData.username} (${userId})`);\n  res.json({ message: `Hello, ${userData.username}` });\n});\n```\n\n**Go:**\n```go\nfunc handler(w http.ResponseWriter, r *http.Request) {\n    userId := r.Header.Get(\"X-User-Id\")\n    userDataJson := r.Header.Get(\"X-User-Data\")\n    // Parse userDataJson as needed\n    fmt.Fprintf(w, \"User ID: %s\", userId)\n}\n```\n\n**Python / Flask:**\n```python\n@app.route('/api/resource')\ndef resource():\n    user_id = request.headers.get('X-User-Id')\n    user_data = json.loads(request.headers.get('X-User-Data', '{}'))\n    return jsonify(message=f\"Hello, {user_data.get('Username')}\")\n```\n\n## Getting the Current User from the Frontend\n\nWeb applications served through the gateway can call the `/_/me` endpoint to retrieve information about the currently logged-in user. The endpoint uses the session cookie (`tg_session_token`) that the browser sends automatically.\n\n**Endpoint:** `GET /_/me`\n\n- Returns `200` with user data if the user is authenticated.\n- Returns `401` if no valid session exists.\n\n**Response (200):**\n\n```json\n{\n  \"authenticated\": true,\n  \"username\": \"testuser\",\n  \"email\": \"user@example.com\",\n  \"name\": \"Test User\",\n  \"picture\": \"https://example.com/picture.jpg\",\n  \"givenName\": \"Test\",\n  \"familyName\": \"User\",\n  \"provider\": \"google\",\n  \"isAdmin\": false,\n  \"timestamp\": \"2026-02-27T12:00:00Z\"\n}\n```\n\n| Field           | Type      | Nullable | Description                                              |\n|-----------------|-----------|----------|----------------------------------------------------------|\n| `authenticated` | `bool`    | No       | Always `true` when the response is 200.                  |\n| `username`      | `string`  | No       | Username of the authenticated user.                      |\n| `email`         | `string`  | Yes      | Email address (format: email).                           |\n| `name`          | `string`  | Yes      | Full display name.                                       |\n| `picture`       | `string`  | Yes      | URL to the user's profile picture.                       |\n| `givenName`     | `string`  | Yes      | First name.                                              |\n| `familyName`    | `string`  | Yes      | Last name.                                               |\n| `provider`      | `string`  | No       | Authentication provider (`basic`, `google`, `github`).   |\n| `isAdmin`       | `bool`    | No       | Whether the user has admin privileges.                   |\n| `timestamp`     | `string`  | No       | Server timestamp (RFC 3339 / ISO 8601).                  |\n\n**Example: Fetching the current user from JavaScript:**\n\n```js\nconst response = await fetch('/_/me', { credentials: 'include' });\nif (response.ok) {\n    const user = await response.json();\n    console.log(`Logged in as ${user.username}`);\n} else {\n    console.log('Not authenticated');\n}\n```\n\n## Login and Logout Links from a Web Page\n\nYou can add direct login/logout links in your frontend pages.\n\nBy default, the management prefix is `_`, so authentication URLs are under `/_/`.\n\n### Login Links\n\nUse the login page endpoint:\n\n- `/_/login`\n\nThis page automatically shows all configured login options (Basic, Google, GitHub, etc.).\n\nOptional redirect after login:\n\n- `/_/login?redirect=/dashboard`\n\n### Logout Link\n\n- `/_/logout`\n\nOptional redirect after logout:\n\n- `/_/logout?redirect=/`\n- `/_/logout?redirect=/goodbye`\n\n### HTML Example\n\n```html\n\u003ca href=\"/_/login?redirect=/dashboard\"\u003eLogin\u003c/a\u003e\n\u003ca href=\"/_/logout?redirect=/\"\u003eLogout\u003c/a\u003e\n```\n\n### JavaScript Example\n\n```js\nfunction login() {\n  window.location.href = '/_/login?redirect=/dashboard';\n}\n\nfunction logout() {\n  window.location.href = '/_/logout?redirect=/';\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjmaister%2Ftaronja-gateway","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjmaister%2Ftaronja-gateway","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjmaister%2Ftaronja-gateway/lists"}