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

@kgen-protocol/auth-core

v1.0.12

Published

Framework-agnostic KGeN login logic — auth state, API flows, token management

Readme

@kgen-protocol/auth-core

Framework-agnostic KGeN login logic — auth state, API flows, and token management. No UI, no routing, no analytics. Works with Next.js, Vite, or any React 18+ app.

Installation

This package is published to GitHub Packages. You need a GITHUB_TOKEN with read:packages scope.

Add to your .npmrc:

@kgen-protocol:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}

Then install:

pnpm add @kgen-protocol/auth-core
# peer deps
pnpm add react axios jotai

Setup

Call initAuthCore once at your app root before using any hooks.

// Next.js — app/providers.tsx
import { initAuthCore } from "@kgen-protocol/auth-core";

initAuthCore({
  baseApiUrl: process.env.NEXT_PUBLIC_BASE_API_URL!,

  onLoginSuccess: (tokens) => {
    // tokens are already saved to cookies — fire analytics here
    analytics.track("login_success");
    router.replace("/home");
  },

  onLogout: (reason) => {
    // reason: "token_expired" | "account_deleted" | "manual"
    router.replace("/login");
  },

  onShieldBlock: (message, status) => {
    // show your shield/restriction overlay
    showShieldModal(message);
  },

  getErrorMessage: (errorCode, fallback, params) => {
    // return a localised string — or skip this and get raw API messages
    return t(`errors.${errorCode}`) ?? fallback;
  },
});
// Vite — src/main.tsx
initAuthCore({
  baseApiUrl: import.meta.env.VITE_BASE_API_URL,
});

Phone OTP Login

import {
  useRegisterOtp,
  useVerifyOtp,
  useResendOtp,
  useUpdatePhoneNumberAtom,
  useUpdateLoginFlowAtom,
  useTimer,
} from "@kgen-protocol/auth-core";

function LoginForm() {
  const { setPhoneNumber } = useUpdatePhoneNumberAtom();
  const { setCountryCode, state } = useUpdateLoginFlowAtom();
  const { secondsLeft, isExpired } = useTimer(30, otpSent);

  const registerOtp = useRegisterOtp();
  const verifyOtp = useVerifyOtp();
  const resendOtp = useResendOtp();

  return (
    <input
      onChange={(e) => {
        setPhoneNumber(e.target.value);   // update atom on every keystroke
        setCountryCode("+91");
      }}
    />
  );
}

Important: Call setPhoneNumber and setCountryCode on onChange, not immediately before calling registerOtp(). Atom updates trigger a re-render — calling the hook on the same tick will read the stale value.

Social Auth

import { useSocialAuthorize, useVerifySocialAuth, SOCIAL_PROVIDERS } from "@kgen-protocol/auth-core";

// Step 1 — get the provider redirect URL
const getRedirectUrl = useSocialAuthorize({
  provider: SOCIAL_PROVIDERS.GOOGLE,
  host: "INDIGG",
  platform: "WEB",
});

const url = await getRedirectUrl();
if (url) window.location.href = url;

// Step 2 — on callback page, exchange the code
const verify = useVerifySocialAuth({
  onSuccess: (tokens) => router.replace("/home"),
  onError: () => setError("Login failed"),
});

// Read params from URL (use your own router)
const code = new URLSearchParams(location.search).get("code");
await verify({ provider: "GOOGLE", platform: "WEB", code, redirectUri });

Discord OAuth

Discord uses a separate hook that sends the auth code as an HTTP header:

import { useSocialDiscordVerify } from "@kgen-protocol/auth-core";

const verifyDiscord = useSocialDiscordVerify({
  onSuccess: (tokens) => router.replace("/home"),
  onError: () => setError("Discord login failed"),
});

// The hook is a no-op when localStorage's socialLoginMethod is not "discord"
const code = new URLSearchParams(location.search).get("code");
await verifyDiscord(code);

Logout

import { useLogout } from "@kgen-protocol/auth-core";

function Header() {
  const logout = useLogout();

  return (
    <button onClick={() => logout("manual")}>Sign out</button>
  );
}

logout(reason) clears all auth cookies, resets all auth atoms, and fires config.onLogout(reason).

| Reason | When | |---|---| | "manual" | User clicked sign out | | "token_expired" | Access token expired | | "account_deleted" | Account was deleted |

Token Utilities

import {
  isAuthenticated,
  getAuthTokens,
  clearAuthTokens,
  parseJwt,
  isTokenExpired,
  saveAuthTokensToCookies,
} from "@kgen-protocol/auth-core";

isAuthenticated();          // true if accessToken cookie exists
getAuthTokens();            // { accessToken, refreshToken, idToken }
clearAuthTokens();          // removes all auth cookies + clears localStorage
parseJwt(token);            // decoded payload object
isTokenExpired(token);      // true if exp < now

Auth State Hook

import { useAuthState, LoginStatus } from "@kgen-protocol/auth-core";

function Header() {
  const { isLoggedIn, userId, countryCode } = useAuthState();

  if (isLoggedIn === LoginStatus.LoggedIn) {
    return <span>Welcome, {userId}</span>;
  }
  return <LoginButton />;
}

| LoginStatus value | Meaning | |---|---| | LoginStatus.Pending | Initial state — not yet determined | | LoginStatus.LoggedIn | User is authenticated | | LoginStatus.LoggedOut | User is not authenticated |

All Exports

Initialisation

| Export | Description | |---|---| | initAuthCore(config) | Initialise with config — call once at app root | | isAuthCoreInitialised() | Returns true if already initialised |

Config type

| Field | Type | Required | Description | |---|---|---|---| | baseApiUrl | string | Yes | Base URL for all KGeN API calls | | onLoginSuccess | (tokens) => void | No | Called after a successful login | | onLogout | (reason) => void | No | Called when user is logged out | | onShieldBlock | (message, status) => void | No | Called on a 403 Shield block | | getErrorMessage | (code, fallback?, params?) => string | No | Return a localised error string |

Hooks — state

| Hook | Returns | Description | |---|---|---| | useAuthState() | { isLoggedIn, userId, countryCode, ipAddress, deviceData, error, emailOptIn } | Read-only auth state | | useUpdateLoginFlowAtom() | { state, setCountryCode, setError, setErrorFlow, setIsLoggedIn, ... } | Read/write login flow state | | useUpdatePhoneNumberAtom() | { phoneNumber, setPhoneNumber, setAuthCode, ... } | Read/write phone number state | | useUpdateOtpAtom() | { otp, setOtp, ... } | Read/write OTP state | | useUpdateModalAtom() | { step, isOpen, setStep, handleModal, ... } | Read/write modal/step state | | useUpdateUserAtom() | { userId, countryCode, setUser, ... } | Read/write user atom | | useLogout() | (reason?) => void | Returns logout function | | useTimer(seconds, active) | { secondsLeft, isExpired, reset } | Countdown timer | | useUserLocationApi() | () => Promise<void> | Fetches and stores user IP + location | | useMonitorAtomState(atom) | void | Dev utility — logs atom state changes |

Hooks — flows

| Hook | Description | |---|---| | useRegisterOtp(props?) | Sends OTP to phone number | | useVerifyOtp(options?) | Verifies OTP and logs the user in | | useResendOtp() | Resends OTP to the current phone number | | useSocialAuthorize(options) | Returns OAuth redirect URL for a provider | | useVerifySocialAuth(options?) | Exchanges OAuth code for tokens (Google/Twitter) | | useSocialDiscordVerify(options?) | Exchanges Discord OAuth code for tokens |

Atoms (advanced)

Raw Jotai atoms are exported for consumers that need direct atom access:

| Export | Description | |---|---| | userAtom | User login status, userId, IP, device data | | userLocationAtom | Country code, country name | | loginFlowAtom | Login flow state (step, errors, country, etc.) | | loginInitialState | Initial value for loginFlowAtom | | phoneNumberFlowAtom | Phone number and auth code | | otpFlowAtom | OTP input state | | modalAtom | Modal open/step/flow state | | shieldAtom | Shield block state | | socialFlowAtom | Active social login method |

Constants

| Export | Description | |---|---| | SOCIAL_PROVIDERS | { GOOGLE, TWITTER, DISCORD, OTPLESS } | | LOGIN_METHOD | { google, twitter, discord, phone } | | COUNTRY_CODES | Dial codes — IN: "+91", BR: "+55", BD: "+880", NP: "+977" | | COUNTRY_NAMES | Country name mapping by dial code | | LOCAL_STORAGE_KEYS | All localStorage key strings | | SESSION_STORAGE_KEYS | { SHIELD_BLOCKED_MODE, SHIELD_ERROR_STATUS, SHIELD_MESSAGE } | | TOKEN_TYPE | { ACCESS, REFRESH, ID } | | LOGIN_FLOW | Step constants for the modal flow | | API_URLS | All API endpoint strings | | OTPLESS_FLOW_TYPE | { VERIFY_USER_NUMBER } | | ERROR_MESSAGE | Generic error message strings | | errorsMapping | Error messages keyed by API status code | | SHIELD_FRAUDS | Shield fraud type constants | | countryCodes | Full country dial-code list | | phoneRegex | Phone number regex for India | | internationalPhoneRegex | International phone number regex | | PHONE_NUMBER_REGEX_BY_COUNTRY_CODE | Phone validation regex per country code |

Token utilities

| Export | Description | |---|---| | isAuthenticated() | true if accessToken cookie is present | | getAuthTokens() | { accessToken, refreshToken, idToken } from cookies | | saveAuthTokensToCookies(tokens, isNewUser?) | Writes auth tokens to cookies | | saveSessionToken(tokens, isNewUser?) | Writes session tokens to cookies (secondary login) | | clearAuthTokens(keysToKeep?) | Removes all auth cookies and clears localStorage | | clearLocalStorageExcept(keysToKeep) | Clears localStorage, preserving specified keys | | getReceivedToken() | { accessToken, refreshToken } from cookies | | readCookie(name) | Reads a cookie by name | | setCookie(name, value, days?) | Writes a cookie (default 7-day expiry) | | removeCookie(name) | Expires a cookie immediately |

Utilities

| Export | Description | |---|---| | getCurrentPlatform() | Returns current platform string (gamer, airdrop, etc.) | | getSourceAsPerWebsiteUrl() | Returns source identifier based on window.location | | isGamerPlatform() | true if current platform is gamer | | isDesktop() | true if running on a desktop browser | | isMobileDevice() | true if running on a mobile browser | | getDeviceType() | Returns "mobile" or "desktop" | | parseJwt(token) | Decodes a JWT — returns payload object | | parseUserIdFromJwt(token) | Extracts userId claim from a JWT | | isTokenExpired(token) | true if JWT exp is in the past | | isIndianUser() | true if stored country code is +91 | | isBrazilUser() | true if stored country code is +55 | | validatePhoneNumberByCountry(phone, countryCode) | Returns error key string or null | | countryOptions | Array of { label, value } for country selector | | getLocalStorageItem(key) | Safe localStorage.getItem (SSR-safe) | | setLocalStorage(key, value) | Safe localStorage.setItem (SSR-safe) | | removeLocalStorage(key) | Safe localStorage.removeItem (SSR-safe) | | getSessionStorageItem(key) | Safe sessionStorage.getItem (SSR-safe) | | setSessionStorageItem(key, value) | Safe sessionStorage.setItem (SSR-safe) | | removeSessionStorageItem(key) | Safe sessionStorage.removeItem (SSR-safe) | | getIPAndLocationFromStorage() | Returns stored IP/location headers object | | ipAddressRef | Mutable ref holding the current IP address |

API layer (advanced)

| Export | Description | |---|---| | apiCalls | Axios wrapper with .get, .post, .put, .delete methods | | getAxiosInstance() | Returns the underlying Axios instance |

Types

Key exported types:

| Type | Description | |---|---| | AuthCoreConfig | Config object passed to initAuthCore | | Tokens | { accessToken, refreshToken, idToken? } | | LoginFlowType | Shape of loginFlowAtom state | | OTPFlowState | Shape of otpFlowAtom state | | PhoneNumberFlowState | Shape of phoneNumberFlowAtom state | | ModalManageState | Shape of modalAtom state | | UserAtomType | Shape of userAtom state | | UserLocation | { countryName, countryCode, continentCode, countryCodeISO3 } | | SocialAuthResponse | API response from social auth verify | | RegisterApiResponse | { authCode, isNewUser } | | VerifyOtpApiResponse | Tokens + status fields | | ApiCallResponse<T> | SuccessApiResponse<T> \| FailedApiResponse<T> | | Flows | Enum of flow types (Login, VerifyOtp, etc.) | | LoginStatus | Enum: Pending \| LoggedIn \| LoggedOut | | ErrorFlow | Enum: PhoneNumber \| Otp \| Twitter \| Discord | | IDeviceInfo | Device info shape |

Peer Dependencies

| Package | Required version | |---|---| | react | >= 18.0.0 | | axios | >= 1.6.0 | | jotai | >= 2.0.0 |

Build

pnpm build          # ESM + CJS + type declarations
pnpm typecheck      # tsc --noEmit
pnpm test           # vitest run

Publishing

Publishing is handled by GitHub Actions on a version tag:

git tag @kgen-protocol/[email protected]
git push --tags