Skip to content

Allow2/Allow2ruby-service

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Allow2 Ruby Service SDK v2

Gem Version Ruby versions CI

Official Allow2 Parental Freedom Service SDK for Ruby -- for web services with user accounts (Rails apps, Sinatra, etc.).

This is a Service SDK -- it runs on your web server, not on a child's device. Following industry standard practice (Stripe, Firebase, Auth0), Allow2 maintains separate Device and Service SDKs. It handles OAuth2 pairing, permission checking, all 3 request types, voice codes, and feedback via the Allow2 Service API.

Gem allow2-service
Targets Ruby 3.0+
Dependencies None (stdlib only: net/http, json, openssl)
Language Ruby

Requirements

  • Ruby 3.0 or later
  • No external gems required (uses stdlib net/http, json, openssl)

Installation

gem install allow2-service

Or add to your Gemfile:

gem "allow2-service"

Quick Start

  1. Register your application at developer.allow2.com and note your client_id and client_secret.

  2. Create a Client and wire up the OAuth2 flow:

require "allow2_service"

allow2 = Allow2Service::Client.new(
  client_id: "YOUR_SERVICE_TOKEN",
  client_secret: "YOUR_SERVICE_SECRET",
  token_storage: Allow2Service::Storage::FileTokenStorage.new("/var/lib/allow2/tokens.json"),
  cache: Allow2Service::Cache::FileCache.new("/tmp/allow2-cache"),
)

# Step 1: Redirect user to Allow2 for pairing
authorize_url = allow2.get_authorize_url(
  user_id: current_user_id,
  redirect_uri: "https://yourapp.com/allow2/callback",
  state: csrf_token,
)
redirect_to authorize_url

# Step 2: Handle the callback
tokens = allow2.exchange_code(
  user_id: current_user_id,
  code: params[:code],
  redirect_uri: "https://yourapp.com/allow2/callback",
)

# Step 3: Check permissions on every request
result = allow2.check(current_user_id, [1, 3]) # Internet + Gaming

if !result.allowed
  # Show block page
else
  remaining = result.remaining_seconds(1)
  # Proceed normally, optionally showing countdown
end

Key Concept: One Account = One Child

The Service API links a specific user account on your site to exactly one Allow2 child. There is no child selector -- the identity is established at OAuth2 pairing time when the parent selects which child this account belongs to.

This means:

  • Each user account on your site maps to one Allow2 child
  • The parent performs pairing once per account, selecting the child
  • All subsequent permission checks for that account apply to that child automatically
  • Parent/admin accounts can be excluded from checking entirely

OAuth2 Flow

Step 1: Authorization

Redirect the user to Allow2 so their parent can pair the account:

state = SecureRandom.hex(16)
session[:allow2_state] = state

authorize_url = allow2.get_authorize_url(
  user_id: current_user_id,
  redirect_uri: "https://yourapp.com/callback",
  state: state,
)
redirect_to authorize_url

Step 2: Code Exchange

Handle the OAuth2 callback:

raise "Invalid state" unless params[:state] == session[:allow2_state]

tokens = allow2.exchange_code(
  user_id: current_user_id,
  code: params[:code],
  redirect_uri: "https://yourapp.com/callback",
)
# Tokens are stored automatically via the configured token_storage

Step 3: Token Refresh

Tokens expire. The SDK handles refresh automatically -- when you call check or any other method that requires authentication, the SDK detects expired tokens and refreshes them transparently, persisting the updated tokens via your token storage.

Permission Checking

Check permissions on every page load or API request:

# Simple format -- flat array of activity IDs (auto-expanded with log: true)
result = allow2.check(user_id, [1, 3, 8]) # Internet + Gaming + Screen Time

# Full format -- explicit log flags
result = allow2.check(user_id, [
  { "id" => 1, "log" => true },  # Internet
  { "id" => 8, "log" => true },  # Screen Time
], timezone: "Australia/Sydney")

if !result.allowed
  puts "Blocked! Day type: #{result.today_day_type.name}"
  result.activities.each do |activity|
    if activity.banned
      puts "#{activity.name} is banned"
    elsif !activity.time_block_allowed
      puts "#{activity.name} outside allowed hours"
    end
  end
else
  remaining = result.remaining_seconds(1)
  # Optionally show countdown in the UI
end

Convenience Check

# Returns true only if ALL specified activities are allowed
allowed = allow2.allowed?(user_id, [1, 3])

Caching

The SDK caches check results internally using your configured cache. The default TTL is 60 seconds and can be overridden via the constructor:

allow2 = Allow2Service::Client.new(
  client_id: "YOUR_TOKEN",
  client_secret: "YOUR_SECRET",
  token_storage: storage,
  cache: cache,
  cache_ttl: 30, # cache for 30 seconds
)

Requests

Children can request changes directly from your site. There are three types of request, and the philosophy is simple: the child drives the configuration, the parent just approves or denies.

More Time

request = allow2.request_more_time(
  user_id: user_id,
  activity_id: 3,       # Gaming
  minutes: 30,
  message: "Almost done with this level!",
)

puts "Request ID: #{request.request_id}"

# Poll for parent response
status = allow2.get_request_status(request.request_id, request.status_secret)

case status
when "approved" then puts "Approved!"
when "denied"   then puts "Request denied."
else                 puts "Still waiting..."
end

Day Type Change

request = allow2.request_day_type_change(
  user_id: user_id,
  day_type_id: 2,        # Weekend
  message: "We have a day off school today.",
)

Ban Lift

request = allow2.request_ban_lift(
  user_id: user_id,
  activity_id: 6,       # Social Media
  message: "I finished all my homework. Can the ban be lifted?",
)

Voice Codes (Offline Approval)

Even though the child is on a website (online), the parent may have no internet -- perhaps they are at work with no signal, or their phone is flat. Voice codes let the parent approve a request by reading a short numeric code over the phone or in person.

Generate a Challenge

pair = allow2.generate_voice_challenge(
  secret: pairing_secret,
  type: Allow2Service::Models::RequestType::MORE_TIME,
  activity_id: 3,       # Gaming
  minutes: 30,          # in 5-min increments
)

puts "Challenge code: #{pair.challenge}"
puts "Read this to your parent. Ask them for the response code."

Verify the Response

valid = allow2.verify_voice_response(
  secret: pairing_secret,
  challenge: pair.challenge,
  response: parent_response_code,
)

if valid
  puts "Approved! Extra time granted."
else
  puts "Invalid code. Please try again."
end

The codes use HMAC-SHA256 challenge-response, date-bound (expires at midnight). The format is compact enough to read over a phone call: a spaced challenge and a 6-digit response.

Feedback

Let users submit bug reports and feature requests directly to you, the developer:

# Submit feedback -- returns the discussion ID
discussion_id = allow2.submit_feedback(
  user_id: user_id,
  category: Allow2Service::Models::FeedbackCategory::BUG,
  message: "The block page appears even when I have time remaining.",
)

# Load feedback threads
threads = allow2.load_feedback(user_id)

# Reply to a thread
allow2.reply_to_feedback(user_id, discussion_id, "This happens every Tuesday.")

Architecture

Module Purpose
Client Main entry point, orchestrates all operations
OAuth2Manager OAuth2 authorize, code exchange, token refresh
PermissionChecker Permission checks with caching
RequestManager All 3 request types with temp token + status polling
VoiceCode HMAC-SHA256 challenge-response for offline approval
FeedbackManager Submit, load, and reply to feedback threads

Models

Model Purpose
CheckResult Parsed permission check response with per-activity status
Activity Single activity's allowed/blocked state and remaining time
DayType Current and upcoming day type information
OAuthTokens Access token, refresh token, expiry
RequestResult Request ID, status secret, and status with helper methods
VoiceCodePair Challenge and expected response pair
RequestType Constants: MORE_TIME, DAY_TYPE_CHANGE, BAN_LIFT
FeedbackCategory Constants: BUG, FEATURE_REQUEST, NOT_WORKING, OTHER

Errors

Error When
Allow2Error Base error for all SDK errors
ApiError HTTP or API-level errors
TokenExpiredError Token refresh failed (re-pairing needed)
UnpairedError No valid tokens for this user (401/403 from API)

Token Storage

The SDK persists OAuth2 tokens automatically via the token storage you provide at construction. Two built-in adapters are included.

FileTokenStorage

token_storage = Allow2Service::Storage::FileTokenStorage.new("/var/lib/allow2/tokens.json")

MemoryTokenStorage (development/testing)

token_storage = Allow2Service::Storage::MemoryTokenStorage.new

Custom Storage (Rails, etc.)

Implement the token storage duck type to integrate with your framework:

class ActiveRecordTokenStorage
  def store(user_id, tokens)
    Allow2Token.upsert({
      user_id: user_id,
      access_token: tokens.access_token,
      refresh_token: tokens.refresh_token,
      expires_at: tokens.expires_at,
    }, unique_by: :user_id)
  end

  def retrieve(user_id)
    record = Allow2Token.find_by(user_id: user_id)
    return nil unless record

    Allow2Service::Models::OAuthTokens.new(
      access_token: record.access_token,
      refresh_token: record.refresh_token,
      expires_at: record.expires_at,
    )
  end

  def delete(user_id)
    Allow2Token.where(user_id: user_id).delete_all
  end

  def exists?(user_id)
    Allow2Token.exists?(user_id: user_id)
  end
end

Device Operational Lifecycle

The Allow2 Service API follows a 7-step lifecycle, adapted for server-side web applications:

  1. Pairing (one-time) -- OAuth2 flow redirects to Allow2 where the parent selects which child this account belongs to. Tokens are stored per user in your database.

  2. Child Identification (automatic) -- one account = one child. The child's identity is established at pairing time and encoded in the OAuth2 tokens. No child selector is needed.

  3. Parent Access -- admin or parent accounts on your site are simply excluded from Allow2 checking. No special Allow2 flow is needed.

  4. Permission Checks (continuous) -- check on every page load or API request, server-side. Pass log: true to record usage. The response includes remaining time, daily limits, time blocks, day types, and bans.

  5. Warnings & Countdowns -- the server calculates remaining time from the check result. Your frontend displays countdowns and warnings as appropriate (e.g. "5 minutes remaining").

  6. Requests -- child requests changes (more time, day type change, ban lift) from your site. The parent approves or denies from their Allow2 app. For parents without internet, voice codes provide offline approval.

  7. Feedback -- bug reports and feature requests are sent directly to you, the developer, via the Allow2 feedback system. This gives you a built-in support channel without building one yourself.

Offline Operation

"Offline" for a web app sounds contradictory -- but the approval channel can be offline even when the child's browser is online.

When the parent has no internet

The child is on your website (online), but their parent's phone may have no signal. Voice codes solve this:

  1. Child clicks "Request More Time" on your site
  2. Your server generates a spaced challenge code
  3. Child reads the code to their parent (phone call, in person)
  4. Parent enters it into their Allow2 app (works offline) and reads back the 6-digit response
  5. Child enters the response on your site
  6. Your server verifies the HMAC-SHA256 response and grants the time

When your server can't reach Allow2

If the Allow2 API is temporarily unreachable:

  • Cache the last check result -- continue enforcing the last known permissions for a short grace period
  • Deny by default -- after the grace period, block access to prevent bypass
  • Queue requests -- store request attempts and replay them when connectivity resumes

License

Copyright 2017-2026 Allow2 Pty Ltd. All rights reserved.

See LICENSE for details.

About

Allow2 Ruby Service SDK for OpenSource Parental Freedom

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors