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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@superapp_men/token-provider

v1.0.7

Published

Secure token provider for SuperApp Partner Apps

Readme

@superapp_men/token-provider

Secure token management for SuperApp Partner Apps. Get authentication tokens from the SuperApp without accessing sensitive APIs directly.

Features

Secure - Partner Apps never handle authentication directly
Simple - Single function call to get tokens
TypeScript - Full type safety and IntelliSense
Cross-Platform - Works on Web, iOS, and Android
Zero Dependencies - Lightweight and efficient

Installation

npm install @superapp_men/token-provider

or

yarn add @superapp_men/token-provider

Basic Usage

Get Access Token

import { TokenProvider } from "@superapp_men/token-provider";

const provider = new TokenProvider();

// Get default access token
const token = await provider.getToken();

console.log(token.token); // "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
console.log(token.tokenType); // "Bearer"

Advanced Usage

With Event Listeners

const provider = new TokenProvider({
  debug: true,
});

// Listen to token events
provider.on("tokenReceived", ({ token }) => {
  console.log("Token received from SuperApp");
  console.log("Token:", token.token);
});

provider.on("error", ({ code, message }) => {
  console.error("Token error:", code, message);
});

provider.on("stateChange", ({ state, previousState }) => {
  console.log(`State: ${previousState} → ${state}`);
});

// Get token
const token = await provider.getToken();

Get User Information

const provider = new TokenProvider();

// Get user info
const userInfo = await provider.getUserInfo();

console.log(userInfo.id); // "user-123"
console.log(userInfo.firstName); // "Hishame"
console.log(userInfo.grade); // 5

Configuration

interface TokenProviderConfig {
  // Default timeout for requests (default: 5000ms)
  timeout?: number;

  // Enable debug logging (default: false)
  debug?: boolean;
}

Example Configuration

const provider = new TokenProvider({
  timeout: 10000, // 10 seconds
  debug: true, // Debug logging enabled
});

API Reference

TokenProvider Class

Constructor

new TokenProvider(config?: TokenProviderConfig)

Methods

getToken(options?: TokenRequestOptions): Promise<TokenResponse>

Get a token from the SuperApp. Each call requests a fresh token from the SuperApp. The package returns the token as-is; token validation should be performed in your partner app using JWKS.

Options:

interface TokenRequestOptions {
  type?: TokenType; // Token type (default: ACCESS)
  timeout?: number; // Request timeout
}

Returns:

interface TokenResponse {
  token: string; // The actual token
  tokenType: string; // "Bearer", etc.
}
getUserInfo(): Promise<UserInfo>

Get current user information.

Returns:

interface UserInfo {
  id: string;
  firstName?: string;
  grade?: number;
}
getState(): TokenProviderState

Get the current provider state.

Returns: 'idle' | 'requesting' | 'ready' | 'error'

on<T>(event: TokenEventType, callback: EventListener<T>): () => void

Add an event listener. Returns unsubscribe function.

off<T>(event: TokenEventType, callback: EventListener<T>): void

Remove an event listener.

removeAllListeners(event?: TokenEventType): void

Remove all event listeners.

setDebug(enabled: boolean): void

Enable or disable debug logging.

destroy(): void

Cleanup and destroy the provider.

Token Validation

The getToken() method returns a token from the SuperApp. Token validation should be performed in your partner app using JWKS (JSON Web Key Set).

Validation Steps (in your app)

  1. Get Token: Call getToken() to receive the token from SuperApp
  2. Fetch JWKS: Fetch public keys from https://supper-app.azurewebsites.net/.well-known/jwks.json
  3. Extract Key ID: Extract the kid (Key ID) from the JWT token header
  4. Match Key: Find the corresponding public key from the JWKS using the kid
  5. Validate Signature: Validate the token signature using the matched public key

JWKS Endpoint

The JWKS endpoint returns a JSON structure like:

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "alg": "RS256",
      "kid": "WUjXNSKJU69KU-8Ccc9wSKoGjVnVpx9SeoF_ih6TNyE",
      "n": "rTknkikAVBH3tWd8p_KaXKOqHx6wBWcbC6DQzoizLhPnZH1kHynEs3EKvN227okxTKfGdmtQgUrC-C-fk5LztMWYR428sao5TmEuhy-bvoEeReh4oDXUqsG7Vc1ilVtjGS6AVpxZtz9Cph4AonLebEyKWHFM9qsqAsmjZNh8L3rqo_uIzz1DVYFer3a1mckgxv1h_EHOFM7SBwiNL3qR4tvsTQNMdHxW49QNg70r69dYy1mbNpZnBBfP-A0bvFrWVnhY3JXEJ25DelvB7UvjlkY3UODuKnlY9fQCrm1nq_aUUYIJJsQRH3DI4N1h3mKYPafrS9iMYf4vi4u85EjESw",
      "e": "AQAB"
    }
  ]
}

Example: Validating Token (Recommended: Backend)

⚠️ Important: Token validation should be performed on your backend server, not in the client-side code. Client-side validation can be bypassed and should only be used for display purposes.

Backend Validation (Recommended)

// Backend API endpoint (Node.js/Express example)
import express from "express";
import jwt from "jsonwebtoken";
import jwksClient from "jwks-rsa";

const app = express();

const client = jwksClient({
  jwksUri: "https://supper-app.azurewebsites.net/.well-known/jwks.json",
});

function getKey(header, callback) {
  client.getSigningKey(header.kid, (err, key) => {
    const signingKey = key.publicKey || key.rsaPublicKey;
    callback(null, signingKey);
  });
}

app.post("/api/protected", (req, res) => {
  const token = req.headers.authorization?.replace("Bearer ", "");

  if (!token) {
    return res.status(401).json({ error: "No token provided" });
  }

  jwt.verify(token, getKey, (err, decoded) => {
    if (err) {
      return res
        .status(401)
        .json({ error: "Invalid token", details: err.message });
    }

    // Token is valid, proceed with request
    res.json({ message: "Access granted", user: decoded });
  });
});

Client-Side Usage

// Frontend: Get token and send to backend
import { TokenProvider } from "@superapp_men/token-provider";

const provider = new TokenProvider();

// Get token from SuperApp
const tokenResponse = await provider.getToken();

// Send token to your backend API
const response = await fetch("https://your-api.com/api/protected", {
  method: "POST",
  headers: {
    Authorization: `${tokenResponse.tokenType} ${tokenResponse.token}`,
  },
});

if (response.ok) {
  const data = await response.json();
  // Use the validated data
}

Events

tokenReceived

Fired when a token is received from the SuperApp.

provider.on("tokenReceived", ({ token }) => {
  console.log("Token:", token.token);
});

stateChange

Fired when the provider state changes.

provider.on("stateChange", ({ state, previousState }) => {
  console.log(`State: ${previousState} → ${state}`);
});

error

Fired when an error occurs.

provider.on("error", ({ code, message, details }) => {
  console.error(`Error [${code}]: ${message}`);
});

Complete Examples

React Hook

import { useState, useEffect } from "react";
import {
  TokenProvider,
  type TokenResponse,
} from "@superapp_men/token-provider";

export function useToken() {
  const [provider] = useState(() => new TokenProvider());
  const [token, setToken] = useState<TokenResponse | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const unsubscribe = provider.on("tokenReceived", ({ token }) => {
      setToken(token);
      setLoading(false);
    });

    const errorUnsub = provider.on("error", ({ message }) => {
      setError(message);
      setLoading(false);
    });

    return () => {
      unsubscribe();
      errorUnsub();
      provider.destroy();
    };
  }, [provider]);

  const getToken = async () => {
    setLoading(true);
    setError(null);
    try {
      const token = await provider.getToken();
      return token;
    } catch (err) {
      setError(err.message);
      throw err;
    }
  };

  return { token, loading, error, getToken, provider };
}

// Usage
function MyComponent() {
  const { token, loading, getToken } = useToken();

  useEffect(() => {
    getToken();
  }, []);

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

  return (
    <div>
      <p>Token: {token?.token.substring(0, 20)}...</p>
      <button onClick={getToken}>Get New Token</button>
    </div>
  );
}

Vue Composable

import { ref, onMounted, onUnmounted } from "vue";
import { TokenProvider } from "@superapp_men/token-provider";

export function useToken() {
  const provider = new TokenProvider();
  const token = ref(null);
  const loading = ref(false);
  const error = ref(null);

  const getToken = async () => {
    loading.value = true;
    error.value = null;
    try {
      token.value = await provider.getToken();
    } catch (err) {
      error.value = err.message;
    } finally {
      loading.value = false;
    }
  };

  onMounted(() => {
    provider.on("tokenReceived", ({ token: t }) => {
      token.value = t;
    });

    provider.on("error", ({ message }) => {
      error.value = message;
    });
  });

  onUnmounted(() => {
    provider.destroy();
  });

  return { token, loading, error, getToken };
}

Utility Functions

decodeJWT(token: string)

Decode a JWT token payload (without verification - for display only).

import { decodeJWT } from "@superapp_men/token-provider";

const token = await provider.getToken();
const payload = decodeJWT(token.token);

console.log(payload.sub); // User ID
console.log(payload.exp); // Expiration
console.log(payload.iat); // Issued at

Error Handling

import { TokenProvider, TokenError } from "@superapp_men/token-provider";

const provider = new TokenProvider();

try {
  const token = await provider.getToken();
} catch (error) {
  switch (error.code) {
    case TokenError.TIMEOUT:
      console.error("Request timeout");
      break;
    case TokenError.UNAUTHORIZED:
      console.error("Not authorized");
      break;
    case TokenError.SUPERAPP_NOT_AVAILABLE:
      console.error("SuperApp not responding");
      break;
    case TokenError.TOKEN_EXPIRED:
      console.error("Token expired - get a new one");
      const newToken = await provider.getToken();
      break;
    default:
      console.error("Unknown error:", error.message);
  }
}

Security Considerations

1. Never Store Tokens in localStorage

// ❌ Bad - vulnerable to XSS
localStorage.setItem("token", token.token);

// ✅ Good - get fresh token when needed
const token = await provider.getToken(); // Fresh from SuperApp

2. Always Use HTTPS

Ensure all communication happens over HTTPS in production.

3. Validate Tokens in Your App

The package returns tokens from the SuperApp without validation. You should validate tokens in your partner app using JWKS (JSON Web Key Set) from https://supper-app.azurewebsites.net/.well-known/jwks.json:

  • Extract the kid from the JWT header
  • Fetch the corresponding public key from JWKS
  • Validate the token signature

Important: Always validate tokens before using them. For production applications, also validate tokens on your backend server.

4. Get Fresh Tokens When Needed

Since tokens are not cached, call getToken() whenever you need a new token. The SuperApp will handle token generation securely.

Token Signing: RS256 (RSA256) Algorithm

Why RS256?

This package uses RS256 (RSA Signature with SHA-256) for token signing, which is an industry-standard asymmetric cryptographic algorithm. Here's why RS256 was chosen:

1. Asymmetric Key Pair Security

  • Private Key: Kept secret by the SuperApp (never shared)
  • Public Key: Published via JWKS endpoint (safe to share)
  • Only the SuperApp can sign tokens (using private key)
  • Anyone can verify tokens (using public key)
  • If the public key is compromised, tokens can still be verified but new tokens cannot be forged

2. Key Rotation Support

  • SuperApp can rotate keys without breaking existing tokens
  • Multiple keys can coexist (identified by kid in JWT header)
  • Partner Apps automatically fetch the correct key from JWKS
  • Enables secure key rotation without service interruption

3. Industry Standard & Compatibility

  • RS256 is the most widely supported JWT signing algorithm
  • Recommended by OAuth 2.0 and OpenID Connect specifications
  • Supported by all major JWT libraries and platforms
  • Better security than symmetric algorithms (HS256) for distributed systems

4. Non-Repudiation

  • Each token signature is cryptographically unique
  • Cannot be forged without the private key
  • Provides strong audit trail and accountability

How RS256 Works

Token Signing Process (SuperApp)

  1. Create JWT Structure:

    Header.Payload.Signature
  2. Header (contains algorithm info):

    {
      "alg": "RS256",
      "typ": "JWT",
      "kid": "superapp-key-2025-01"
    }
  3. Payload (token claims):

    {
      "sub": "user-123",
      "exp": 1735689600,
      "iat": 1735686000,
      "aud": "partner-app-id"
    }
  4. Signing:

    • SuperApp creates: base64Url(header) + "." + base64Url(payload)
    • Signs this string using RSA private key with SHA-256 hash
    • Appends signature: header.payload.signature

Token Verification Process (Partner App)

  1. Extract Key ID:

    • Decode JWT header to get kid (Key ID)
    • Example: "kid": "superapp-key-2025-01"
  2. Fetch Public Key:

    • Request JWKS from: https://supper-app.azurewebsites.net/.well-known/jwks.json
    • Find key matching the kid from JWKS response
  3. Verify Signature:

    • Recreate: base64Url(header) + "." + base64Url(payload)
    • Use RSA public key to verify the signature
    • If signature matches → token is authentic and unmodified

Cryptographic Flow

┌─────────────────────────────────────────────────────────┐
│                    SuperApp (Signer)                    │
├─────────────────────────────────────────────────────────┤
│  1. Generate RSA Key Pair                               │
│     - Private Key (SECRET) → Used to SIGN tokens      │
│     - Public Key → Published in JWKS                   │
│                                                          │
│  2. Create JWT Token                                    │
│     Header: { alg: "RS256", kid: "key-2025-01" }       │
│     Payload: { sub, exp, iat, aud, ... }               │
│                                                          │
│  3. Sign Token                                          │
│     signature = RSA_SIGN(                               │
│       SHA256(header + "." + payload),                   │
│       private_key                                       │
│     )                                                   │
│                                                          │
│  4. Return: header.payload.signature                    │
└─────────────────────────────────────────────────────────┘
                        │
                        │ Token (JWT)
                        ▼
┌─────────────────────────────────────────────────────────┐
│                 Partner App (Verifier)                   │
├─────────────────────────────────────────────────────────┤
│  1. Receive JWT Token                                   │
│                                                          │
│  2. Extract kid from Header                            │
│     Decode header → Get "kid": "key-2025-01"           │
│                                                          │
│  3. Fetch Public Key from JWKS                          │
│     GET /.well-known/jwks.json                         │
│     Find key where kid = "key-2025-01"                 │
│                                                          │
│  4. Verify Signature                                    │
│     RSA_VERIFY(                                         │
│       signature,                                        │
│       SHA256(header + "." + payload),                   │
│       public_key                                        │
│     )                                                   │
│                                                          │
│  5. If valid → Token is authentic ✅                    │
│     If invalid → Token is forged/rejected ❌            │
└─────────────────────────────────────────────────────────┘

Security Properties

Integrity Protection

  • Any modification to header or payload invalidates the signature
  • Detects tampering immediately during verification

Authentication

  • Only SuperApp (with private key) can create valid tokens
  • Partner Apps can verify authenticity using public key

Key Isolation

  • Private key never leaves SuperApp servers
  • Public key can be safely shared via JWKS
  • Compromise of public key doesn't allow token forgery

Forward Security

  • Old tokens remain valid even after key rotation
  • New tokens use new keys (identified by different kid)
  • Enables secure key rotation without breaking existing sessions

Comparison with Other Algorithms

| Algorithm | Type | Security | Key Distribution | Use Case | | --------- | ---------- | -------- | ------------------- | -------------------------------------------------- | | RS256 | Asymmetric | High | Public key via JWKS | ✅ Distributed systems (SuperApp architecture) | | HS256 | Symmetric | Medium | Shared secret | Single service | | ES256 | Asymmetric | High | Public key via JWKS | Alternative to RS256 |

Why RS256 over HS256?

  • HS256 requires sharing a secret key → security risk if compromised
  • RS256 uses public/private key pair → public key can be safely shared
  • RS256 enables key rotation without re-deploying Partner Apps

Why RS256 over ES256?

  • RS256 has broader library support
  • RS256 keys are larger but more compatible
  • RS256 is the OAuth 2.0 recommended default

Best Practices

  1. Always Verify Tokens: Never trust tokens without signature verification
  2. Cache JWKS: Fetch JWKS periodically and cache (with TTL) to reduce requests
  3. Handle Key Rotation: If kid not found, refresh JWKS and retry verification
  4. Validate Claims: After signature verification, validate exp, aud, iss claims
  5. Use HTTPS: Always fetch JWKS over HTTPS to prevent MITM attacks

Example: JWKS Structure

{
  "keys": [
    {
      "kty": "RSA", // Key type: RSA
      "use": "sig", // Use: signature
      "alg": "RS256", // Algorithm: RS256
      "kid": "superapp-key-2025-01", // Key ID (matches JWT header)
      "n": "rTknkikAVBH3tWd8p_KaXKOqHx6wBWcbC6DQzoizLhPnZH1kHynEs3EKvN227okxTKfGdmtQgUrC-C-fk5LztMWYR428sao5TmEuhy-bvoEeReh4oDXUqsG7Vc1ilVtjGS6AVpxZtz9Cph4AonLebEyKWHFM9qsqAsmjZNh8L3rqo_uIzz1DVYFer3a1mckgxv1h_EHOFM7SBwiNL3qR4tvsTQNMdHxW49QNg70r69dYy1mbNpZnBBfP-A0bvFrWVnhY3JXEJ25DelvB7UvjlkY3UODuKnlY9fQCrm1nq_aUUYIJJsQRH3DI4N1h3mKYPafrS9iMYf4vi4u85EjESw",
      "e": "AQAB" // RSA public exponent
    }
  ]
}

The n (modulus) and e (exponent) values together form the RSA public key used for signature verification.

Browser Support

  • Chrome/Edge 80+
  • Firefox 75+
  • Safari 14+
  • iOS Safari 14+ (via Capacitor)
  • Android WebView (via Capacitor)

Troubleshooting

"SuperApp not available"

Cause: Partner App is not loaded within SuperApp
Solution: Ensure app is running in iframe or Capacitor WebView

"Request timeout"

Cause: SuperApp not responding or slow network
Solution: Increase timeout in configuration

const provider = new TokenProvider({ timeout: 10000 });

"Token expired"

Cause: Token has expired
Solution: Get a new token by calling getToken() again

try {
  const token = await provider.getToken();
  // Use token...
} catch (error) {
  if (error.code === TokenError.TOKEN_EXPIRED) {
    // Get a new token
    const newToken = await provider.getToken();
  }
}

License

MIT

Support


Ready to use secure tokens? Install the package and get started! 🔐