Skip to content

Allow2/Allow2java-service

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Allow2 Java Service SDK v2

CI Maven Central

Developer Resources -- The Allow2 MCP Server provides comprehensive API documentation, integration guides, architecture overviews, and interactive examples. Connect it to your AI coding assistant for the best development experience. Start there.

Official Allow2 Parental Freedom Service SDK for Java -- for web services with user accounts (Spring Boot, Jakarta EE, etc.).

This is a Service SDK -- it runs on your web server, not on a child's device. If you are building a device or product owned by a family (a game, smart device, desktop app, etc.), you need the Device SDK instead.

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.

Group com.allow2
Artifact allow2-service
Targets Java 11+
HTTP java.net.http.HttpClient (no external deps)

Requirements

  • Java 11 or later
  • No external HTTP dependencies (uses java.net.http.HttpClient)

Installation

Gradle

implementation 'com.allow2:allow2-service:2.0.0-alpha.1'

Maven

<dependency>
    <groupId>com.allow2</groupId>
    <artifactId>allow2-service</artifactId>
    <version>2.0.0-alpha.1</version>
</dependency>

Quick Start

  1. Register your application at developer.allow2.com and note your clientId and clientSecret.

  2. Create an Allow2Client and wire up the OAuth2 flow:

import com.allow2.service.Allow2Client;
import com.allow2.service.storage.FileTokenStorage;
import com.allow2.service.cache.MemoryCache;
import com.allow2.service.models.CheckResult;

Allow2Client allow2 = new Allow2Client.Builder("YOUR_SERVICE_TOKEN", "YOUR_SERVICE_SECRET")
    .tokenStorage(new FileTokenStorage("/var/lib/allow2/tokens.json"))
    .cache(new MemoryCache())
    .build();

// Step 1: Redirect user to Allow2 for pairing
String authorizeUrl = allow2.getAuthorizeUrl(
    currentUserId,
    "https://yourapp.com/allow2/callback",
    csrfToken
);
// redirect to authorizeUrl

// Step 2: Handle the callback
allow2.exchangeCode(currentUserId, code, "https://yourapp.com/allow2/callback");

// Step 3: Check permissions on every request
CheckResult result = allow2.check(currentUserId, Arrays.asList(1, 3)); // Internet + Gaming

if (!result.isAllowed()) {
    // Show block page
} else {
    int remaining = result.getRemainingSeconds(1);
    // Proceed normally, optionally showing countdown
}

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:

String state = UUID.randomUUID().toString();
session.setAttribute("allow2_state", state);

String authorizeUrl = allow2.getAuthorizeUrl(currentUserId, "https://yourapp.com/callback", state);
// redirect to authorizeUrl

Step 2: Code Exchange

Handle the OAuth2 callback:

if (!state.equals(session.getAttribute("allow2_state"))) {
    throw new SecurityException("Invalid state parameter");
}

OAuthTokens tokens = allow2.exchangeCode(currentUserId, code, "https://yourapp.com/callback");
// Tokens are stored automatically via the configured TokenStorage

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 TokenStorageInterface.

Permission Checking

Check permissions on every page load or API request:

// Simple format -- list of activity IDs (auto-expanded with log: true)
CheckResult result = allow2.check(userId, Arrays.asList(1, 3, 8)); // Internet + Gaming + Screen Time

if (!result.isAllowed()) {
    System.out.println("Blocked! Day type: " + result.getTodayDayType().getName());
    for (Activity activity : result.getActivities()) {
        if (activity.isBanned()) {
            System.out.println(activity.getName() + " is banned");
        } else if (!activity.isTimeBlockAllowed()) {
            System.out.println(activity.getName() + " outside allowed hours");
        }
    }
} else {
    int remaining = result.getRemainingSeconds(1);
    // Optionally show countdown in the UI
}

Convenience Check

// Returns true only if ALL specified activities are allowed
boolean allowed = allow2.isAllowed(userId, Arrays.asList(1, 3));

Caching

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

Allow2Client allow2 = new Allow2Client.Builder("YOUR_TOKEN", "YOUR_SECRET")
    .tokenStorage(storage)
    .cache(cache)
    .cacheTtl(30) // cache for 30 seconds
    .build();

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

RequestResult request = allow2.requestMoreTime(userId, 3, 30, "Almost done with this level!");

System.out.println("Request ID: " + request.getRequestId());

// Poll for parent response
String status = allow2.getRequestStatus(request.getRequestId(), request.getStatusSecret());

if ("approved".equals(status)) {
    System.out.println("Approved!");
} else if ("denied".equals(status)) {
    System.out.println("Request denied.");
} else {
    System.out.println("Still waiting...");
}

Day Type Change

RequestResult request = allow2.requestDayTypeChange(userId, 2, "We have a day off school today.");

Ban Lift

RequestResult request = allow2.requestBanLift(userId, 6, "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

import com.allow2.service.models.RequestType;

VoiceCodePair pair = allow2.generateVoiceChallenge(
    pairingSecret,
    RequestType.MORE_TIME,
    3,       // Gaming
    30       // in 5-min increments
);

System.out.println("Challenge code: " + pair.getChallenge());
System.out.println("Read this to your parent. Ask them for the response code.");

Verify the Response

boolean isValid = allow2.verifyVoiceResponse(pairingSecret, pair.getChallenge(), parentResponseCode);

if (isValid) {
    System.out.println("Approved! Extra time granted.");
} else {
    System.out.println("Invalid code. Please try again.");
}

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:

import com.allow2.service.models.FeedbackCategory;

// Submit feedback -- returns the discussion ID
String discussionId = allow2.submitFeedback(userId, FeedbackCategory.BUG,
    "The block page appears even when I have time remaining.");

// Load feedback threads
List<Map<String, Object>> threads = allow2.loadFeedback(userId);

// Reply to a thread
allow2.replyToFeedback(userId, discussionId, "This happens every Tuesday.");

Architecture

Module Purpose
Allow2Client 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 Enum: MORE_TIME, DAY_TYPE_CHANGE, BAN_LIFT
FeedbackCategory Enum: BUG, FEATURE_REQUEST, NOT_WORKING, OTHER

Exceptions

Exception When
Allow2Exception Base exception for all SDK errors
ApiException HTTP or API-level errors
TokenExpiredException Token refresh failed (re-pairing needed)
UnpairedException No valid tokens for this user (401/403 from API)

Token Storage

The SDK persists OAuth2 tokens automatically via the TokenStorageInterface you provide at construction.

MemoryTokenStorage (testing)

import com.allow2.service.storage.MemoryTokenStorage;

TokenStorageInterface tokenStorage = new MemoryTokenStorage();

FileTokenStorage (development)

import com.allow2.service.storage.FileTokenStorage;

TokenStorageInterface tokenStorage = new FileTokenStorage("/var/lib/allow2/tokens.json");

Custom Storage (Spring Boot, etc.)

Implement TokenStorageInterface to integrate with your framework:

import com.allow2.service.TokenStorageInterface;
import com.allow2.service.models.OAuthTokens;

public class JpaTokenStorage implements TokenStorageInterface {

    private final TokenRepository repository;

    public JpaTokenStorage(TokenRepository repository) {
        this.repository = repository;
    }

    @Override
    public void store(String userId, OAuthTokens tokens) {
        repository.save(new TokenEntity(userId, tokens.getAccessToken(),
            tokens.getRefreshToken(), tokens.getExpiresAt()));
    }

    @Override
    public OAuthTokens retrieve(String userId) {
        return repository.findById(userId)
            .map(e -> new OAuthTokens(e.getAccessToken(), e.getRefreshToken(), e.getExpiresAt()))
            .orElse(null);
    }

    @Override
    public void delete(String userId) {
        repository.deleteById(userId);
    }

    @Override
    public boolean exists(String userId) {
        return repository.existsById(userId);
    }
}

License

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

See LICENSE for details.

About

Allow2 Java Service SDK for OpenSource Parental Freedom

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors