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

@kiriminaja/auth

v0.0.5

Published

Browser OAuth PKCE SDK for Kiriminaja

Downloads

569

Readme

@kiriminaja/auth

Browser-only, ESM-only OAuth 2.0 authorization-code + PKCE helper for Kiriminaja.

It opens https://app.kiriminaja.com/oauth/authorize/ in a popup when possible and automatically falls back to a full-page redirect when the popup is blocked, closed, or times out.

Install

bun add @kiriminaja/auth

OAuth client registration (alpha)

Kiriminaja OAuth is currently in alpha. Before integrating, register your application with the Kiriminaja team by emailing [email protected].

Include the following details in your request:

  • Application name and a short description of its intended use.
  • Environment (development, staging, or production).
  • Exact HTTPS callback URL(s), such as https://your-app.example/auth/callback.
  • Requested OAuth scope(s).
  • A technical contact for the integration.

After approval, Kiriminaja provides the clientId, approved scopes, authorization endpoint, token endpoint, and token-validation details for the requested environment. Callback URLs must match the registered URL exactly; do not use wildcard URLs or derive them from user input.

Configure the client

The SDK is browser-only. In Nuxt or another SSR framework, import and initialize it only in client-side code (for example, a .client plugin or behind if (import.meta.client)). It deliberately throws if Nuxt loads it through an import.meta.server bundle or if it is constructed outside a browser.

import { KiriminajaAuth } from '@kiriminaja/auth'

export const auth = new KiriminajaAuth({
  clientId: 'sandbox-dev-sso',
  scope: 'sandbox',
  redirectUri: 'https://example.com/auth/callback',
})

Use a development authorization server

The production authorization endpoint defaults to https://app.kiriminaja.com/oauth/authorize/. Override authorizationEndpoint when your application is running against an approved development environment:

export const auth = new KiriminajaAuth({
  clientId: 'sandbox-dev-sso',
  scope: 'sandbox',
  redirectUri: 'https://example.com/auth/callback',
  authorizationEndpoint: 'https://app.dev.kiriminaja.com/oauth/authorize/',
})

Use an endpoint controlled by Kiriminaja and ensure its OAuth client has the same registered callback URL. Do not accept this value from untrusted input.

Complete integration guide

Register one exact HTTPS callback URL with Kiriminaja, for example https://your-app.example/auth/callback. That page is required for both popup and full-page redirect authorization.

Your integration has four responsibilities:

  1. Create one KiriminajaAuth instance in browser-only code.
  2. Start authorization from a user gesture (such as a Sign in button).
  3. Handle the registered callback route using notifyPopupCallback() and handleRedirectCallback().
  4. Send the resulting code and PKCE verifier to your backend for exchange.

1. Start sign-in from your login page

Call authorize() from the click handler. It defaults to popup mode and opens the Kiriminaja authorization page centered on screen.

async function exchange(result: { code: string; codeVerifier: string }) {
  const response = await fetch('/api/auth/kiriminaja/exchange', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    credentials: 'include',
    body: JSON.stringify(result),
  })

  if (!response.ok) throw new Error('Unable to complete sign-in.')
}

async function signIn(): Promise<void> {
  // Must be called directly from a user interaction, otherwise browsers may
  // block the popup.
  const result = await auth.authorize()

  // A successful popup resolves here. Exchange the code, then update your
  // application session/profile state and navigate to the signed-in page.
  if (result) {
    await exchange(result)
    window.location.replace('/dashboard')
  }

  // No result means the SDK navigated this tab to the authorization endpoint
  // as its redirect fallback. The callback page completes that flow.
}

The SDK automatically falls back to a full-page redirect when the popup is blocked, manually closed, or times out. To always use a redirect, call:

await auth.authorize({ mode: 'redirect' })

2. Implement the callback page

At the exact path configured as redirectUri, use this client-side callback handler. Do not process OAuth query parameters on the server.

import { notifyPopupCallback } from '@kiriminaja/auth'
import { auth } from './auth'

async function exchange(result: { code: string; codeVerifier: string }) {
  const response = await fetch('/api/auth/kiriminaja/exchange', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    credentials: 'include',
    body: JSON.stringify(result),
  })

  if (!response.ok) throw new Error('Unable to complete sign-in.')
}

async function completeCallback(): Promise<void> {
  // Popup flow: post the complete callback URL to the login window and close
  // this window. The login page's authorize() promise then resolves.
  if (notifyPopupCallback()) return

  // Redirect flow: this tab is the original application tab. Validate state,
  // exchange its authorization code, then load the authenticated application.
  const result = auth.handleRedirectCallback()
  await exchange(result)
  window.location.replace('/dashboard')
}

void completeCallback()

notifyPopupCallback() returns true only when the callback page has an opener. It sends kiriminaja:oauth:callback only to the callback URL's origin, then closes the popup. When it returns false, the callback is a normal redirect. The SDK validates the message origin, callback origin, and OAuth state before returning a popup result.

Flow summary

| Situation | What authorize() does | Where code exchange happens | | --- | --- | --- | | Popup opens and user succeeds | Resolves with AuthorizationResult in the login window | Login-page handler | | Popup is blocked, closed, or times out | Navigates the current tab to authorization | Callback page | | mode: 'redirect' | Navigates the current tab to authorization | Callback page | | User denies access | Returns OAuth error on the callback URL | Handle/display error on callback page |

For popup success, update your reactive session/profile store after exchange. If your backend sets an HTTP-only session cookie, a full navigation (such as window.location.replace('/dashboard')) is the most reliable way to start the application with the new authenticated session.

Exchange and validate on your backend

This package deliberately does not exchange authorization codes or validate access tokens. It is a browser SDK, so it must never receive an OAuth client secret or persist tokens that grant access to your backend.

Create an endpoint in your application backend (for example, POST /api/auth/kiriminaja/exchange) and send it the code and codeVerifier returned by this SDK. That endpoint must:

  1. Exchange the authorization code with the Kiriminaja OAuth token endpoint configured for your OAuth client, using the client credentials only on the server.
  2. Validate the returned token according to the token format and issuer contract supplied by Kiriminaja (signature, issuer, audience, expiry, and required scopes).
  3. Establish your application's server-side session or issue its own secure session cookie. Do not return a confidential-client refresh token to the browser.

The authorization endpoint (/oauth/authorize/) is intentionally separate from the token endpoint. Obtain the correct token endpoint and validation/JWKS details for each environment from Kiriminaja's OAuth configuration; do not derive or hard-code them from the authorization URL.

If you need a reusable server implementation, provide it as a separate server-side package. Keeping it out of @kiriminaja/auth preserves this package's browser-only API and prevents accidental exposure of secrets.

Generated authorization URL

The SDK generates an authorization request equivalent to:

https://app.kiriminaja.com/oauth/authorize/?client_id=sandbox-dev-sso&scope=sandbox&redirect_uri=https%3A%2F%2Fyour-app.example%2Fauth%2Fcallback&state=<random>&code_challenge=<sha256-pkce-challenge>&code_challenge_method=S256

Use await auth.createAuthorizationUrl() if you only need the URL, for example to render a login link. It stores the matching PKCE verifier in sessionStorage so handleRedirectCallback() can validate it later.

Security model

  • A random state and PKCE code verifier are created per authorization request.
  • The verifier is stored in sessionStorage and consumed once after the callback.
  • Exchange the code for tokens from your backend. Do not expose a confidential OAuth client secret in browser code.
  • Requires a secure browser context with Web Crypto support.

Publishing

The package export map exposes only an import entry—there is no CommonJS require export.

Releases are created from an up-to-date, clean main checkout. The command validates the package, bumps its version, creates a vX.Y.Z Git tag, and pushes it. The tag-triggered GitHub Actions workflow publishes the package.

bun run release          # patch release
bun run release minor    # minor release
bun run release major    # major release

Publishing uses npm Trusted Publishing through GitHub Actions; no npm token is required. Configure npm's trusted publisher with this repository and the .github/workflows/release.yml workflow.