Personal Access Tokens
Personal Access Tokens (PATs) provide secure API access for users to integrate with the Manager programmatically without using their primary credentials.
Overview
PATs are user-scoped tokens that allow:
- API Authentication - Access GraphQL and REST APIs programmatically
- Script Automation - Automate workflows with CLI tools or scripts
- CI/CD Integration - Deploy and manage resources from CI/CD pipelines
- Third-party Tools - Integrate with external tools and services
No tokens are minted by CLI login
pfy login does not provision a PAT — there is no browser flow. Create tokens here in the Manager UI and pass them to pfy login --token. Automatic per-machine token minting is planned; see Known Issues and the CLI login docs.
Security Features
PBKDF2 Hashing
PATs are hashed using PBKDF2 (Password-Based Key Derivation Function 2) following OWASP 2023 recommendations:
- 600,000 iterations - Computational cost to prevent brute-force attacks
- SHA-256 hash function
- Unique salt per token
- Only the hash is stored - plaintext token never persisted
Token Prefix
Tokens use the format pat_<prefix>_<random>. The 8-character prefix is stored for searchability without compromising security. This allows you to identify tokens in the UI without exposing the full value.
Example: pat_AbCd1234_9f8e7d6c... → Prefix AbCd1234 is searchable
Creating a Personal Access Token
Via UI
- Click Personal Access Tokens in the sidebar
- Click Create Token
- Fill in token details:
- Name - Descriptive name (e.g., "GitHub Actions", "CLI Tool")
- Expires At - Optional expiration date
- Click Create
- Copy the token immediately - it will only be shown once
IMPORTANT
The token is displayed only once during creation. Store it securely - you cannot retrieve it later.
Via API
mutation {
createPersonalAccessToken(
name: "GitHub Actions Deployment"
expiresAt: "2025-12-31T23:59:59Z"
) {
token
personalAccessToken {
id
name
createdAt
expiresAt
}
}
}Response:
{
"data": {
"createPersonalAccessToken": {
"token": "pat_AbCd1234_9f8e7d6c5b4a...",
"personalAccessToken": {
"id": "uuid",
"name": "GitHub Actions Deployment",
"createdAt": "2025-12-01T10:00:00Z",
"expiresAt": "2025-12-31T23:59:59Z"
}
}
}
}Using Personal Access Tokens
HTTP Authentication
Include the token in the Authorization header:
curl -H "Authorization: Bearer pat_AbCd1234_9f8e7d6c5b4a..." \
https://manager.example.com/queryGraphQL Client
import { createClient } from "@urql/core";
const client = createClient({
url: "https://manager.example.com/query",
fetchOptions: {
headers: {
Authorization: `Bearer ${process.env.PAT_TOKEN}`,
},
},
});CLI Tools
# Log in once with your PAT (--server is required too)
pfy login --server https://manager.example.com --token "pat_AbCd1234_9f8e7d6c5b4a..."
# Then use the CLI normally
pfy project listManaging Tokens
Listing Your Tokens
Via UI: Open the Personal Access Tokens page from the sidebar to view all your tokens.
Via API:
query {
myPersonalAccessTokens(
filters: []
order: { field: "created_at", direction: DESC }
pagination: { limit: 20, offset: 0 }
) {
id
name
tokenPrefix
createdAt
expiresAt
revoked
lastUsedAt
}
}Revoking a Token
Revoking a token immediately prevents it from being used for authentication.
Via UI:
- Open the Personal Access Tokens page
- Find the token to revoke
- Click Revoke
- Confirm the action
Via API:
mutation {
revokePersonalAccessToken(id: "token-uuid") {
id
revoked
}
}TIP
Revoked tokens are not deleted but marked as revoked. This preserves audit history.
Deleting a Token
Deleting permanently removes the token record.
Via UI:
- Open the Personal Access Tokens page
- Find the token to delete
- Click Delete
- Confirm the action
Via API:
mutation {
deletePersonalAccessToken(id: "token-uuid")
}Token Expiration
Setting Expiration
When creating a token, you can optionally set an expiration date (it must be in the future):
mutation {
createPersonalAccessToken(
name: "Temporary Token"
expiresAt: "2025-12-31T23:59:59Z"
) {
token
personalAccessToken {
expiresAt
}
}
}No Expiration
Tokens without an expiration date remain valid indefinitely until revoked or deleted.
Use cases for non-expiring tokens:
- Long-running services
- Internal automation
- Development environments
Use cases for expiring tokens:
- Temporary access grants
- External contractors
- Security compliance requirements
Best Practices
Security
- Store tokens securely - Use environment variables or secret managers
- Never commit tokens to version control
- Set expiration dates for tokens that don't need indefinite access
- Revoke unused tokens - Clean up tokens that are no longer needed
- Use descriptive names - Make it easy to identify token purpose
- Rotate tokens regularly - Create new tokens and revoke old ones periodically
Organization
- One token per use case - Create separate tokens for different tools/scripts
- Name tokens clearly - Use names that indicate purpose and location (e.g., "Production CI/CD", "Development CLI")
- Document token usage - Keep records of what each token is used for
- Monitor token usage - Check
lastUsedAtto identify unused tokens
Operational
- Test tokens after creation - Verify tokens work before deploying to production
- Have a rotation plan - Regularly create new tokens and retire old ones
- Alert on expiration - Monitor tokens approaching expiration
- Coordinate revocation - Plan token revocation to avoid service disruptions
Troubleshooting
Token Not Working
Check:
- Token hasn't expired (
expiresAt) - Token hasn't been revoked (
revoked) - Token is correctly formatted in the
Authorizationheader - Token includes the
Bearerprefix in the header - Network connectivity to Manager API
Authentication Failures
Verify:
- Authorization header format:
Authorization: Bearer <token> - Token string is complete and unmodified
- Token belongs to the user making the request
- User account is active and not disabled
Token Not Found
Possible causes:
- Token was deleted
- Token belongs to different user
- Token ID is incorrect
- Database connectivity issues
Comparison with Machine Users
| Feature | Personal Access Tokens | Machine Users |
|---|---|---|
| Scope | User-level | Tenant-level |
| Purpose | Personal automation | Service authentication |
| Expiration | Optional | No expiration |
| Authentication | Bearer only | Bearer or Basic |
| Use Case | CLI tools, scripts | Backend services |
| Created By | Individual users | Administrators |
When to use PATs:
- Personal automation and scripts
- CLI tool authentication
- Development and testing
- User-specific integrations
When to use Machine Users:
- Production backend services
- Service-to-service communication
- Tenant-scoped operations
- Long-running services
See Machine Users for service account authentication.
API Reference
Complete PAT management via GraphQL API:
# List tokens
query {
myPersonalAccessTokens(
filters: []
order: { field: "created_at", direction: DESC }
pagination: { limit: 20, offset: 0 }
) {
id
name
tokenPrefix
createdAt
expiresAt
lastUsedAt
}
}
# Create token
mutation {
createPersonalAccessToken(
name: "New Token"
expiresAt: "2025-12-31T23:59:59Z"
) {
token
personalAccessToken {
id
name
}
}
}
# Revoke token
mutation {
revokePersonalAccessToken(id: "token-uuid") {
id
revoked
}
}
# Delete token
mutation {
deletePersonalAccessToken(id: "token-uuid")
}See the full API Reference for details.