ask-auth

Credential resolution for the ask-rb ecosystem. A single API for resolving credentials across all tools and service gems. Zero external dependencies.

gem "ask-auth"

Quick Start

require "ask-auth"

# Simple — works everywhere, no config needed
token = Ask::Auth.resolve(:github_token)
# => "ghp_abc123..."

# With a user context (for per-user providers like Database)
token = Ask::Auth.resolve(:openai_api_key, user: current_user)

Resolution Chain

By default, resolve walks providers in order and returns the first match:

Order Provider Source
1 Env Environment variables
2 File ~/.ask/credentials.yml
3 RailsCredentials Rails.application.credentials
4 Database ActiveRecord-backed token storage
5 OAuth Interactive PKCE flow (deferred)

Configuration

Ask::Auth.configure do |c|
  c.providers = [
    Ask::Auth::Providers::Env.new,
    Ask::Auth::Providers::File.new(path: "~/.myapp/creds.yml"),
    Ask::Auth::Providers::Database.new(model: AccessToken)
  ]
end

Once configured, the configuration is frozen and thread-safe.

Providers

Env

Ask::Auth.resolve(:github_token)
# Checks: ENV["GITHUB_TOKEN"] → ENV["GITHUBTOKEN"] → ENV["github_token"]

No configuration needed.

File

# ~/.ask/credentials.yml
github_token: ghp_abc123...
openai_api_key: sk-...
Ask::Auth::Providers::File.new
Ask::Auth::Providers::File.new(path: "~/.custom/credentials.yml")

File created with 0600 permissions on write.

RailsCredentials

Ask::Auth.resolve(:github_token)
# Looks up: Rails.application.credentials.github.token

Converts snake_case to dot-separated paths. Safely returns nil when Rails is not loaded.

Database

Expects a model with user_id, name, token, expires_at, refresh_token.

# app/models/credential.rb
class Credential < ApplicationRecord
  belongs_to :user
end

# config/initializers/auth.rb
Ask::Auth.configure do |c|
  c.providers = [
    Ask::Auth::Providers::Database.new
  ]
end

Automatically calls refresh! when a token has expired and a refresh token is available.

OAuth

provider = Ask::Auth::Providers::OAuth.new(
  client_id: "your-client-id",
  authorize_url: "https://provider.com/oauth/authorize",
  token_url: "https://provider.com/oauth/token"
)

# Step 1: Authorization URL
url = provider.authorize_url(user: current_user)
# Step 2: Exchange code for token
provider.authorize!(user: current_user, code: params[:code])

Custom Providers

Ask::Auth.configure do |c|
  c.providers = [
    Ask::Auth::Providers::Env.new,
    ->(name, user: nil) { user&.api_key_for(name) }
  ]
end

Error Handling

Ask::Auth::MissingCredential    # All providers returned nil
Ask::Auth::InvalidCredential    # Token rejected at usage time

Implementation

  • Zero runtime dependencies — uses only Ruby stdlib (yaml, fileutils, openssl)
  • Thread-safe — configuration frozen after configure
  • Blank normalization — empty/whitespace values treated as nil
  • No other ask-rb gems required — independent of the tool/agent stack

This site uses Just the Docs, a documentation theme for Jekyll.