npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@autonomous-ai/auth-sdk

v1.0.12

Published

Client SDK for auth-service SSO flow with OAuth2 PKCE

Readme

@autonomous-ai/auth-sdk

Client SDK for the auth-service SSO flow — OAuth2 Authorization Code + PKCE (S256), with first-class React bindings.

  • Zero runtime dependencies — React is an optional peer dependency
  • ESM only, ships TypeScript types
  • Automatic token refresh before expiry
  • Post-login redirect via nextUrl, no server-side changes needed

Installation

npm install @autonomous-ai/auth-sdk

React is optional — only needed if you import @autonomous-ai/auth-sdk/react:

npm install react   # >= 18

Local development

npm install && npm run build
npm link                       # in this repo
npm link @autonomous-ai/auth-sdk   # in the consuming app

Entry points

| Import path | Contents | | ------------------------------- | ---------------------------------------------------- | | @autonomous-ai/auth-sdk | AuthClient, TokenManager, PKCE helpers, all types | | @autonomous-ai/auth-sdk/react | AuthProvider, useAuth, useUser, useAuthCallback |

Quick Start (React)

1. Wrap your app with AuthProvider

Define authConfig outside the component (or memoize it) — a new object identity on every render re-triggers the provider's effects.

import { AuthProvider } from "@autonomous-ai/auth-sdk/react";

const authConfig = {
  ssoUrl: "https://sso.example.com",
  clientId: "my-app",
  redirectUri: "https://app.example.com/callback",
  scope: "openid profile email",
};

function App() {
  return (
    <AuthProvider config={authConfig}>
      <YourApp />
    </AuthProvider>
  );
}

2. Use the hooks

import { useAuth, useUser } from "@autonomous-ai/auth-sdk/react";

function LoginButton() {
  const { isAuthenticated, login, logout, isLoading } = useAuth();

  if (isLoading) return <div>Loading...</div>;

  if (isAuthenticated) {
    return <button onClick={() => logout()}>Logout</button>;
  }

  return <button onClick={() => login()}>Login with SSO</button>;
}

function UserProfile() {
  const user = useUser();

  if (!user) return null;

  return <p>Welcome, {user.fullName || user.email}!</p>;
}

3. Handle the OAuth2 callback

Mount this on the route you registered as redirectUri. The hook reads code / state from the URL, exchanges them for tokens, and is guarded against React StrictMode double-invocation.

import { useEffect } from "react";
import { useAuthCallback, useAuth } from "@autonomous-ai/auth-sdk/react";
import { useNavigate } from "react-router-dom";

function CallbackPage() {
  const navigate = useNavigate();
  const { refreshAuthState } = useAuth();
  const { isLoading, error, success, nextUrl } = useAuthCallback(authConfig);

  useEffect(() => {
    if (success) {
      refreshAuthState(); // re-read tokens written by the callback
      navigate(nextUrl || "/");
    }
  }, [success, nextUrl, navigate, refreshAuthState]);

  if (isLoading) return <div>Processing login...</div>;
  if (error) return <div>Login failed: {error}</div>;

  return null;
}

4. Post-login redirect (nextUrl)

Pass nextUrl to login() to send the user back to the page they started from. It is stored in sessionStorage before the SSO redirect and returned by useAuthCallback after a successful exchange.

import { useEffect } from "react";
import { useAuth } from "@autonomous-ai/auth-sdk/react";
import { useLocation } from "react-router-dom";

function ProtectedPage() {
  const location = useLocation();
  const { isAuthenticated, isLoading, login } = useAuth();

  useEffect(() => {
    if (!isLoading && !isAuthenticated) {
      login({
        nextUrl: `${location.pathname}${location.search}${location.hash}`,
      });
    }
  }, [isAuthenticated, isLoading, login, location]);

  if (isLoading || !isAuthenticated) {
    return <div>Redirecting to login...</div>;
  }

  return <div>Protected content</div>;
}

Vanilla JavaScript / TypeScript

import { AuthClient } from "@autonomous-ai/auth-sdk";

const client = new AuthClient({
  ssoUrl: "https://sso.example.com",
  clientId: "my-app",
  redirectUri: "https://app.example.com/callback",
  scope: "openid profile email",
});

// Start login (redirects the browser to SSO)
await client.authorize({ nextUrl: "/dashboard" });

// On the callback page
const params = new URLSearchParams(window.location.search);
const result = await client.handleCallback(
  params.get("code")!,
  params.get("state")!
);

if (result.success) {
  if (result.nextUrl) window.location.href = result.nextUrl;
} else {
  console.error(result.error);
}

// Auth status
if (client.isAuthenticated()) {
  console.log("User:", client.getUser());
}

// Access token for API calls — refreshes automatically if expired
const token = await client.getValidAccessToken();
await fetch("/api/me", { headers: { Authorization: `Bearer ${token}` } });

// Logout (clears tokens and redirects to SSO logout)
client.logout();

API Reference

AuthClient

| Method | Returns | Description | | ------------------------------- | -------------------------- | ---------------------------------------- | | authorize(options?) | Promise<void> | Start OAuth2 login flow (redirects away) | | handleCallback(code, state) | Promise<CallbackResult> | Exchange authorization code for tokens | | refreshToken() | Promise<TokenResponse> | Refresh the access token | | logout(redirectUri?) | Promise<void> | Revoke this sign-in, clear tokens and redirect to SSO logout | | isAuthenticated() | boolean | Whether a valid session exists | | getAccessToken() | string \| null | Current access token | | getRefreshToken() | string \| null | Current refresh token | | getUser() | User \| null | User decoded from the JWT | | isTokenExpired(buffer = 60) | boolean | Expiry check with a seconds buffer | | getValidAccessToken() | Promise<string> | Token, refreshed if expired | | clearTokens() | void | Clear tokens without an SSO logout |

createAuthClient(config) is a factory shorthand for new AuthClient(config).

Login source tracking

  • login({ entryPoint: 'header' }) / authorize({ entryPoint }) tells auth-service which UI placement started the sign-in.
  • After the callback, result.tokens.first_time_in_app is true the first time this user gets a session in your app (use it for onboarding). result.tokens.first_time is true for their first sign-in anywhere in the Autonomous ecosystem.

Your own "Continue with Google" button

login({ provider: 'google' }) // or 'apple'; authorize({ provider }) on AuthClient

When the SSO would show its login page, it goes straight to that provider and comes back signed in — no second click on the SSO page. If the browser already has an SSO session, that session is used and the hint does nothing. If the provider reports a cancel or a failure, the user lands on the ordinary SSO login page; pressing Back at the provider returns to your app. Needs auth-service v1.0.77 or later; an older SSO ignores the parameter. The Node client takes the same option: signIn({ provider: 'google' }).

AuthConfig

| Option | Type | Default | Description | | ------------- | -------------- | ---------------- | ------------------------------------ | | ssoUrl | string | required | SSO server base URL | | clientId | string | required | OAuth2 client ID | | redirectUri | string | required | Callback URL registered with the SSO | | scope | string? | - | Space-separated scopes | | storage | TokenStorage? | localStorage | Custom token storage |

AuthorizeOptions

| Option | Type | Description | | ----------- | --------------------------------------- | ----------------------------------------- | | prompt | 'select_account' \| 'none' \| 'login' | Force account selection or silent auth | | loginHint | string | Pre-fill email for login | | nextUrl | string | URL to redirect to after successful login | | entryPoint | string | UI placement that started the sign-in | | provider | 'google' \| 'apple' | Go straight to that provider (see above) |

User

Parsed from the JWT's ext_info claim.

| Field | Type | Description | | -------------------- | ----------- | ------------------------- | | id | string | User ID | | email | string | User email | | fullName | string? | Full name | | code | string? | User code | | roles | string[]? | User roles | | scope | string? | Granted scope | | companyDomain | string? | Company domain | | companyDomainType | string? | Company domain type | | isEppUser | boolean? | Employee Purchase Program | | vendorId | string? | Vendor ID | | vendorCode | string? | Vendor code | | vendorName | string? | Vendor name | | referralCode | string? | Referral code |

CallbackResult

| Field | Type | Description | | --------- | ----------------- | -------------------------------------- | | success | boolean | Whether the code exchange succeeded | | tokens | TokenResponse? | Tokens returned by auth-service | | error | string? | Error message when success is false | | nextUrl | string? | Post-login redirect target |

React hooks

| Hook | Returns | Description | | ------------------------- | ----------------------------------------------------------- | ---------------------- | | useAuth() | AuthContextValue | Auth state and actions | | useUser() | User \| null | Current user | | useAuthCallback(config) | { isLoading, error, success, result, nextUrl } | Handle OAuth2 callback |

useAuth() returns isAuthenticated, isLoading, user, error, plus login(), logout(), getAccessToken(), refreshToken() and refreshAuthState().

useAuthCallback creates its own AuthClient and works outside AuthProvider — but call refreshAuthState() afterwards so the provider picks up the new tokens.

AuthProvider props

| Prop | Type | Default | Description | | --------------- | ------------ | -------- | -------------------------------- | | config | AuthConfig | required | Auth configuration | | autoRefresh | boolean | true | Auto-refresh tokens | | refreshBuffer | number | 60 | Seconds before expiry to refresh | | onAuthChange | function | - | Called on auth state change |

Custom storage

Tokens go to localStorage by default. Supply any object implementing TokenStorage to change that — e.g. sessionStorage so the session dies with the tab:

const client = new AuthClient({
  ...config,
  storage: {
    getItem: (key) => sessionStorage.getItem(key),
    setItem: (key, value) => sessionStorage.setItem(key, value),
    removeItem: (key) => sessionStorage.removeItem(key),
  },
});

The PKCE verifier and state always use sessionStorage, regardless of this setting.

Node (CLIs)

npm install @autonomous-ai/auth-sdk
import { createNodeAuthClient } from '@autonomous-ai/auth-sdk/node'

const auth = createNodeAuthClient({
  ssoUrl: 'https://auth.autonomous.ai',
  clientId: 'my-cli',
  appName: 'my-cli', // ~/.config/my-cli/auth.json
})

await auth.signIn()                 // browser + 127.0.0.1, or pasted code over SSH
const token = await auth.getAccessToken() // refreshes when it is about to expire
await auth.logout()                 // revokes the sign-in and forgets it

signIn({ mode }) forces a flow: 'loopback' (browser on this machine) or 'manual' (paste the code the page shows). The default, 'auto', uses the pasted code over SSH, on a machine with no browser, or whenever the browser could not be opened.

Register both redirect URIs for the client: http://127.0.0.1/callback and https://<sso-domain>/oauth2/code.

getAccessToken() throws AuthSessionError: SIGNED_OUT means the sign-in was revoked or expired and the session has been cleared — run your login command again; UNAVAILABLE means the service could not be reached (or the local session file could not be read/locked) and the session was kept; NO_SESSION means nobody has signed in yet.

getSession() and isSignedIn() are synchronous and read only from memory, so they return null/false until the on-disk session has actually been loaded. signIn() and getAccessToken() both load it as a side effect, but if you need the answer before calling either of those — e.g. to decide whether to show a "sign in" prompt — call await auth.loadSession() first.

Requires Node 20 or newer. The session file is created 0600 in ~/.config/<appName>/ (%APPDATA%\<appName>\ on Windows).

Security

  • PKCE (S256) prevents authorization code interception
  • state parameter for CSRF protection, validated on callback
  • PKCE verifier kept in sessionStorage, never in localStorage
  • Tokens refreshed automatically before expiry (refreshBuffer)

License

MIT