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

@modelprofile.com/authswitch-signin

v11.4.1

Published

Sign a ChatGPT account in by device code and keep its grant usable across processes, over a store the caller supplies.

Readme

@modelprofile.com/authswitch-signin

Sign a ChatGPT account in by device code, and keep its grant usable for any number of processes that share one store. Part of authswitch, which runs its own sign-ins on this package.

Issue Reporting and Security

For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.

Install

pnpm add @modelprofile.com/authswitch-signin

The package has no runtime dependencies; it needs Node 24 and the global fetch, or a fetch you pass.

What it does

  • The sign-in protocol Codex uses: the device code, its poll, the authorization-code exchange, the refresh-token grant, the revocation and the claims of the tokens (requestOpenAiDeviceCode, pollOpenAiDeviceCodeOnce, pollOpenAiDeviceCode, exchangeOpenAiDeviceAuthorization, refreshOpenAiTokens, revokeOpenAiRefreshToken, parseOpenAiTokenClaims).
  • A device sign-in that survives a restart (startOpenAiDeviceSignIn, pollOpenAiDeviceSignIn).
  • A grant manager over a store you supply (OpenAiGrantManager).

Every failure has a fixed message. No response body, token, code or header crosses an error, a status or a return value other than the credential itself.

A device sign-in any process can finish

import {
  pollOpenAiDeviceSignIn,
  startOpenAiDeviceSignIn,
} from '@modelprofile.com/authswitch-signin';

const { prompt, state } = await startOpenAiDeviceSignIn();
// Show prompt.verificationUrl and prompt.userCode; the code expires at prompt.expiresAt.
// Keep `state` sealed: its deviceAuthId with its userCode complete the sign-in.

const poll = await pollOpenAiDeviceSignIn(state);
if (poll.status === 'approved') {
  // poll.result.credential is the sign-in; poll.result.account says whose it is.
} else if (poll.status === 'pending') {
  // Poll again at poll.nextPollAt at the earliest.
} else {
  // 'expired': start a new sign-in.
}

startOpenAiDeviceSignIn stamps the state with issuedAt and expiresAt. The code lives fifteen minutes, the time Codex polls it and tells the person. pollOpenAiDeviceSignIn asks once, bounded by the time the code has left and by thirty seconds, and answers expired without a request once that time is up. A malformed state (its shape, or a window longer than fifteen minutes) is refused before anything is sent; only shape and window are checked, not where the state came from.

The service answers the authorization once. Poll one state from one caller at a time. After approved, or after a failed exchange, start a new sign-in rather than polling the same state again.

A grant several processes share

OpenAiGrantManager answers current access for each provider request. It refreshes the tokens when the access expires within five minutes, or is older than eight days (Codex's two rules), or the provider rejected exactly the current generation.

import {
  OpenAiGrantManager,
  type IOpenAiGrantStore,
} from '@modelprofile.com/authswitch-signin';

const store: IOpenAiGrantStore = createMyStore(); // load / create / compareAndSet / remove
const grants = new OpenAiGrantManager({ store });

await grants.create('organization-1', poll.result); // after an approved sign-in

const access = await grants.resolveAccess('organization-1', {
  signal,
  rejectedGeneration, // the generation a 401 rejected, if any
});
// { accessToken, accountId, isFedrampAccount, expiresAt, generation }

That answer is the resolveAccess result FlexHarness's createOpenAiChatGptResolvingModelConnection() takes:

const connection = createOpenAiChatGptResolvingModelConnection({
  expectedIdentity: { accountId, isFedrampAccount },
  resolveAccess: ({ signal, rejectedGeneration }) =>
    grants.resolveAccess('organization-1', { signal, rejectedGeneration }),
});

The store

The store is yours. load reads a record, create inserts a new one, and compareAndSet and remove write only while the record is still at the revision the manager read. Both must compare and write in one atomic step, as a database's conditional update does. The record carries the credential. Seal it at rest, never log it, and hand it back unchanged. The manager checks every record it reads, and refuses one it would not have written with invalid_record.

The rules

  • A refresh token whose outcome is unknown is never sent a second time. Before it sends a refresh, the manager stores the grant as refresh_uncertain, with an attempt that is given up after sixty seconds. A success stores the rotated tokens as active and moves the generation on.

  • Every other outcome after the request was handed to fetch makes the grant reauth_required: a refusal, an HTTP error, a lost connection, or the end of the attempt without an answer. So does a refresh that answers another workspace or user. Only a new sign-in, through replace, repairs it. This is deliberate: any answer to a sent refresh other than new tokens, a 429 or a 5xx included, means the refresh token's outcome is unknown, and such a token is never sent again. So an outage at OpenAI can require signing in again.

  • A refresh that was never sent keeps the grant active and sets retryAt, with exponential backoff and the service's Retry-After. Access that is still valid long enough is served meanwhile. Otherwise the call fails with access_not_fresh.

  • Within a process, one refresh per grant runs at a time. Across processes, the store's compare-and-set decides, and a process that finds another's refresh in flight waits for its outcome. A caller that stops waiting (signal) leaves the refresh running to its end, so its outcome is recorded.

  • keepFresh(grantId) refreshes a grant that is due, for a periodic job that keeps an idle sign-in alive.

  • remove(grantId) deletes the grant, then revokes its refresh token at the service. revocation is:

    • confirmed when the service revoked the current token;
    • unconfirmed when a refresh may have replaced that token before;
    • failed when the service did not confirm.

    The grant is removed in every case.

Errors are OpenAiGrantError with a code:

  • not_found
  • reauth_required, with details.reauthReason: refresh_failed, refresh_interrupted or identity_changed
  • access_not_fresh, with details.retryAt
  • invalid_argument
  • invalid_record
  • aborted
  • contended

status(grantId) answers the grant's state, workspace, plan and times without a token.

License and Legal Information

This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the repository license file.

The ChatGPT sign-in code is adapted from OpenAI Codex, which is licensed under the Apache License, Version 2.0. Its notices are in third-party-notices.md and its license in openai-codex-license.txt; both ship with this package.

Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.

Trademarks

This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.

Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.

Company Information

Task Venture Capital GmbH Registered at District Court Bremen HRB 35230 HB, Germany

For any legal inquiries or further information, please contact us via email at [email protected].

By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.