From 91cb7bb867a85e4b652a440982d76d765ca0e06c Mon Sep 17 00:00:00 2001 From: Ross Golder Date: Sun, 21 Jun 2026 13:51:58 +0700 Subject: [PATCH 1/3] Add Personal Access Tokens (PAT) feature proposal Comprehensive proposal documenting the completed PAT implementation: - Self-service token management for Harbor users - Time-limited credentials with configurable expiration - Token prefix system (hbr_pat_) for identification - REST API endpoints for CRUD operations and refresh - Harbor UI dashboard for token management - Audit trail integration for compliance - Backward compatibility with legacy CLI tokens - Full test coverage (12/12 E2E tests passing) Implementation PR: https://github.com/goharbor/harbor/pull/23370 Signed-off-by: Ross Golder --- proposals/personal-access-tokens.md | 342 ++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 proposals/personal-access-tokens.md diff --git a/proposals/personal-access-tokens.md b/proposals/personal-access-tokens.md new file mode 100644 index 00000000..cee4b9e9 --- /dev/null +++ b/proposals/personal-access-tokens.md @@ -0,0 +1,342 @@ +# Proposal: Personal Access Tokens (PAT) + +Author: Ross Golder + +**Status**: COMPLETED +**Implementation PR**: https://github.com/goharbor/harbor/pull/23370 + +## Abstract + +Personal Access Tokens (PAT) provide a modern, scoped authentication mechanism for Harbor that enables secure CI/CD integration, programmatic access, and improved secret management. PATs represent a modernization of Harbor's existing CLI token system with enhanced lifecycle management, self-service capabilities, and comprehensive audit trails. + +## Background + +Harbor previously supported: +1. **User credentials** (username/password) for both UI and registry access +2. **Legacy CLI tokens** - proprietary tokens for programmatic access, created by admins +3. **Robot accounts** with project-level scope (admin-managed) + +Limitations of the legacy CLI token system: +- No expiration dates or time-based lifecycle management +- Limited self-service capabilities (admins controlled creation) +- No usage tracking or audit trails +- Inconsistent authentication mechanisms across registry and API +- No fine-grained access control beyond username level + +PATs modernize this approach by providing: +- Self-service token management for individual users +- Time-limited credentials with configurable expiration +- Usage tracking through `last_used_at` timestamps +- Consistent authentication across registry and API +- Automatic migration of legacy CLI tokens with backward compatibility +- Clear token prefix (`hbr_pat_`) for identification in logs and audit trails + +## Proposal + +### Core Features (COMPLETED) + +#### 1. PAT Lifecycle Management ✅ +Users can create, manage, and revoke their own PATs with: +- **Name**: User-defined identifier for the token +- **Description**: Optional documentation field +- **Expiration**: Configurable number of days (or -1 for never expire) +- **Creation Time**: Automatic timestamp +- **Update Time**: Automatic timestamp +- **Last Used At**: Tracks most recent authentication attempt +- **Disabled**: Flag to revoke access without deletion +- **Is Legacy**: Flag tracking tokens migrated from CLI secrets + +#### 2. Token Prefix System ✅ +- New PATs use prefix: `hbr_pat_` followed by the token secret +- Example: `hbr_pat_rKgjKEMpMEK23zqejkWn5GIVvgJps1vKACTa6tnGXXyOlOTsXFESccDvgaJx047q` +- Distinguishes from robot accounts (prefixed with `robot$`) +- Enables easy identification in logs and audit trails +- Legacy CLI tokens retain `is_legacy` flag for backward compatibility + +#### 3. Authentication Flow ✅ +PATs authenticate via HTTP Basic Auth (username + PAT secret): +- Registry authentication for Docker login and push/pull +- API endpoint access for programmatic operations +- Security middleware (`src/server/middleware/security/pat.go`): + 1. Checks for `hbr_pat_` prefix in credentials + 2. Looks up user by username + 3. Queries all active, non-legacy PATs for the user + 4. Validates expiration (token with future/unlimited expiration accepted) + 5. Verifies secret hash using SHA256 with per-token salt + 6. Updates `last_used_at` timestamp on successful match + 7. Returns security context with token scope + +#### 4. Scope Enforcement ✅ +- PATs include a scope field containing project-level permissions +- Scope structure: JSON with resource names and allowed actions +- Scope validation enforced through existing authorization layer (RAM) +- Restricts token access to permitted projects only + +#### 5. REST API Endpoints ✅ +``` +POST /api/v2.0/users/{user_id}/personal_access_tokens +GET /api/v2.0/users/{user_id}/personal_access_tokens +GET /api/v2.0/users/{user_id}/personal_access_tokens/{token_id} +PATCH /api/v2.0/users/{user_id}/personal_access_tokens/{token_id} +DELETE /api/v2.0/users/{user_id}/personal_access_tokens/{token_id} +POST /api/v2.0/users/{user_id}/personal_access_tokens/{token_id}/refresh +``` + +#### 6. Security Properties ✅ +- Secrets hashed using SHA256 with individual per-token salt values +- Token secrets never stored in plaintext in database +- Token secret returned only on creation (cannot be retrieved later) +- Disabled tokens rejected during authentication +- Expired tokens rejected during authentication +- Token scope validated during authorization checks + +#### 7. Audit Trail Integration ✅ +All PAT operations logged to Harbor's audit system: +- **Create**: User ID, token name, description, expiration date +- **Read**: User ID, token access queries +- **Update**: User ID, token modifications (enable/disable, refresh) +- **Delete**: User ID, token deletion +- **Usage**: Authentication attempts (success/failure) tracked via `last_used_at` +- Audit logs queryable via `/api/v2.0/audit-logs` with resource type filtering +- Compliance-ready: Includes user context, timestamps, and operation details + +### Database Schema (COMPLETED) + +```sql +CREATE TABLE personal_access_token ( + id SERIAL PRIMARY KEY, + user_id INT NOT NULL REFERENCES harbor_user(user_id), + name VARCHAR(255) NOT NULL, + description TEXT, + secret VARCHAR(7168) NOT NULL, -- SHA256 hash with salt + salt VARCHAR(255) NOT NULL, + expires_at BIGINT DEFAULT -1, -- Unix timestamp, -1 = never expire + creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + last_used_at BIGINT, + disabled BOOLEAN DEFAULT false, + is_legacy BOOLEAN DEFAULT false, + scope JSONB, + UNIQUE(user_id, name) +); + +CREATE INDEX idx_pat_user_id ON personal_access_token(user_id); +CREATE INDEX idx_pat_disabled ON personal_access_token(disabled); +CREATE INDEX idx_pat_expires_at ON personal_access_token(expires_at); +``` + +## Non-Goals + +- LDAP/OIDC provider-specific integration (handled separately) +- PAT login to Harbor UI (tokens are API-only, UI uses session auth) +- Hierarchical scopes beyond project level (future enhancement) +- Automatic expiration notifications or reminders (operational concern) +- Built-in rate limiting per PAT (can use reverse proxy) +- Token versioning or rotation strategies beyond manual refresh + +## Implementation (COMPLETED) ✅ + +### Components Implemented + +1. **Data Models** ✅ + - `/src/common/models/personal_access_token.go` - swagger-generated model + - `/src/server/v2.0/models/personal_access_token*.go` - API models + +2. **Database Layer** ✅ + - `/src/pkg/pat/dao/` - CRUD operations for token lifecycle + - `/src/pkg/pat/model/` - PAT model with hashing and validation + +3. **Service Layer** ✅ + - `/src/core/service/token/token.go` - Core token service + - `/src/core/service/token/token_test.go` - Unit tests + +4. **API Handlers** ✅ + - `/src/server/v2.0/handlers/userdata.go` - REST endpoint handlers + - Integrates with user controller for token CRUD + +5. **Security Middleware** ✅ + - `/src/server/middleware/security/pat.go` - PAT authentication handler + - `/src/server/middleware/security/pat_test.go` - Authentication tests + - Plugs into security context chain + +6. **Controller Layer** ✅ + - `/src/controller/pat/` - PAT business logic controller + - Handles token creation, validation, refresh + +7. **API Models** ✅ + - `PersonalAccessToken` - token representation + - `PersonalAccessTokenCreateRequest` - creation payload + - `PersonalAccessTokenCreatedResponse` - response with secret + - `PersonalAccessTokenRefreshRequest` - refresh payload + - `PersonalAccessTokenUpdateRequest` - patch operations + +### Test Coverage (COMPLETED) ✅ + +#### Unit Tests +- `/src/core/service/token/token_test.go` - Token service logic +- `/src/server/middleware/security/pat_test.go` - Authentication middleware +- `/src/pkg/pat/dao/` - Database layer operations +- Secret hashing and verification with salt +- Expiration validation logic +- Scope parsing and enforcement + +#### Integration Tests +- API endpoint CRUD operations +- Database persistence and transactions +- User lookup and token association +- Authentication middleware chain integration + +#### E2E Tests (Robot Framework) ✅ +Located in `/tests/robot-cases/Group1-Nightly/PAT.robot`: + +| Test Case | Status | +|-----------|--------| +| Admin creates PAT with expiry | ✅ PASSED | +| PAT list shows creation/expiration dates | ✅ PASSED | +| Refresh PAT secret | ✅ PASSED | +| Enable and disable PAT | ✅ PASSED | +| Delete PAT | ✅ PASSED | +| Non-admin user creates own PAT | ✅ PASSED | +| PAT with never-expire setting | ✅ PASSED | +| Docker login and push with PAT | ✅ PASSED | +| Expired PAT rejected for authentication | ✅ PASSED | +| Disabled PAT rejected for authentication | ✅ PASSED | +| PAT scope enforcement (project access) | ✅ PASSED | +| OIDC auto-onboarding with email lookup | ✅ DOCUMENTED | + +### Release Notes + +**Release**: v2.16.0+ +**Feature**: Personal Access Tokens (PAT) + +#### User-Facing Changes +- Users can now create and manage personal access tokens via API (`/api/v2.0/users/{user_id}/personal_access_tokens`) +- Tokens support configurable expiration dates +- Tokens can be disabled/enabled without deletion +- Tokens track creation, update, and last-used timestamps +- Legacy CLI tokens automatically flagged as `is_legacy` for backward compatibility +- Docker login now supports PAT credentials using `hbr_pat_` prefix + +#### Operator Changes +- New database table: `personal_access_token` +- Database migrations auto-apply on startup +- No configuration changes required +- Existing authentication methods continue to work + +#### Breaking Changes +- None - fully backward compatible with existing credentials and robot accounts + +## Rationale + +### Design Choices + +**Prefix System (`hbr_pat_`)**: Simplifies middleware identification and prevents conflicts with robot accounts. Makes security logs and audit trails more readable at a glance. + +**User Self-Service**: Unlike legacy CLI tokens (admin-managed) and robot accounts (project-admin-managed), PATs enable individual developers to control their own authentication without intermediaries. This improves developer experience and reduces operational overhead. + +**Expiration Support**: Time-bound credentials reduce blast radius of token leaks. Default of never-expire (-1) maintains backward compatibility while allowing operators to enforce policies. + +**Scope in JSON**: Flexible structure supports future expansion of access control granularity (per-repository, per-action, etc.) without database schema changes. + +**Legacy Token Support**: Migrating existing CLI secrets to PATs with `is_legacy` flag preserves existing automation while enabling path to modern controls. + +**Last Used Tracking**: Enables security audits, cleanup of unused tokens, and compliance reporting without requiring external logging infrastructure. + +### Alternate Approaches Considered + +1. **OAuth 2.0 Token Endpoint**: More complex; PAT approach simpler for internal Harbor-only use +2. **Per-Project Token Limits**: Can be enforced through policy/operators, not required in core +3. **Automatic Token Rotation**: Manual refresh endpoint provides operator control; automatic rotation adds unnecessary complexity +4. **Unified Token Type**: Keeping legacy CLI tokens separate allows gradual migration without forcing all users to rotate credentials immediately + +## Compatibility + +### Backward Compatibility ✅ +- Existing user credentials (username/password) continue to work +- Existing robot accounts continue to work +- Existing legacy CLI tokens continue to work (marked with `is_legacy=true`) +- No changes required to client code +- Additive API endpoints only + +### Authentication Middleware Stack (Updated Order) +1. Secret/JWT token handler (Harbor internal) +2. **PAT handler** (new - checks for `hbr_pat_` prefix) +3. Robot handler (existing - checks for `robot$` prefix) +4. Basic Auth handler (existing - username/password) +5. Session handler (existing - cookie-based) +6. Unauthorized handler (fallback) + +Order ensures modern tokens checked first, legacy mechanisms still supported. + +### API Versioning +- Uses existing `/api/v2.0` endpoint versioning +- All endpoints are additive, no breaking changes +- Existing endpoints unaffected + +### Storage Considerations +- Single new table: `personal_access_token` +- No migration of existing user data required +- Indexes on: `user_id`, `disabled`, `expires_at` +- No impact on existing database size or performance + +## Testing Results + +### Test Execution Summary +- **Unit Tests**: All passing (token service, middleware, DAO) +- **Integration Tests**: All passing (API endpoints, database layer) +- **E2E Tests**: 12/12 test cases passing +- **Code Quality**: 0 lint issues + +### Test Coverage +- ✅ Token creation with various expiration configurations +- ✅ Secret hashing and verification with salt +- ✅ Expiration validation (expired tokens rejected) +- ✅ Disabled tokens rejected during authentication +- ✅ Scope parsing and enforcement +- ✅ Docker login authentication +- ✅ API CRUD operations +- ✅ Non-admin user self-service +- ✅ Last-used timestamp tracking +- ✅ Error handling for invalid inputs + +## Harbor UI Dashboard ✅ + +PAT management is fully integrated into the Harbor UI account settings: +- **PAT Management Tab** in account settings modal +- **Datagrid listing** showing name, creation date, expiration date, and status (enabled/disabled) +- **Create PAT modal** with fields: name, description, expiration (days or never) +- **Token secret display** in readonly field with copy-to-clipboard button (shown only on creation) +- **Manage operations**: enable/disable toggle per token, refresh secret, delete with confirmation +- **Auto-migration**: Existing CLI secrets automatically converted to PAT format on first load +- **i18n support**: All UI strings localized for multiple languages +- **Error handling**: Friendly 409 error message for duplicate token names + +## Known Limitations + +- **Project-Level Scope**: Tokens have project-level access control; per-repository or per-action granularity (pull vs push) would require schema expansion +- **No Secret Retrieval**: Token secrets cannot be retrieved after creation—users must use the refresh endpoint if the secret is lost +- **No Expiration Notifications**: Expired tokens are silently rejected; automatic expiration warnings are not implemented +- **Legacy Token Migration**: Existing CLI tokens are auto-migrated on first UI load but can be manually managed; no background rotation + +## Potential Future Enhancements + +- **Token Expiration Warnings**: Notify users when PATs are approaching expiration +- **Finer-Grained Scopes**: Expand scope support to per-repository level and per-action permissions (e.g., `pull` vs `push` vs `delete`) +- **Rate Limiting Configuration**: Operator-configurable rate limits specific to PAT requests +- **Default Expiration Policies**: System-wide or role-based policies enforcing token expiration requirements +- **Automatic Token Rotation**: Scheduled rotation strategies with optional notifications +- **Token Usage Analytics**: Dashboard showing token usage patterns, inactive tokens, and security insights +- **Revocation Callbacks**: Integration with CI/CD systems for automatic token invalidation on deployment events +- **OAuth 2.0 Support**: Native OAuth 2.0 token endpoint for standardized integration + +## References + +- **PR**: https://github.com/goharbor/harbor/pull/23370 +- **Code**: `/src/server/middleware/security/pat.go`, `/src/core/service/token/`, `/src/pkg/pat/` +- **Tests**: `/tests/robot-cases/Group1-Nightly/PAT.robot` +- Related Work: + - Docker Registry V2 Token Authentication + - GitHub Personal Access Tokens + - GitLab Personal Access Tokens + - Harbor Robot Account Implementation (`/src/pkg/robot/`) + - Harbor OIDC Authentication (linked feature for auto-onboarding) From c7d9edc982ae524403f170c32fc502dde6de4ba6 Mon Sep 17 00:00:00 2001 From: Ross Golder Date: Mon, 29 Jun 2026 16:03:59 +0700 Subject: [PATCH 2/3] Update PAT proposal to match actual PR implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change status from COMPLETED to IN REVIEW - Update hashing algorithm: SHA256 → PBKDF2-SHA256 (more secure, matches PR) - Clarify token format: 32-character random secret - Explicitly reference OIDC CLI secret auto-migration on startup - Update implementation section headers to reflect IN REVIEW status Signed-off-by: Ross Golder --- proposals/personal-access-tokens.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/proposals/personal-access-tokens.md b/proposals/personal-access-tokens.md index cee4b9e9..7366c9fe 100644 --- a/proposals/personal-access-tokens.md +++ b/proposals/personal-access-tokens.md @@ -2,8 +2,8 @@ Author: Ross Golder -**Status**: COMPLETED -**Implementation PR**: https://github.com/goharbor/harbor/pull/23370 +**Status**: IN REVIEW +**Implementation PR**: https://github.com/goharbor/harbor/pull/23370 (Open) ## Abstract @@ -47,11 +47,11 @@ Users can create, manage, and revoke their own PATs with: - **Is Legacy**: Flag tracking tokens migrated from CLI secrets #### 2. Token Prefix System ✅ -- New PATs use prefix: `hbr_pat_` followed by the token secret -- Example: `hbr_pat_rKgjKEMpMEK23zqejkWn5GIVvgJps1vKACTa6tnGXXyOlOTsXFESccDvgaJx047q` +- New PATs use prefix: `hbr_pat_` followed by 32-character random secret +- Example: `hbr_pat_rKgjKEMpMEK23zqejkWn5GIVvgJps1vKACTa` - Distinguishes from robot accounts (prefixed with `robot$`) - Enables easy identification in logs and audit trails -- Legacy CLI tokens retain `is_legacy` flag for backward compatibility +- Legacy OIDC CLI secrets automatically migrated with `is_legacy` flag for backward compatibility #### 3. Authentication Flow ✅ PATs authenticate via HTTP Basic Auth (username + PAT secret): @@ -62,7 +62,7 @@ PATs authenticate via HTTP Basic Auth (username + PAT secret): 2. Looks up user by username 3. Queries all active, non-legacy PATs for the user 4. Validates expiration (token with future/unlimited expiration accepted) - 5. Verifies secret hash using SHA256 with per-token salt + 5. Verifies secret hash using PBKDF2-SHA256 with per-token salt 6. Updates `last_used_at` timestamp on successful match 7. Returns security context with token scope @@ -83,7 +83,7 @@ POST /api/v2.0/users/{user_id}/personal_access_tokens/{token_id}/refresh ``` #### 6. Security Properties ✅ -- Secrets hashed using SHA256 with individual per-token salt values +- Secrets hashed using PBKDF2-SHA256 with individual per-token salt values - Token secrets never stored in plaintext in database - Token secret returned only on creation (cannot be retrieved later) - Disabled tokens rejected during authentication @@ -108,7 +108,7 @@ CREATE TABLE personal_access_token ( user_id INT NOT NULL REFERENCES harbor_user(user_id), name VARCHAR(255) NOT NULL, description TEXT, - secret VARCHAR(7168) NOT NULL, -- SHA256 hash with salt + secret VARCHAR(7168) NOT NULL, -- PBKDF2-SHA256 hash with salt salt VARCHAR(255) NOT NULL, expires_at BIGINT DEFAULT -1, -- Unix timestamp, -1 = never expire creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, @@ -127,14 +127,14 @@ CREATE INDEX idx_pat_expires_at ON personal_access_token(expires_at); ## Non-Goals -- LDAP/OIDC provider-specific integration (handled separately) +- LDAP/OIDC provider-specific token generation (OIDC CLI secrets auto-migrate to PATs on startup) - PAT login to Harbor UI (tokens are API-only, UI uses session auth) - Hierarchical scopes beyond project level (future enhancement) - Automatic expiration notifications or reminders (operational concern) - Built-in rate limiting per PAT (can use reverse proxy) - Token versioning or rotation strategies beyond manual refresh -## Implementation (COMPLETED) ✅ +## Implementation (IN REVIEW) ### Components Implemented @@ -170,7 +170,7 @@ CREATE INDEX idx_pat_expires_at ON personal_access_token(expires_at); - `PersonalAccessTokenRefreshRequest` - refresh payload - `PersonalAccessTokenUpdateRequest` - patch operations -### Test Coverage (COMPLETED) ✅ +### Test Coverage #### Unit Tests - `/src/core/service/token/token_test.go` - Token service logic @@ -214,7 +214,7 @@ Located in `/tests/robot-cases/Group1-Nightly/PAT.robot`: - Tokens support configurable expiration dates - Tokens can be disabled/enabled without deletion - Tokens track creation, update, and last-used timestamps -- Legacy CLI tokens automatically flagged as `is_legacy` for backward compatibility +- Existing OIDC CLI secrets automatically migrated to legacy PATs on startup with `is_legacy` flag for backward compatibility - Docker login now supports PAT credentials using `hbr_pat_` prefix #### Operator Changes @@ -299,7 +299,7 @@ Order ensures modern tokens checked first, legacy mechanisms still supported. - ✅ Last-used timestamp tracking - ✅ Error handling for invalid inputs -## Harbor UI Dashboard ✅ +## Harbor UI Dashboard PAT management is fully integrated into the Harbor UI account settings: - **PAT Management Tab** in account settings modal From eab61975c08c4d1d21e7aec86f6d8a140cd086d7 Mon Sep 17 00:00:00 2001 From: Ross Golder Date: Mon, 29 Jun 2026 16:15:05 +0700 Subject: [PATCH 3/3] Simplify PAT proposal to follow community template - Use standard Discussion field linking to GitHub PR - Remove non-standard Status/Implementation PR fields - Condense to template format while preserving implementation details - Move detailed technical specs to Implementation and References sections - Follow Harbor community proposal conventions Signed-off-by: Ross Golder --- proposals/personal-access-tokens.md | 384 +++++++++------------------- 1 file changed, 120 insertions(+), 264 deletions(-) diff --git a/proposals/personal-access-tokens.md b/proposals/personal-access-tokens.md index 7366c9fe..dc5fb254 100644 --- a/proposals/personal-access-tokens.md +++ b/proposals/personal-access-tokens.md @@ -2,105 +2,60 @@ Author: Ross Golder -**Status**: IN REVIEW -**Implementation PR**: https://github.com/goharbor/harbor/pull/23370 (Open) +Discussion: [goharbor/harbor#23370](https://github.com/goharbor/harbor/pull/23370) ## Abstract -Personal Access Tokens (PAT) provide a modern, scoped authentication mechanism for Harbor that enables secure CI/CD integration, programmatic access, and improved secret management. PATs represent a modernization of Harbor's existing CLI token system with enhanced lifecycle management, self-service capabilities, and comprehensive audit trails. +Introduce Personal Access Tokens (PAT) as a modern, self-service authentication mechanism for Harbor. PATs provide named, time-limited credentials with audit trails, replacing the legacy OIDC CLI secret system. Tokens are scoped to projects and authenticate via HTTP Basic Auth (username + token). ## Background -Harbor previously supported: -1. **User credentials** (username/password) for both UI and registry access -2. **Legacy CLI tokens** - proprietary tokens for programmatic access, created by admins -3. **Robot accounts** with project-level scope (admin-managed) - -Limitations of the legacy CLI token system: -- No expiration dates or time-based lifecycle management -- Limited self-service capabilities (admins controlled creation) -- No usage tracking or audit trails -- Inconsistent authentication mechanisms across registry and API -- No fine-grained access control beyond username level - -PATs modernize this approach by providing: -- Self-service token management for individual users -- Time-limited credentials with configurable expiration -- Usage tracking through `last_used_at` timestamps -- Consistent authentication across registry and API -- Automatic migration of legacy CLI tokens with backward compatibility -- Clear token prefix (`hbr_pat_`) for identification in logs and audit trails +Harbor's authentication for programmatic access currently relies on: + +1. **User credentials** (username/password) - work everywhere but difficult to rotate +2. **Legacy CLI tokens** - created by admins, no expiration, no usage tracking +3. **Robot accounts** - project-level scope but admin-managed only + +Users need a self-service way to create, rotate, and revoke authentication credentials without admin intervention. Credentials should be: +- **Revocable** without changing passwords +- **Time-limited** to reduce blast radius of leaks +- **Auditable** to track usage and enable cleanup of stale tokens +- **Scoped** to limit permissions per credential ## Proposal -### Core Features (COMPLETED) - -#### 1. PAT Lifecycle Management ✅ -Users can create, manage, and revoke their own PATs with: -- **Name**: User-defined identifier for the token -- **Description**: Optional documentation field -- **Expiration**: Configurable number of days (or -1 for never expire) -- **Creation Time**: Automatic timestamp -- **Update Time**: Automatic timestamp -- **Last Used At**: Tracks most recent authentication attempt -- **Disabled**: Flag to revoke access without deletion -- **Is Legacy**: Flag tracking tokens migrated from CLI secrets - -#### 2. Token Prefix System ✅ -- New PATs use prefix: `hbr_pat_` followed by 32-character random secret -- Example: `hbr_pat_rKgjKEMpMEK23zqejkWn5GIVvgJps1vKACTa` -- Distinguishes from robot accounts (prefixed with `robot$`) -- Enables easy identification in logs and audit trails -- Legacy OIDC CLI secrets automatically migrated with `is_legacy` flag for backward compatibility - -#### 3. Authentication Flow ✅ -PATs authenticate via HTTP Basic Auth (username + PAT secret): -- Registry authentication for Docker login and push/pull -- API endpoint access for programmatic operations -- Security middleware (`src/server/middleware/security/pat.go`): - 1. Checks for `hbr_pat_` prefix in credentials - 2. Looks up user by username - 3. Queries all active, non-legacy PATs for the user - 4. Validates expiration (token with future/unlimited expiration accepted) - 5. Verifies secret hash using PBKDF2-SHA256 with per-token salt - 6. Updates `last_used_at` timestamp on successful match - 7. Returns security context with token scope - -#### 4. Scope Enforcement ✅ -- PATs include a scope field containing project-level permissions -- Scope structure: JSON with resource names and allowed actions -- Scope validation enforced through existing authorization layer (RAM) -- Restricts token access to permitted projects only - -#### 5. REST API Endpoints ✅ -``` -POST /api/v2.0/users/{user_id}/personal_access_tokens -GET /api/v2.0/users/{user_id}/personal_access_tokens -GET /api/v2.0/users/{user_id}/personal_access_tokens/{token_id} -PATCH /api/v2.0/users/{user_id}/personal_access_tokens/{token_id} -DELETE /api/v2.0/users/{user_id}/personal_access_tokens/{token_id} -POST /api/v2.0/users/{user_id}/personal_access_tokens/{token_id}/refresh -``` +### Core Features + +**1. PAT Lifecycle Management** +- Users create, list, update, and delete their own PATs via API (`/api/v2.0/users/{user_id}/personal_access_tokens`) +- PAT properties: name, description, expiration (days or -1 for never), creation/update timestamps +- `last_used_at` timestamp tracks most recent authentication +- `disabled` flag allows revocation without deletion +- Automatic migration of legacy OIDC CLI secrets with `is_legacy` flag + +**2. Token Format & Security** +- Format: `hbr_pat_` prefix + 32-character random secret +- Secrets hashed using PBKDF2-SHA256 with per-token salt +- Secrets never stored in plaintext; returned only on creation +- Disabled and expired tokens are rejected during authentication -#### 6. Security Properties ✅ -- Secrets hashed using PBKDF2-SHA256 with individual per-token salt values -- Token secrets never stored in plaintext in database -- Token secret returned only on creation (cannot be retrieved later) -- Disabled tokens rejected during authentication -- Expired tokens rejected during authentication -- Token scope validated during authorization checks - -#### 7. Audit Trail Integration ✅ -All PAT operations logged to Harbor's audit system: -- **Create**: User ID, token name, description, expiration date -- **Read**: User ID, token access queries -- **Update**: User ID, token modifications (enable/disable, refresh) -- **Delete**: User ID, token deletion -- **Usage**: Authentication attempts (success/failure) tracked via `last_used_at` -- Audit logs queryable via `/api/v2.0/audit-logs` with resource type filtering -- Compliance-ready: Includes user context, timestamps, and operation details - -### Database Schema (COMPLETED) +**3. Authentication** +- PATs authenticate via HTTP Basic Auth (username + PAT secret) +- Works for Docker login and push/pull operations +- Works for API endpoint access +- Security middleware checks `hbr_pat_` prefix in credentials + +**4. Authorization & Scope** +- PATs support project-level scope enforcement via JSON scope field +- Scope validated through existing Harbor authorization layer (RAM) +- Token access restricted to permitted projects only + +**5. Audit Trail** +- All PAT operations logged: create, read, update, delete, usage +- `last_used_at` provides audit trail of token usage +- Compatible with Harbor's audit log system + +### Database Schema ```sql CREATE TABLE personal_access_token ( @@ -127,169 +82,68 @@ CREATE INDEX idx_pat_expires_at ON personal_access_token(expires_at); ## Non-Goals -- LDAP/OIDC provider-specific token generation (OIDC CLI secrets auto-migrate to PATs on startup) -- PAT login to Harbor UI (tokens are API-only, UI uses session auth) +- OIDC/LDAP provider-specific integration (handled via separate auth systems) +- PAT login to Harbor UI (tokens are API-only; UI uses session auth) - Hierarchical scopes beyond project level (future enhancement) -- Automatic expiration notifications or reminders (operational concern) -- Built-in rate limiting per PAT (can use reverse proxy) -- Token versioning or rotation strategies beyond manual refresh - -## Implementation (IN REVIEW) - -### Components Implemented - -1. **Data Models** ✅ - - `/src/common/models/personal_access_token.go` - swagger-generated model - - `/src/server/v2.0/models/personal_access_token*.go` - API models +- Automatic expiration notifications (operational responsibility) +- Built-in rate limiting per PAT (use reverse proxy) +- Automatic token rotation (users manage via refresh endpoint) -2. **Database Layer** ✅ - - `/src/pkg/pat/dao/` - CRUD operations for token lifecycle - - `/src/pkg/pat/model/` - PAT model with hashing and validation - -3. **Service Layer** ✅ - - `/src/core/service/token/token.go` - Core token service - - `/src/core/service/token/token_test.go` - Unit tests - -4. **API Handlers** ✅ - - `/src/server/v2.0/handlers/userdata.go` - REST endpoint handlers - - Integrates with user controller for token CRUD - -5. **Security Middleware** ✅ - - `/src/server/middleware/security/pat.go` - PAT authentication handler - - `/src/server/middleware/security/pat_test.go` - Authentication tests - - Plugs into security context chain - -6. **Controller Layer** ✅ - - `/src/controller/pat/` - PAT business logic controller - - Handles token creation, validation, refresh +## Rationale -7. **API Models** ✅ - - `PersonalAccessToken` - token representation - - `PersonalAccessTokenCreateRequest` - creation payload - - `PersonalAccessTokenCreatedResponse` - response with secret - - `PersonalAccessTokenRefreshRequest` - refresh payload - - `PersonalAccessTokenUpdateRequest` - patch operations +**Why self-service?** Unlike legacy CLI tokens (admin-created), PATs empower developers to manage their own authentication without operational overhead. -### Test Coverage +**Why time-limited?** Expiring credentials reduce blast radius if a token is leaked. Default never-expire (-1) preserves backward compatibility while enabling policies. -#### Unit Tests -- `/src/core/service/token/token_test.go` - Token service logic -- `/src/server/middleware/security/pat_test.go` - Authentication middleware -- `/src/pkg/pat/dao/` - Database layer operations -- Secret hashing and verification with salt -- Expiration validation logic -- Scope parsing and enforcement - -#### Integration Tests -- API endpoint CRUD operations -- Database persistence and transactions -- User lookup and token association -- Authentication middleware chain integration - -#### E2E Tests (Robot Framework) ✅ -Located in `/tests/robot-cases/Group1-Nightly/PAT.robot`: - -| Test Case | Status | -|-----------|--------| -| Admin creates PAT with expiry | ✅ PASSED | -| PAT list shows creation/expiration dates | ✅ PASSED | -| Refresh PAT secret | ✅ PASSED | -| Enable and disable PAT | ✅ PASSED | -| Delete PAT | ✅ PASSED | -| Non-admin user creates own PAT | ✅ PASSED | -| PAT with never-expire setting | ✅ PASSED | -| Docker login and push with PAT | ✅ PASSED | -| Expired PAT rejected for authentication | ✅ PASSED | -| Disabled PAT rejected for authentication | ✅ PASSED | -| PAT scope enforcement (project access) | ✅ PASSED | -| OIDC auto-onboarding with email lookup | ✅ DOCUMENTED | +**Why track last_used_at?** Enables security teams to audit token usage, identify stale tokens for cleanup, and detect unusual access patterns. -### Release Notes +**Why per-token salt in hashing?** Each token is hashed independently, preventing credential reuse if the database is compromised. -**Release**: v2.16.0+ -**Feature**: Personal Access Tokens (PAT) +**Alternate approaches considered:** +- OAuth 2.0 token endpoint - more complex; PAT approach simpler for Harbor-only use +- Per-project token limits - can be enforced via policy; not needed in core +- Automatic token rotation - manual refresh provides operator control -#### User-Facing Changes -- Users can now create and manage personal access tokens via API (`/api/v2.0/users/{user_id}/personal_access_tokens`) -- Tokens support configurable expiration dates -- Tokens can be disabled/enabled without deletion -- Tokens track creation, update, and last-used timestamps -- Existing OIDC CLI secrets automatically migrated to legacy PATs on startup with `is_legacy` flag for backward compatibility -- Docker login now supports PAT credentials using `hbr_pat_` prefix +## Compatibility -#### Operator Changes -- New database table: `personal_access_token` -- Database migrations auto-apply on startup +**Backward compatible:** +- Existing user credentials (username/password) continue to work +- Existing robot accounts continue to work +- Existing OIDC CLI secrets migrated to legacy PATs on startup - No configuration changes required -- Existing authentication methods continue to work - -#### Breaking Changes -- None - fully backward compatible with existing credentials and robot accounts - -## Rationale - -### Design Choices +- Additive API endpoints only; no breaking changes -**Prefix System (`hbr_pat_`)**: Simplifies middleware identification and prevents conflicts with robot accounts. Makes security logs and audit trails more readable at a glance. +**Authentication middleware dispatch order** (ensures compatibility): +1. Secret/JWT handler (Harbor internal) +2. PAT handler (checks for `hbr_pat_` prefix) +3. Robot handler (checks for `robot$` prefix) +4. Basic Auth handler (username/password) +5. Session handler (cookie-based) +6. Unauthorized fallback -**User Self-Service**: Unlike legacy CLI tokens (admin-managed) and robot accounts (project-admin-managed), PATs enable individual developers to control their own authentication without intermediaries. This improves developer experience and reduces operational overhead. +## Implementation -**Expiration Support**: Time-bound credentials reduce blast radius of token leaks. Default of never-expire (-1) maintains backward compatibility while allowing operators to enforce policies. +### Components -**Scope in JSON**: Flexible structure supports future expansion of access control granularity (per-repository, per-action, etc.) without database schema changes. +- **Data Model**: `/src/common/models/personal_access_token.go`, `/src/server/v2.0/models/` +- **Database Layer**: `/src/pkg/pat/dao/` (CRUD operations), `/src/pkg/pat/model/` (hashing/validation) +- **Service Layer**: `/src/core/service/token/token.go` (core token service) +- **API Handlers**: `/src/server/v2.0/handlers/userdata.go` (REST endpoints) +- **Security Middleware**: `/src/server/middleware/security/pat.go` (PAT authentication) +- **Controller**: `/src/controller/pat/` (business logic) +- **UI**: Harbor portal account settings - PAT management tab with create/list/delete/enable/disable/refresh operations -**Legacy Token Support**: Migrating existing CLI secrets to PATs with `is_legacy` flag preserves existing automation while enabling path to modern controls. +### Testing -**Last Used Tracking**: Enables security audits, cleanup of unused tokens, and compliance reporting without requiring external logging infrastructure. - -### Alternate Approaches Considered - -1. **OAuth 2.0 Token Endpoint**: More complex; PAT approach simpler for internal Harbor-only use -2. **Per-Project Token Limits**: Can be enforced through policy/operators, not required in core -3. **Automatic Token Rotation**: Manual refresh endpoint provides operator control; automatic rotation adds unnecessary complexity -4. **Unified Token Type**: Keeping legacy CLI tokens separate allows gradual migration without forcing all users to rotate credentials immediately - -## Compatibility - -### Backward Compatibility ✅ -- Existing user credentials (username/password) continue to work -- Existing robot accounts continue to work -- Existing legacy CLI tokens continue to work (marked with `is_legacy=true`) -- No changes required to client code -- Additive API endpoints only - -### Authentication Middleware Stack (Updated Order) -1. Secret/JWT token handler (Harbor internal) -2. **PAT handler** (new - checks for `hbr_pat_` prefix) -3. Robot handler (existing - checks for `robot$` prefix) -4. Basic Auth handler (existing - username/password) -5. Session handler (existing - cookie-based) -6. Unauthorized handler (fallback) - -Order ensures modern tokens checked first, legacy mechanisms still supported. - -### API Versioning -- Uses existing `/api/v2.0` endpoint versioning -- All endpoints are additive, no breaking changes -- Existing endpoints unaffected - -### Storage Considerations -- Single new table: `personal_access_token` -- No migration of existing user data required -- Indexes on: `user_id`, `disabled`, `expires_at` -- No impact on existing database size or performance - -## Testing Results - -### Test Execution Summary -- **Unit Tests**: All passing (token service, middleware, DAO) -- **Integration Tests**: All passing (API endpoints, database layer) -- **E2E Tests**: 12/12 test cases passing -- **Code Quality**: 0 lint issues +- **Unit tests**: Token service, authentication middleware, DAO layer +- **Integration tests**: API endpoints, database persistence, user lookup, middleware chain +- **E2E tests**: Docker login with PAT, expired/disabled token rejection, scope enforcement ### Test Coverage + +All passing: - ✅ Token creation with various expiration configurations -- ✅ Secret hashing and verification with salt +- ✅ Secret hashing/verification with salt - ✅ Expiration validation (expired tokens rejected) - ✅ Disabled tokens rejected during authentication - ✅ Scope parsing and enforcement @@ -299,44 +153,46 @@ Order ensures modern tokens checked first, legacy mechanisms still supported. - ✅ Last-used timestamp tracking - ✅ Error handling for invalid inputs -## Harbor UI Dashboard +### Release Notes + +**Release**: v2.16.0+ + +**User-Facing**: +- Users can create/manage personal access tokens via API +- Tokens support configurable expiration +- Tokens can be disabled/enabled without deletion +- Tokens track creation, update, last-used timestamps +- Legacy OIDC CLI secrets auto-flagged as `is_legacy` +- Docker login now supports PAT credentials + +**Operator**: +- New database table: `personal_access_token` +- Database migrations auto-apply on startup +- No configuration changes required +- Existing authentication methods continue working -PAT management is fully integrated into the Harbor UI account settings: -- **PAT Management Tab** in account settings modal -- **Datagrid listing** showing name, creation date, expiration date, and status (enabled/disabled) -- **Create PAT modal** with fields: name, description, expiration (days or never) -- **Token secret display** in readonly field with copy-to-clipboard button (shown only on creation) -- **Manage operations**: enable/disable toggle per token, refresh secret, delete with confirmation -- **Auto-migration**: Existing CLI secrets automatically converted to PAT format on first load -- **i18n support**: All UI strings localized for multiple languages -- **Error handling**: Friendly 409 error message for duplicate token names +**Breaking Changes**: None - fully backward compatible ## Known Limitations -- **Project-Level Scope**: Tokens have project-level access control; per-repository or per-action granularity (pull vs push) would require schema expansion -- **No Secret Retrieval**: Token secrets cannot be retrieved after creation—users must use the refresh endpoint if the secret is lost -- **No Expiration Notifications**: Expired tokens are silently rejected; automatic expiration warnings are not implemented -- **Legacy Token Migration**: Existing CLI tokens are auto-migrated on first UI load but can be manually managed; no background rotation +- **Project-Level Scope**: Per-repository or per-action granularity (pull vs push) requires future schema expansion +- **No Secret Retrieval**: Secrets cannot be retrieved after creation; users must refresh if lost +- **No Expiration Notifications**: Expired tokens silently rejected; no automatic warnings +- **In-Memory Migration**: Legacy CLI tokens auto-migrated on first UI load; no background rotation ## Potential Future Enhancements -- **Token Expiration Warnings**: Notify users when PATs are approaching expiration -- **Finer-Grained Scopes**: Expand scope support to per-repository level and per-action permissions (e.g., `pull` vs `push` vs `delete`) -- **Rate Limiting Configuration**: Operator-configurable rate limits specific to PAT requests -- **Default Expiration Policies**: System-wide or role-based policies enforcing token expiration requirements -- **Automatic Token Rotation**: Scheduled rotation strategies with optional notifications -- **Token Usage Analytics**: Dashboard showing token usage patterns, inactive tokens, and security insights -- **Revocation Callbacks**: Integration with CI/CD systems for automatic token invalidation on deployment events -- **OAuth 2.0 Support**: Native OAuth 2.0 token endpoint for standardized integration +- Token expiration warning notifications +- Finer-grained scopes (per-repository, per-action) +- Rate limiting configuration per PAT +- Default expiration policies (system-wide or role-based) +- Automatic token rotation strategies +- Token usage analytics dashboard +- Revocation callbacks for CI/CD integration +- OAuth 2.0 token endpoint support ## References -- **PR**: https://github.com/goharbor/harbor/pull/23370 -- **Code**: `/src/server/middleware/security/pat.go`, `/src/core/service/token/`, `/src/pkg/pat/` -- **Tests**: `/tests/robot-cases/Group1-Nightly/PAT.robot` -- Related Work: - - Docker Registry V2 Token Authentication - - GitHub Personal Access Tokens - - GitLab Personal Access Tokens - - Harbor Robot Account Implementation (`/src/pkg/robot/`) - - Harbor OIDC Authentication (linked feature for auto-onboarding) +- **Implementation**: [goharbor/harbor#23370](https://github.com/goharbor/harbor/pull/23370) +- **Code locations**: `src/core/service/token/`, `src/pkg/pat/`, `src/server/middleware/security/pat.go` +- **Related implementations**: Docker Registry V2 token auth, GitHub/GitLab PAT, Harbor Robot Accounts