Skip to main content

Auth API

Base URL: https://auth.cloud.eddisonso.com

Authentication

POST /api/login

Authenticate and receive a session JWT.

Auth: None

ParamTypeInRequiredDescription
usernamestringbodyYesUsername
passwordstringbodyYesPassword

Example request:

curl -X POST https://auth.cloud.eddisonso.com/api/login \
-H "Content-Type: application/json" \
-d '{"username": "alice", "password": "secret"}'

Response:

If the user has no 2FA configured:

{
"username": "alice",
"display_name": "Alice",
"user_id": "abc123",
"is_admin": false,
"token": "eyJhbGci..."
}

If the user has security keys registered (2FA required):

{
"requires_2fa": true,
"challenge_token": "eyJhbGci..."
}

Use the challenge_token with the WebAuthn login endpoints to complete 2FA.


GET /api/session

Get current user info from session token.

Auth: Session

ParamTypeInRequiredDescription
AuthorizationstringheaderYesBearer <token>

Example request:

curl https://auth.cloud.eddisonso.com/api/session \
-H "Authorization: Bearer eyJhbGci..."

Response:

{
"username": "alice",
"display_name": "Alice",
"user_id": "abc123",
"is_admin": false
}

POST /api/logout

Acknowledge logout (client removes token).

Auth: None

Example request:

curl -X POST https://auth.cloud.eddisonso.com/api/logout

Response:

{
"status": "ok"
}

WebAuthn (2FA Login)

POST /api/webauthn/login/begin

Begin WebAuthn authentication ceremony (2FA). Requires a challenge token from the initial login response.

Auth: Challenge token

ParamTypeInRequiredDescription
AuthorizationstringheaderYesBearer <challenge_token>

Example request:

curl -X POST https://auth.cloud.eddisonso.com/api/webauthn/login/begin \
-H "Authorization: Bearer eyJhbGci..."

Response:

{
"options": {
"challenge": "...",
"timeout": 60000,
"rpId": "cloud.eddisonso.com",
"allowCredentials": [...]
},
"state": "ceremony_state_token"
}

Pass options to navigator.credentials.get() in the browser, then send the credential to /api/webauthn/login/finish.


POST /api/webauthn/login/finish

Complete WebAuthn authentication and receive a session token.

Auth: None

ParamTypeInRequiredDescription
statestringbodyYesState token from begin response
credentialobjectbodyYesSerialized credential from browser

Example request:

curl -X POST https://auth.cloud.eddisonso.com/api/webauthn/login/finish \
-H "Content-Type: application/json" \
-d '{
"state": "ceremony_state_token",
"credential": {...}
}'

Response:

{
"username": "alice",
"display_name": "Alice",
"user_id": "abc123",
"is_admin": false,
"token": "eyJhbGci..."
}

User Settings

GET /api/settings/keys

List all security keys (WebAuthn credentials) for the authenticated user.

Auth: Session

Example request:

curl https://auth.cloud.eddisonso.com/api/settings/keys \
-H "Authorization: Bearer eyJhbGci..."

Response:

{
"keys": [
{
"id": "base64_credential_id",
"name": "YubiKey 5",
"authenticator_type": "Security Key",
"created_at": 1712224000
}
]
}

POST /api/settings/keys/add/begin

Begin WebAuthn registration ceremony to add a new security key.

Auth: Session

Example request:

curl -X POST https://auth.cloud.eddisonso.com/api/settings/keys/add/begin \
-H "Authorization: Bearer eyJhbGci..."

Response:

{
"options": {
"challenge": "...",
"rp": {"name": "Edd Cloud", "id": "cloud.eddisonso.com"},
"user": {...},
"pubKeyCredParams": [...],
"timeout": 60000
},
"state": "ceremony_state_token"
}

POST /api/settings/keys/add/finish

Complete WebAuthn registration and save the new security key.

Auth: Session

ParamTypeInRequiredDescription
statestringbodyYesState token from begin response
credentialobjectbodyYesSerialized credential from browser
namestringbodyNoCustom name for the key (default: "")

Example request:

curl -X POST https://auth.cloud.eddisonso.com/api/settings/keys/add/finish \
-H "Authorization: Bearer eyJhbGci..." \
-H "Content-Type: application/json" \
-d '{
"state": "ceremony_state_token",
"credential": {...},
"name": "YubiKey 5"
}'

Response: 200 OK with empty body.


POST /api/settings/keys/delete

Delete a security key.

Auth: Session

ParamTypeInRequiredDescription
idstringbodyYesBase64-encoded credential ID

Example request:

curl -X POST https://auth.cloud.eddisonso.com/api/settings/keys/delete \
-H "Authorization: Bearer eyJhbGci..." \
-H "Content-Type: application/json" \
-d '{"id": "base64_credential_id"}'

Response: 200 OK with empty body.


POST /api/settings/keys/rename

Rename a security key.

Auth: Session

ParamTypeInRequiredDescription
idstringbodyYesBase64-encoded credential ID
namestringbodyYesNew name for the key

Example request:

curl -X POST https://auth.cloud.eddisonso.com/api/settings/keys/rename \
-H "Authorization: Bearer eyJhbGci..." \
-H "Content-Type: application/json" \
-d '{"id": "base64_credential_id", "name": "Work YubiKey"}'

Response: 200 OK with empty body.


PUT /api/settings/profile

Update display name for the authenticated user.

Auth: Session

ParamTypeInRequiredDescription
display_namestringbodyYesNew display name

Example request:

curl -X PUT https://auth.cloud.eddisonso.com/api/settings/profile \
-H "Authorization: Bearer eyJhbGci..." \
-H "Content-Type: application/json" \
-d '{"display_name": "Alice Smith"}'

Response:

{
"username": "alice",
"display_name": "Alice Smith",
"user_id": "abc123"
}

PUT /api/settings/password

Change password for the authenticated user.

Auth: Session

ParamTypeInRequiredDescription
current_passwordstringbodyYesCurrent password
new_passwordstringbodyYesNew password

Example request:

curl -X PUT https://auth.cloud.eddisonso.com/api/settings/password \
-H "Authorization: Bearer eyJhbGci..." \
-H "Content-Type: application/json" \
-d '{"current_password": "old", "new_password": "new"}'

Response: 200 OK with empty body.


GET /api/settings/sessions

List all active sessions for the authenticated user.

Auth: Session

Example request:

curl https://auth.cloud.eddisonso.com/api/settings/sessions \
-H "Authorization: Bearer eyJhbGci..."

Response:

[
{
"id": 123,
"ip_address": "203.0.113.42",
"created_at": 1712224000,
"is_current": true
},
{
"id": 122,
"ip_address": "198.51.100.10",
"created_at": 1712220000,
"is_current": false
}
]

Sessions automatically expire when their JWT token expires. There is no manual revocation endpoint.


API Tokens

POST /api/tokens

Create a new API token.

Auth: Session

ParamTypeInRequiredDescription
namestringbodyYesToken name (max 64 chars)
scopesobjectbodyYesScope-to-actions map (see Token Scopes)
expires_instringbodyNo"30d", "90d", "365d", or "never" (default "never")

Each scope key follows the format <service>.<user_id>[.<resource>[.<id>]] and maps to an array of actions: "create", "read", "update", "delete".

Example request:

curl -X POST https://auth.cloud.eddisonso.com/api/tokens \
-H "Authorization: Bearer eyJhbGci..." \
-H "Content-Type: application/json" \
-d '{
"name": "ci-deploy",
"scopes": {
"compute.abc123.containers": ["read", "create", "delete"]
},
"expires_in": "90d"
}'

Response:

{
"id": "tok_5f3a",
"name": "ci-deploy",
"scopes": {
"compute.abc123.containers": ["read", "create", "delete"]
},
"expires_at": 1720000000,
"created_at": 1712224000,
"last_used_at": 0,
"token": "ecloud_eyJhbGci..."
}

The token field is only returned on creation. Store it — it cannot be retrieved later.


GET /api/tokens

List all API tokens for the authenticated user.

Auth: Session

Example request:

curl https://auth.cloud.eddisonso.com/api/tokens \
-H "Authorization: Bearer eyJhbGci..."

Response:

[
{
"id": "tok_5f3a",
"name": "ci-deploy",
"scopes": {
"compute.abc123.containers": ["read", "create", "delete"]
},
"expires_at": 1720000000,
"created_at": 1712224000,
"last_used_at": 1712300000,
"service_account_id": null
}
]

For tokens bound to a service account, service_account_id will contain the service account ID instead of null.


DELETE /api/tokens/{id}

Delete an API token.

Auth: Session

ParamTypeInRequiredDescription
idstringpathYesToken ID

Example request:

curl -X DELETE https://auth.cloud.eddisonso.com/api/tokens/tok_5f3a \
-H "Authorization: Bearer eyJhbGci..."

Response:

{
"status": "ok"
}

GET /api/tokens/{id}/check

Service-to-service endpoint to check whether an API token is valid and not revoked.

Auth: None

ParamTypeInRequiredDescription
idstringpathYesToken ID

Example request:

curl https://auth.cloud.eddisonso.com/api/tokens/tok_5f3a/check

Response:

{
"status": "valid"
}

Returns 404 if the token is not found, revoked, or expired.

For service account tokens, the response includes the service account's current scopes:

{
"status": "valid",
"scopes": {
"compute.abc123.containers": ["read", "create"]
}
}

Docker Registry Token

GET /v2/token

Issues a short-lived JWT for Docker Token Authentication. This endpoint is called automatically by Docker/OCI clients when accessing registry.cloud.eddisonso.com. It is not typically called directly.

Auth: HTTP Basic (optional — omit for anonymous/public access)

ParamTypeInRequiredDescription
servicestringqueryYesRegistry hostname (e.g. registry.cloud.eddisonso.com)
scopestringqueryNoOCI scope string (e.g. repository:ecloud-auth:pull,push)

Example request (user login):

curl -u "eddison:mypassword" \
"https://auth.cloud.eddisonso.com/v2/token?service=registry.cloud.eddisonso.com&scope=repository:ecloud-auth:pull,push"

Example request (service account):

curl -u "my-sa:ecloud_eyJhbGci..." \
"https://auth.cloud.eddisonso.com/v2/token?service=registry.cloud.eddisonso.com&scope=repository:ecloud-auth:pull"

Response:

{
"token": "eyJhbGci...",
"expires_in": 300,
"issued_at": "2026-03-15T12:00:00Z"
}

The returned token is a JWT valid for 5 minutes. The access claim contains the granted OCI scopes. Actions not permitted (e.g. push to another user's private repo) are silently dropped from the granted set — the token is still issued, just with reduced scope.

Access rules:

IdentityPull (public)Pull (private)Push
AnonymousYesNoNo
Authenticated userYesOwn reposOwn repos
Service accountYesScoped reposScoped repos

For service account tokens, scopes are mapped from the storage.<userID>.registry.<repo> scope format to OCI pull/push actions. A wildcard storage.<userID>.registry scope grants access to all of that user's repositories.


Service Accounts

Service accounts separate identity and permissions from credentials. Create a service account with scoped permissions, then generate tokens that inherit those permissions. Updating a service account's scopes takes effect for all its tokens within the 5-minute cache TTL.

POST /api/service-accounts

Create a new service account.

Auth: Session

ParamTypeInRequiredDescription
namestringbodyYesService account name (max 64 chars)
scopesobjectbodyYesScope-to-actions map (same format as token scopes)

Example request:

curl -X POST https://auth.cloud.eddisonso.com/api/service-accounts \
-H "Authorization: Bearer eyJhbGci..." \
-H "Content-Type: application/json" \
-d '{
"name": "ci-pipeline",
"scopes": {
"compute.abc123.containers": ["read", "create", "delete"]
}
}'

Response:

{
"id": "xK9mPq",
"name": "ci-pipeline",
"scopes": {
"compute.abc123.containers": ["read", "create", "delete"]
},
"token_count": 0,
"created_at": 1712224000
}

GET /api/service-accounts

List all service accounts for the authenticated user.

Auth: Session

Example request:

curl https://auth.cloud.eddisonso.com/api/service-accounts \
-H "Authorization: Bearer eyJhbGci..."

Response:

[
{
"id": "xK9mPq",
"name": "ci-pipeline",
"scopes": {
"compute.abc123.containers": ["read", "create", "delete"]
},
"token_count": 2,
"created_at": 1712224000
}
]

GET /api/service-accounts/{id}

Get a service account by ID.

Auth: Session

ParamTypeInRequiredDescription
idstringpathYesService account ID

Response: Same format as list item above.


PUT /api/service-accounts/{id}/scopes

Update a service account's scopes. Changes take effect for all tokens within the 5-minute cache TTL.

Auth: Session

ParamTypeInRequiredDescription
idstringpathYesService account ID
scopesobjectbodyYesNew scope-to-actions map

Example request:

curl -X PUT https://auth.cloud.eddisonso.com/api/service-accounts/xK9mPq/scopes \
-H "Authorization: Bearer eyJhbGci..." \
-H "Content-Type: application/json" \
-d '{
"scopes": {
"compute.abc123.containers": ["read"],
"storage.abc123.files": ["read"]
}
}'

Response:

{
"status": "ok"
}

DELETE /api/service-accounts/{id}

Delete a service account. All its tokens are revoked (cascade delete).

Auth: Session

ParamTypeInRequiredDescription
idstringpathYesService account ID

Example request:

curl -X DELETE https://auth.cloud.eddisonso.com/api/service-accounts/xK9mPq \
-H "Authorization: Bearer eyJhbGci..."

Response:

{
"status": "ok"
}

POST /api/service-accounts/{id}/tokens

Create a token for a service account. The token inherits the service account's scopes (no scopes in the request).

Auth: Session

ParamTypeInRequiredDescription
idstringpathYesService account ID
namestringbodyYesToken name (max 64 chars)
expires_instringbodyNo"30d", "90d", "365d", or "never"

Example request:

curl -X POST https://auth.cloud.eddisonso.com/api/service-accounts/xK9mPq/tokens \
-H "Authorization: Bearer eyJhbGci..." \
-H "Content-Type: application/json" \
-d '{"name": "production", "expires_in": "365d"}'

Response:

{
"id": "tK3nAb",
"name": "production",
"expires_at": 1743760000,
"created_at": 1712224000,
"last_used_at": 0,
"token": "ecloud_eyJhbGci..."
}

The token field is only returned on creation. Store it — it cannot be retrieved later.


GET /api/service-accounts/{id}/tokens

List all tokens for a service account.

Auth: Session

ParamTypeInRequiredDescription
idstringpathYesService account ID

Example request:

curl https://auth.cloud.eddisonso.com/api/service-accounts/xK9mPq/tokens \
-H "Authorization: Bearer eyJhbGci..."

Response:

[
{
"id": "tK3nAb",
"name": "production",
"expires_at": 1743760000,
"created_at": 1712224000,
"last_used_at": 1712300000
}
]