Get the FREE Ultimate OpenClaw Setup Guide →

nylo-auth

npx machina-cli add skill nylo-core/claude-code/nylo-auth --openclaw
Files (1)
SKILL.md
10.3 KB

Nylo Authentication

Overview

Nylo v7 authentication is managed through the Auth class, which stores user session data securely and syncs it to Backpack (an in-memory key-value store) for fast synchronous access. It supports multiple named sessions, token management, and integrates with the router for authenticated routes.

When to Use

  • Implementing login/logout flows
  • Storing and retrieving auth tokens
  • Checking if a user is authenticated
  • Updating stored auth data (e.g., token refresh)
  • Managing multiple auth sessions (user, device, admin)
  • Setting up routes that require authentication
  • Generating unique device identifiers
  • When NOT to use: For general-purpose key-value storage unrelated to auth, use nylo-storage instead

Quick Reference

ActionCode
Authenticate userawait Auth.authenticate(data: user)
Get all auth dataAuth.data()
Get specific fieldAuth.data(field: 'token')
Check if authenticatedawait Auth.isAuthenticated()
Update auth dataawait Auth.set((data) { data['token'] = newToken; return data; })
Logoutawait Auth.logout()
Logout all sessionsawait Auth.logoutAll(sessions: ['device', 'admin'])
Sync to Backpackawait Auth.syncToBackpack()
Get device IDawait Auth.deviceId()
Set authenticated routerouter.add(HomePage.path).authenticatedRoute()
Navigate to auth routerouteToAuthenticatedRoute()

Auth.authenticate()

Stores user session data. Accepts a Map, a Model instance, or no data (timestamp only):

// Store a Map
await Auth.authenticate(data: {
  "token": "ey2sdm...",
  "userId": 123,
  "email": "user@example.com",
});

// Store a Model instance
User user = User(id: 123, email: "user@example.com", token: "ey2sdm...");
await Auth.authenticate(data: user);

// Authenticate with no data (stores timestamp only)
await Auth.authenticate();

// Named session
await Auth.authenticate(
  data: {"deviceToken": "abc123"},
  session: 'device',
);

Auth.data()

Retrieves stored auth data synchronously from Backpack:

// All auth data
dynamic userData = Auth.data();

// Specific field
String? token = Auth.data(field: 'token');
int? userId = Auth.data(field: 'userId');

// Named session
dynamic deviceData = Auth.data(session: 'device');
String? deviceToken = Auth.data(field: 'deviceToken', session: 'device');

Auth.isAuthenticated()

Checks if a user is currently authenticated:

bool isLoggedIn = await Auth.isAuthenticated();

// Check specific session
bool deviceRegistered = await Auth.isAuthenticated(session: 'device');
bool isAdmin = await Auth.isAuthenticated(session: 'admin');

Auth.set()

Updates or modifies existing auth data:

// Update specific field
await Auth.set((data) {
  data['token'] = 'new_token_value';
  return data;
});

// Add new fields
await Auth.set((data) {
  data['refreshToken'] = 'refresh_abc123';
  data['lastLogin'] = DateTime.now().toIso8601String();
  return data;
});

// Replace entire data
await Auth.set((data) => {
  'token': newToken,
  'userId': userId,
});

Auth.logout()

Removes authenticated user from default or named session:

// Default session
await Auth.logout();

// Named session
await Auth.logout(session: 'device');

// All sessions
await Auth.logoutAll(sessions: ['device', 'admin']);

Complete Login Flow

class _LoginPageState extends NyPage<LoginPage> {

  Future<void> handleLogin(String email, String password) async {
    // 1. Call API
    User? user = await api<AuthApiService>((request) =>
      request.login(email: email, password: password)
    );

    if (user == null) {
      showToastDanger(description: "Invalid credentials");
      return;
    }

    // 2. Store authenticated user
    await Auth.authenticate(data: user);

    // 3. Navigate to authenticated route (clears stack)
    routeToAuthenticatedRoute();
  }
}

Complete Logout Flow

Future<void> handleLogout() async {
  await Auth.logout();
  routeTo(LoginPage.path, navigationType: NavigationType.pushAndForgetAll);
}

Multiple Named Sessions

Nylo supports separate authentication contexts for different purposes:

// Default user session
await Auth.authenticate(data: {
  "token": "user_token_123",
  "userId": 42,
});

// Device session
await Auth.authenticate(
  data: {"deviceToken": "abc123"},
  session: 'device',
);

// Admin session
await Auth.authenticate(
  data: {"adminToken": "admin_xyz", "role": "superadmin"},
  session: 'admin',
);

// Access each session independently
String? userToken = Auth.data(field: 'token');            // default session
String? deviceToken = Auth.data(field: 'deviceToken', session: 'device');
String? adminRole = Auth.data(field: 'role', session: 'admin');

// Check each session
bool isLoggedIn = await Auth.isAuthenticated();
bool isDeviceRegistered = await Auth.isAuthenticated(session: 'device');
bool isAdmin = await Auth.isAuthenticated(session: 'admin');

// Logout specific session
await Auth.logout(session: 'device');

Token Storage and Backpack Sync

Auth data is stored securely (persistent storage) and automatically synced to Backpack (in-memory store) for fast synchronous reads. If you need to manually sync:

// Sync default session
await Auth.syncToBackpack();

// Sync specific session
await Auth.syncToBackpack(session: 'device');

// Sync all sessions
await Auth.syncAllToBackpack(sessions: ['device', 'admin']);

How it works:

  1. Auth.authenticate() writes data to secure persistent storage AND syncs to Backpack
  2. Auth.data() reads from Backpack (in-memory) for instant access
  3. Auth.set() updates both persistent storage and Backpack
  4. Auth.logout() removes from both stores

Token Refresh Pattern

Combine Auth with NyApiService's token refresh:

class ApiService extends NyApiService {
  @override
  Future<RequestHeaders> setAuthHeaders(RequestHeaders headers) async {
    String? token = Auth.data(field: 'token');
    if (token != null) {
      headers.addBearerToken(token);
    }
    return headers;
  }

  @override
  Future<bool> shouldRefreshToken() async {
    // Check if token is expired
    return false;
  }

  @override
  Future<void> refreshToken(Dio dio) async {
    dynamic response = (await dio.post(
      "https://example.com/refresh-token"
    )).data;

    // Update stored token
    await Auth.set((data) {
      data['token'] = response['token'];
      return data;
    });
  }
}

Device ID

Generate and persist a unique device identifier across sessions:

String deviceId = await Auth.deviceId();
// Example: "550e8400-e29b-41d4-a716-446655440000-1704067200000"

Register Device with Backend

Future<void> registerDevice() async {
  String deviceId = await Auth.deviceId();
  String? pushToken = await FirebaseMessaging.instance.getToken();

  await api<DeviceApiService>((request) =>
    request.register(deviceId: deviceId, pushToken: pushToken)
  );

  await Auth.authenticate(
    data: {"deviceId": deviceId, "pushToken": pushToken},
    session: 'device',
  );
}

Authenticated Routes

Set a route as the initial page for authenticated users in lib/routes/router.dart:

appRouter() => nyRoutes((router) {
  router.add(LoginPage.path).initialRoute();
  router.add(HomePage.path).authenticatedRoute();
  router.add(ProfilePage.path);
});

After authentication, navigate to the authenticated route:

await Auth.authenticate(data: user);
routeToAuthenticatedRoute();

Conditional Authenticated Route

router.add(HomePage.path).authenticatedRoute(
  when: () => hasCompletedSetup()
);

Auth Route Guard

Protect routes that require authentication:

class AuthRouteGuard extends NyRouteGuard {
  @override
  Future<GuardResult> onBefore(RouteContext context) async {
    bool isLoggedIn = await Auth.isAuthenticated();
    if (!isLoggedIn) {
      return redirect(LoginPage.path);
    }
    return next();
  }
}

// Apply to routes
router.add(ProfilePage.path, routeGuards: [AuthRouteGuard()]);

// Apply to route group
router.group(() => {
  "route_guards": [AuthRouteGuard()],
  "prefix": "/dashboard",
}, (router) {
  router.add(DashboardPage.path);
  router.add(SettingsPage.path);
});

Helper Functions

Nylo provides convenience functions that mirror Auth class methods:

// Authenticate
await authAuthenticate(data: user);
await authAuthenticate(data: device, session: 'device');

// Read data
dynamic userData = authData();
String? token = authData(field: 'token');
dynamic deviceData = authData(session: 'device');

// Update data
await authSet((data) {
  data['token'] = newToken;
  return data;
});

// Check auth state
bool isLoggedIn = await authIsAuthenticated();
bool deviceAuth = await authIsAuthenticated(session: 'device');

// Logout
await authLogout();
await authLogoutAll(sessions: ['device', 'admin']);

// Sync
await authSyncToBackpack();

// Device ID
String deviceId = await authDeviceId();

// Auth key
String key = authKey();

Common Mistakes

MistakeFix
Calling Auth.data() before Auth.authenticate()Always authenticate first; Auth.data() returns null if no session exists
Not awaiting Auth.authenticate()It is async; always use await to ensure data is stored before proceeding
Navigating before auth completesawait Auth.authenticate(data: user) must finish before calling routeToAuthenticatedRoute()
Forgetting to clear nav stack on logoutUse NavigationType.pushAndForgetAll or routeToInitial() after logout to prevent back navigation
Using Auth.data() in an API interceptor without null checkToken may be null if user is not authenticated; always check before adding to headers
Not setting up authenticatedRoute() in routerThe route must be registered with .authenticatedRoute() for routeToAuthenticatedRoute() to work
Storing large objects in AuthAuth is for credentials and tokens; store user profiles in Backpack or NyStorage separately
Confusing session namesNamed sessions are string-based; use consistent constants to avoid typos

Source

git clone https://github.com/nylo-core/claude-code/blob/main/skills/nylo-auth/SKILL.mdView on GitHub

Overview

Nylo Authentication in Nylo v7 securely stores user session data and syncs it to Backpack for fast access. It supports multiple named sessions (user, device, admin) and integrates with the router to protect authenticated routes.

How This Skill Works

Auth stores session data securely and exposes it via Auth.data(). It handles multiple named sessions, token management, and route protections by integrating with the Nylo router. You can authenticate with data, read tokens, check authentication status, update tokens with set, and log out when needed.

When to Use It

  • Implementing login/logout flows
  • Storing and retrieving auth tokens
  • Checking if a user is authenticated
  • Managing multiple auth sessions (user, device, admin)
  • Configuring routes that require authentication

Quick Start

  1. Step 1: After a successful login, call Auth.authenticate(data: userMap) to store the session
  2. Step 2: Use Auth.isAuthenticated() or Auth.data() to drive UI and access controls
  3. Step 3: Protect routes with router.add(HomePage.path).authenticatedRoute() and call Auth.logout() to end sessions

Best Practices

  • Use named sessions for different contexts to prevent token leakage between scopes
  • Store tokens and user data securely and rely on Backpack for fast access
  • Read data with Auth.data() or a specific field with Auth.data(field: 'token')
  • Guard routes by using router.add(...).authenticatedRoute()
  • Explicitly logout with Auth.logout() or Auth.logoutAll(...) when needed

Example Use Cases

  • Authenticate a user with a Map: await Auth.authenticate(data: { 'token': 'ey...', 'userId': 123, 'email': 'user@example.com' })
  • Get all auth data: dynamic userData = Auth.data()
  • Check authentication: bool isLoggedIn = await Auth.isAuthenticated()
  • Refresh token: await Auth.set((data) { data['token'] = newToken; return data; })
  • Logout all named sessions: await Auth.logoutAll(sessions: ['device', 'admin'])

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers