Skip to content

Authentication Methods

VSKI supports three main authentication methods:

  1. Email/Password - Traditional username and password authentication
  2. API Keys - For service-to-service authentication
  3. OAuth2 - Third-party identity providers (Google, GitHub, etc.)
from vski import VskiClient
async with VskiClient("http://127.0.0.1:3000") as client:
# Login with email and password (also sets the client token)
result = await client.auth.login("user@example.com", "password123")
print(result["token"]) # JWT token
print(result["user"]) # user record

The login method returns:

  • token: JWT token for authenticated requests
  • user: Full user record including email, name, etc.
client.set_token(result["token"])
# Then use the client normally
user = await client.auth.me()
user = await client.auth.me()
print(user["id"], user["email"], user.get("name"))
print(user.get("verified")) # email verification status
user = await client.auth.register({
"email": "newuser@example.com",
"password": "SecurePassword123!",
"passwordConfirm": "SecurePassword123!",
"name": "John Doe",
})

Note: Password hashing is handled automatically. The user is created in the configured default auth collection.

Email verification and password-reset endpoints are not exposed by the SDK yet; watch the changelog. The command line bootstrap path for the first account is the /installer page or POST /api/admins/init (see Getting Started).

Every deployment has an admin (superuser) account for administrative tasks:

# Login as admin (also sets the client token)
result = await client.admins.auth_with_password(
"admin@example.com", "password123"
)
# The admin client has access to settings and management endpoints
collections = await client.settings.collections.get_list()

From the command line: vski login stores the admin JWT in ~/.vski/config.json.

Important: Change the default admin password immediately in production!

API keys are ideal for service-to-service authentication or worker processes.

Terminal window
vski keys create worker --roles service # raw key printed exactly once
# Admin-only SDK call
api_key = await client.keys.generate({
"name": "Worker Service",
"enabled": True,
"roles": "service", # comma-separated: admin, service, workflows, plugin
})
print(api_key["key"]) # the actual API key (only shown once)
print(api_key["id"]) # reference ID for the key
client.set_api_key("vski_...") # sent as X-API-Key on every request
records = await client.collection("posts").get_list(1, 20)

img

VSKI supports OAuth2 for third-party identity providers.

methods = await client.auth.list_methods()
print(methods["oauth2"]["providers"])
# [
# {"name": "google", "displayName": "Google"},
# {"name": "github", "displayName": "GitHub"},
# ...other providers
# ]
# Exchange the authorization code (obtained from the provider's callback)
# for a VSKI JWT token
auth_data = await client.auth.auth_via_oauth2(
"google",
"authorization-code-from-callback",
"http://127.0.0.1:3000/auth/callback",
)
print(auth_data["token"])
print(auth_data["user"])

Note: The OAuth2 flow requires obtaining the authorization code separately from the OAuth provider. The auth_via_oauth2 method exchanges the code for a JWT token.

The SDK automatically includes the authentication token in request headers:

client.set_token("your-jwt-token")
# all subsequent requests include:
# Authorization: Bearer your-jwt-token
client.set_api_key("vski_...")
# all subsequent requests include:
# X-API-Key: vski_...

When using multiple databases, scope the client (or a sub-client) to a specific database; auth flows then resolve against that database:

client.set_db("my-database")
# or a database-scoped client that shares the connection
db_client = client.db("my-database")

Per-collection auth (an auth collection other than the default) goes through the collection itself:

result = await client.collection("_users").auth_with_password(
"user@example.com", "password123"
)
vski/default/collections.yaml
version: 1
database: default
collections:
_users:
type: auth
fields:
- {name: email, type: email, required: true}
- {name: password, type: text, required: true}
- {name: name, type: text}
- {name: avatar, type: url}

Auth collections automatically include:

  • id: Unique identifier
  • email: Email address
  • created: Creation timestamp
  • updated: Last update timestamp

Note: The collection’s fields define the additional attributes available on user records.

from vski import VskiError
try:
await client.auth.login("user@example.com", "wrong-password")
except VskiError as e:
if "Invalid credentials" in str(e):
print("Wrong email or password")
elif e.status_code == 401:
print("Unauthorized")
  1. Change default admin password: immediately after first setup
  2. Use HTTPS in production: always use secure connections
  3. Rotate API keys: regularly update API keys
  4. Use strong passwords: enforce password complexity requirements
  5. Enable email verification: verify user email addresses
  6. Implement rate limiting: prevent brute force attacks (built in for auth endpoints via AUTH_RATE_LIMIT_PER_HOUR)
  7. Store tokens securely: use secure storage for JWT tokens
  8. Set appropriate token expiration: balance security and user experience
  9. Use the principle of least privilege: give users only the access they need
result = await client.auth.refresh() # also sets the new token
client.set_token(result["token"])
# When a token expires, the SDK raises VskiError with status 401
from vski import VskiError
try:
await client.collection("posts").get_list(1, 20)
except VskiError as e:
if e.status_code == 401:
# token expired: re-authenticate
result = await client.auth.login(email, password)
client.set_token(result["token"])

VSKI integrates with collections to implement role-based access:

# a roles lookup collection
collections:
_roles:
type: base
fields:
- {name: name, type: text, required: true}
- {name: permissions, type: json}
# assign a role to a user
await client.collection("_users").update("user-id", {"role": "editor"})

Access is enforced by collection rules referencing the role (see Rules and Access Control):

collections:
posts:
rules:
create: "@request.auth.role = 'editor'"
list: "'editor' in @request.auth.roles OR published = true"
MethodEndpointDescription
POST/api/auth/loginLogin with email/password
POST/api/auth/registerRegister new user
POST/api/auth/refreshRefresh JWT token
GET/api/auth/methodsList auth providers
GET/api/auth/meGet current user
GET/api/auth/oauth2-loginOAuth2 code exchange
POST/api/admins/auth-with-passwordAdmin authentication
POST/api/admins/initInitialize first admin
GET/api/admins/has-adminsCheck if admins exist
GET/api/admins/meGet current admin
POST/api/keysGenerate API key
POST/api/collections/{collection}/auth-with-passwordCollection-specific auth