Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Main

on:
push:
branches:
- master
pull_request:

concurrency:
group: main-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
main:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [22, 24]

steps:
- uses: actions/checkout@v4

- name: Setup Node and pnpm
uses: silverhand-io/actions-node-pnpm-run-steps@v5
with:
pnpm-version: 10.8.0
node-version: ${{ matrix.node-version }}

- name: Build
run: pnpm -r build

- name: Typecheck
run: pnpm -r typecheck

- name: Test
run: pnpm -r test
33 changes: 33 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Publish

on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:

concurrency:
group: publish-${{ github.head_ref || github.run_id }}
cancel-in-progress: false

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write

steps:
- uses: actions/checkout@v4

- name: Setup Node and pnpm
uses: silverhand-io/actions-node-pnpm-run-steps@v5
with:
pnpm-version: 10.8.0
node-version: 22

- name: Publish to npm
working-directory: packages/cli-auth
run: pnpm publish --provenance --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTOMATION_TOKEN }}
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# @logto-io/cli-auth
# cli-auth

Pluggable authentication for CLI apps — supports OAuth Device Code, Authorization Code + PKCE, Client Credentials, and Token Exchange (RFC 8693).

## Install

```bash
npm install @logto-io/cli-auth
npm install cli-auth
# or
pnpm add @logto-io/cli-auth
pnpm add cli-auth
```

Requires Node.js >= 22.
Expand All @@ -17,7 +17,7 @@ Requires Node.js >= 22.
All strategies share the same lifecycle: **create** → **login** → **getToken** → **logout**.

```ts
import { createCliAuth } from "@logto-io/cli-auth";
import { createCliAuth } from "cli-auth";

const auth = createCliAuth({ strategy: "device-code", ... });

Expand All @@ -40,7 +40,7 @@ await auth.logout();
Every strategy requires a `storage` object. You control where tokens are persisted:

```ts
import type { Storage } from "@logto-io/cli-auth";
import type { Storage } from "cli-auth";

const storage: Storage = {
load: async () => { /* return saved credential or undefined */ },
Expand All @@ -54,23 +54,23 @@ const storage: Storage = {
**memoryStorage** — in-memory, useful for testing or short-lived processes:

```ts
import { memoryStorage } from "@logto-io/cli-auth";
import { memoryStorage } from "cli-auth";

const storage = memoryStorage();
```

**fileStorage** — JSON file with atomic writes and secure permissions (0o600 file, 0o700 directory):

```ts
import { fileStorage } from "@logto-io/cli-auth";
import { fileStorage } from "cli-auth";

const storage = fileStorage({ dir: "~/.myapp" });
```

**keyringStorage** — system keyring (macOS Keychain, Windows Credential Store, Linux Secret Service) via [`@napi-rs/keyring`](https://github.com/nicolo-ribaudo/keyring-node):

```ts
import { keyringStorage } from "@logto-io/cli-auth";
import { keyringStorage } from "cli-auth";
import { Entry } from "@napi-rs/keyring";

const storage = keyringStorage({
Expand All @@ -89,7 +89,7 @@ pnpm add @napi-rs/keyring
When multiple processes share the same storage (e.g. parallel CLI invocations), concurrent token refreshes can cause conflicts. Add a `lock` to serialize refresh operations:

```ts
import { fileStorage, fileLock } from "@logto-io/cli-auth";
import { fileStorage, fileLock } from "cli-auth";

const storage = fileStorage({ dir: "~/.myapp" })
.withLock(fileLock({ lockPath: "~/.myapp/.lock" }));
Expand All @@ -98,7 +98,7 @@ const storage = fileStorage({ dir: "~/.myapp" })
`fileLock` uses atomic file creation (`O_CREAT | O_EXCL`) to ensure only one process refreshes at a time. This works with any storage backend, including keyringStorage:

```ts
import { keyringStorage, fileLock } from "@logto-io/cli-auth";
import { keyringStorage, fileLock } from "cli-auth";
import { Entry } from "@napi-rs/keyring";

const storage = keyringStorage({ entry: new Entry("my-app", "tokens") })
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"engines": {
"node": ">=22.0.0"
},
"packageManager": "pnpm@10.0.0",
"packageManager": "pnpm@10.8.0",
"scripts": {
"build": "pnpm -r build",
"test": "pnpm -r test",
Expand Down
8 changes: 7 additions & 1 deletion packages/cli-auth/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@logto-io/cli-auth",
"name": "cli-auth",
"version": "0.0.0",
"description": "Pluggable authentication for CLI apps — OAuth Device Code, Authorization Code + PKCE, Client Credentials, and Token Exchange.",
"type": "module",
Expand All @@ -14,6 +14,7 @@
],
"scripts": {
"build": "vite build && tsc --emitDeclarationOnly --outDir dist",
"prepack": "pnpm run build",
"test": "vitest run",
"test:watch": "vitest",
"typecheck": "tsc --noEmit"
Expand All @@ -22,6 +23,11 @@
"node": ">=22.0.0"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/logto-io/cli-auth.git",
"directory": "packages/cli-auth"
},
"devDependencies": {
"@types/node": "^25.5.2",
"msw": "^2.13.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/samples/authorization-code.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "dotenv/config";
import { createCliAuth } from "@logto-io/cli-auth";
import { createCliAuth } from "cli-auth";
import { spawn } from "node:child_process";

const auth = createCliAuth({
Expand Down
2 changes: 1 addition & 1 deletion packages/samples/client-credentials.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "dotenv/config";
import { createCliAuth } from "@logto-io/cli-auth";
import { createCliAuth } from "cli-auth";

const auth = createCliAuth({
strategy: "client-credentials",
Expand Down
2 changes: 1 addition & 1 deletion packages/samples/device-code.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "dotenv/config";
import { createCliAuth } from "@logto-io/cli-auth";
import { createCliAuth } from "cli-auth";

const auth = createCliAuth({
strategy: "device-code",
Expand Down
4 changes: 2 additions & 2 deletions packages/samples/keyring-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
* and then removes it. You can observe the entry appearing and disappearing
* in your OS credential manager (e.g. macOS Keychain Access).
*/
import { keyringStorage } from "@logto-io/cli-auth";
import type { TokenSet } from "@logto-io/cli-auth";
import { keyringStorage } from "cli-auth";
import type { TokenSet } from "cli-auth";
import { Entry } from "@napi-rs/keyring";

const SERVICE = "logto-cli-auth-sample";
Expand Down
2 changes: 1 addition & 1 deletion packages/samples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"keyring-storage": "tsx keyring-storage.ts"
},
"dependencies": {
"@logto-io/cli-auth": "workspace:*",
"cli-auth": "workspace:*",
"@napi-rs/keyring": "^1.2.0",
"dotenv": "^17.4.1"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/samples/token-exchange.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "dotenv/config";
import { createCliAuth } from "@logto-io/cli-auth";
import { createCliAuth } from "cli-auth";

const tokenEndpoint = process.env.TOKEN_EXCHANGE_TOKEN_ENDPOINT!;
const clientId = process.env.TOKEN_EXCHANGE_CLIENT_ID!;
Expand Down
6 changes: 3 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading