From 0c059617c8be9697e5a03d0d4d8cffec177edccc Mon Sep 17 00:00:00 2001 From: Eduardo Roth Date: Fri, 5 Dec 2025 18:17:26 -0600 Subject: [PATCH 1/6] chore(auth): remove config service --- src/service/auth-config.svc.ts | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/service/auth-config.svc.ts diff --git a/src/service/auth-config.svc.ts b/src/service/auth-config.svc.ts deleted file mode 100644 index 4b85f115..00000000 --- a/src/service/auth-config.svc.ts +++ /dev/null @@ -1,25 +0,0 @@ -const DEFAULT_REALM_URL = 'https://idp.prod.apps.herodevs.io/realms/universe/protocol/openid-connect'; -const DEFAULT_CLIENT_ID = 'default-public'; -const DEFAULT_SERVICE_NAME = '@herodevs/cli'; -const DEFAULT_ACCESS_KEY = 'access-token'; -const DEFAULT_REFRESH_KEY = 'refresh-token'; - -export function getRealmUrl() { - return process.env.OAUTH_CONNECT_URL || DEFAULT_REALM_URL; -} - -export function getClientId() { - return process.env.OAUTH_CLIENT_ID || DEFAULT_CLIENT_ID; -} - -export function getTokenServiceName() { - return process.env.HD_AUTH_SERVICE_NAME || DEFAULT_SERVICE_NAME; -} - -export function getAccessTokenKey() { - return process.env.HD_AUTH_ACCESS_KEY || DEFAULT_ACCESS_KEY; -} - -export function getRefreshTokenKey() { - return process.env.HD_AUTH_REFRESH_KEY || DEFAULT_REFRESH_KEY; -} From 0a48124526abbdd5b87b6030c33cacb1b36f4849 Mon Sep 17 00:00:00 2001 From: Eduardo Roth Date: Fri, 5 Dec 2025 18:18:00 -0600 Subject: [PATCH 2/6] chore(auth): env variables don't change at runtime, so no need for functions --- src/config/auth.config.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/config/auth.config.ts diff --git a/src/config/auth.config.ts b/src/config/auth.config.ts new file mode 100644 index 00000000..14e0de2e --- /dev/null +++ b/src/config/auth.config.ts @@ -0,0 +1,11 @@ +const DEFAULT_REALM_URL = 'https://idp.prod.apps.herodevs.io/realms/universe/protocol/openid-connect'; +const DEFAULT_CLIENT_ID = 'default-public'; +const DEFAULT_SERVICE_NAME = '@herodevs/cli'; +const DEFAULT_ACCESS_KEY = 'access-token'; +const DEFAULT_REFRESH_KEY = 'refresh-token'; + +export const REALM_URL = process.env.OAUTH_CONNECT_URL ?? DEFAULT_REALM_URL; +export const CLIENT_ID = process.env.OAUTH_CLIENT_ID ?? DEFAULT_CLIENT_ID; +export const SERVICE_NAME = process.env.HD_AUTH_SERVICE_NAME ?? DEFAULT_SERVICE_NAME; +export const ACCESS_KEY = process.env.HD_AUTH_ACCESS_KEY ?? DEFAULT_ACCESS_KEY; +export const REFRESH_KEY = process.env.HD_AUTH_REFRESH_KEY ?? DEFAULT_REFRESH_KEY; From 3539286004cf138d6727f08c1aa9d8415051850a Mon Sep 17 00:00:00 2001 From: Eduardo Roth Date: Fri, 5 Dec 2025 18:19:01 -0600 Subject: [PATCH 3/6] chore(auth): update auth config from service to new file --- src/commands/auth/login.ts | 6 +++--- src/service/auth-refresh.svc.ts | 10 +++++----- src/service/auth-token.svc.ts | 28 ++++++++-------------------- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/commands/auth/login.ts b/src/commands/auth/login.ts index ec76ce01..11d97985 100644 --- a/src/commands/auth/login.ts +++ b/src/commands/auth/login.ts @@ -3,8 +3,8 @@ import http from 'node:http'; import { createInterface } from 'node:readline'; import { URL } from 'node:url'; import { Command } from '@oclif/core'; +import { CLIENT_ID, REALM_URL } from '../../config/auth.config.js'; import { persistTokenResponse } from '../../service/auth.svc.ts'; -import { getClientId, getRealmUrl } from '../../service/auth-config.svc.ts'; import type { TokenResponse } from '../../types/auth.ts'; import { openInBrowser } from '../../utils/open-in-browser.ts'; @@ -14,8 +14,8 @@ export default class AuthLogin extends Command { private server?: http.Server; private readonly port = parseInt(process.env.OAUTH_CALLBACK_PORT || '4000', 10); private readonly redirectUri = process.env.OAUTH_CALLBACK_REDIRECT || `http://localhost:${this.port}/oauth2/callback`; - private readonly realmUrl = getRealmUrl(); - private readonly clientId = getClientId(); + private readonly realmUrl = REALM_URL; + private readonly clientId = CLIENT_ID; async run() { if (typeof (this.config as { runHook?: unknown }).runHook === 'function') { diff --git a/src/service/auth-refresh.svc.ts b/src/service/auth-refresh.svc.ts index 309812a6..d310f2fd 100644 --- a/src/service/auth-refresh.svc.ts +++ b/src/service/auth-refresh.svc.ts @@ -1,5 +1,5 @@ +import { CLIENT_ID, REALM_URL } from '../config/auth.config.js'; import type { TokenResponse } from '../types/auth.ts'; -import { getClientId, getRealmUrl } from './auth-config.svc.ts'; interface AuthOptions { clientId?: string; @@ -7,8 +7,8 @@ interface AuthOptions { } export async function refreshTokens(refreshToken: string, options: AuthOptions = {}): Promise { - const clientId = options.clientId ?? getClientId(); - const realmUrl = options.realmUrl ?? getRealmUrl(); + const clientId = options.clientId ?? CLIENT_ID; + const realmUrl = options.realmUrl ?? REALM_URL; const tokenUrl = `${realmUrl}/token`; const body = new URLSearchParams({ @@ -36,8 +36,8 @@ export async function logoutFromProvider(refreshToken: string | undefined, optio return; } - const clientId = options.clientId ?? getClientId(); - const realmUrl = options.realmUrl ?? getRealmUrl(); + const clientId = options.clientId ?? CLIENT_ID; + const realmUrl = options.realmUrl ?? REALM_URL; const logoutUrl = `${realmUrl}/logout`; const body = new URLSearchParams({ diff --git a/src/service/auth-token.svc.ts b/src/service/auth-token.svc.ts index 10328802..34b584c2 100644 --- a/src/service/auth-token.svc.ts +++ b/src/service/auth-token.svc.ts @@ -1,5 +1,5 @@ import { AsyncEntry } from '@napi-rs/keyring'; -import { getAccessTokenKey, getRefreshTokenKey, getTokenServiceName } from './auth-config.svc.ts'; +import { ACCESS_KEY, REFRESH_KEY, SERVICE_NAME } from '../config/auth.config.js'; export interface StoredTokens { accessToken?: string; @@ -9,26 +9,18 @@ export interface StoredTokens { const TOKEN_SKEW_SECONDS = 30; export async function saveTokens(tokens: { accessToken: string; refreshToken?: string }) { - const service = getTokenServiceName(); - const accessKey = getAccessTokenKey(); - const refreshKey = getRefreshTokenKey(); - - const accessTokenSet = new AsyncEntry(service, accessKey).setPassword(tokens.accessToken); + const accessTokenSet = new AsyncEntry(SERVICE_NAME, ACCESS_KEY).setPassword(tokens.accessToken); const refreshTokenSet = tokens.refreshToken - ? new AsyncEntry(service, refreshKey).setPassword(tokens.refreshToken) - : new AsyncEntry(service, refreshKey).deletePassword(); + ? new AsyncEntry(SERVICE_NAME, REFRESH_KEY).setPassword(tokens.refreshToken) + : new AsyncEntry(SERVICE_NAME, REFRESH_KEY).deletePassword(); return Promise.all([accessTokenSet, refreshTokenSet]); } export async function getStoredTokens(): Promise { - const service = getTokenServiceName(); - const accessKey = getAccessTokenKey(); - const refreshKey = getRefreshTokenKey(); - return Promise.all([ - new AsyncEntry(service, accessKey).getPassword(), - new AsyncEntry(service, refreshKey).getPassword(), + new AsyncEntry(SERVICE_NAME, ACCESS_KEY).getPassword(), + new AsyncEntry(SERVICE_NAME, REFRESH_KEY).getPassword(), ]).then(([accessToken, refreshToken]) => { if (!accessToken && !refreshToken) { return; @@ -42,13 +34,9 @@ export async function getStoredTokens(): Promise { } export async function clearStoredTokens() { - const service = getTokenServiceName(); - const accessKey = getAccessTokenKey(); - const refreshKey = getRefreshTokenKey(); - return Promise.all([ - new AsyncEntry(service, accessKey).deletePassword(), - new AsyncEntry(service, refreshKey).deletePassword(), + new AsyncEntry(SERVICE_NAME, ACCESS_KEY).deletePassword(), + new AsyncEntry(SERVICE_NAME, REFRESH_KEY).deletePassword(), ]); } From c4a0524caea78c7317fdde1461b52a024fa5a6c3 Mon Sep 17 00:00:00 2001 From: Eduardo Roth Date: Fri, 5 Dec 2025 18:19:14 -0600 Subject: [PATCH 4/6] chore(auth): fix type issues --- test/commands/auth/logout.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/commands/auth/logout.test.ts b/test/commands/auth/logout.test.ts index 15a8902c..cf67151e 100644 --- a/test/commands/auth/logout.test.ts +++ b/test/commands/auth/logout.test.ts @@ -1,3 +1,4 @@ +import type { Config } from '@oclif/core'; import { type Mock, vi } from 'vitest'; vi.mock('../../../src/service/auth-token.svc.ts', () => ({ @@ -22,7 +23,7 @@ describe('AuthLogout command', () => { it('logs when there are no stored tokens', async () => { (getStoredTokens as Mock).mockResolvedValue(undefined); - const command = new AuthLogout([], {} as Record); + const command = new AuthLogout([], {} as unknown as Config); const logSpy = vi.spyOn(command, 'log').mockImplementation(() => {}); await command.run(); @@ -33,7 +34,7 @@ describe('AuthLogout command', () => { it('revokes tokens and clears local storage', async () => { (getStoredTokens as Mock).mockResolvedValue({ refreshToken: 'refresh', accessToken: 'access' }); - const command = new AuthLogout([], {} as Record); + const command = new AuthLogout([], {} as unknown as Config); const logSpy = vi.spyOn(command, 'log').mockImplementation(() => {}); await command.run(); @@ -46,7 +47,7 @@ describe('AuthLogout command', () => { it('warns when remote logout fails but still clears tokens', async () => { (getStoredTokens as Mock).mockResolvedValue({ refreshToken: 'refresh' }); (logoutFromProvider as Mock).mockRejectedValueOnce(new Error('network fail')); - const command = new AuthLogout([], {} as Record); + const command = new AuthLogout([], {} as unknown as Config); const warnSpy = vi.spyOn(command, 'warn').mockImplementation((msg) => msg); await command.run(); From 0d2141805970dc7ae3b0c7fb5d0a6b2363789ca1 Mon Sep 17 00:00:00 2001 From: Eduardo Roth Date: Fri, 5 Dec 2025 18:19:45 -0600 Subject: [PATCH 5/6] chore(auth): update readme for auth commands --- README.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d0d0fd32..8c2e2710 100644 --- a/README.md +++ b/README.md @@ -81,13 +81,42 @@ USAGE ## Commands +* [`hd auth login`](#hd-auth-login) +* [`hd auth logout`](#hd-auth-logout) * [`hd help [COMMAND]`](#hd-help-command) * [`hd report committers`](#hd-report-committers) * [`hd scan eol`](#hd-scan-eol) * [`hd tracker init`](#hd-tracker-init) * [`hd tracker run`](#hd-tracker-run) * [`hd update [CHANNEL]`](#hd-update-channel) - * **NOTE:** Only applies to [binary installation method](#binary-installation). NPM users should use [`npm install`](#global-npm-installation) to update to the latest version. + +## `hd auth login` + +OAuth CLI login + +``` +USAGE + $ hd auth login + +DESCRIPTION + OAuth CLI login +``` + +_See code: [src/commands/auth/login.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.14/src/commands/auth/login.ts)_ + +## `hd auth logout` + +Logs out of HeroDevs OAuth and clears stored tokens + +``` +USAGE + $ hd auth logout + +DESCRIPTION + Logs out of HeroDevs OAuth and clears stored tokens +``` + +_See code: [src/commands/auth/logout.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.14/src/commands/auth/logout.ts)_ ## `hd help [COMMAND]` From 83af6b0a8e948c78ec81f317253d72dc77786796 Mon Sep 17 00:00:00 2001 From: Eduardo Roth Date: Thu, 11 Dec 2025 15:37:15 -0600 Subject: [PATCH 6/6] chore(auth): check for any falsy value --- src/config/auth.config.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/config/auth.config.ts b/src/config/auth.config.ts index 14e0de2e..bda0e3d9 100644 --- a/src/config/auth.config.ts +++ b/src/config/auth.config.ts @@ -4,8 +4,8 @@ const DEFAULT_SERVICE_NAME = '@herodevs/cli'; const DEFAULT_ACCESS_KEY = 'access-token'; const DEFAULT_REFRESH_KEY = 'refresh-token'; -export const REALM_URL = process.env.OAUTH_CONNECT_URL ?? DEFAULT_REALM_URL; -export const CLIENT_ID = process.env.OAUTH_CLIENT_ID ?? DEFAULT_CLIENT_ID; -export const SERVICE_NAME = process.env.HD_AUTH_SERVICE_NAME ?? DEFAULT_SERVICE_NAME; -export const ACCESS_KEY = process.env.HD_AUTH_ACCESS_KEY ?? DEFAULT_ACCESS_KEY; -export const REFRESH_KEY = process.env.HD_AUTH_REFRESH_KEY ?? DEFAULT_REFRESH_KEY; +export const REALM_URL = process.env.OAUTH_CONNECT_URL || DEFAULT_REALM_URL; +export const CLIENT_ID = process.env.OAUTH_CLIENT_ID || DEFAULT_CLIENT_ID; +export const SERVICE_NAME = process.env.HD_AUTH_SERVICE_NAME || DEFAULT_SERVICE_NAME; +export const ACCESS_KEY = process.env.HD_AUTH_ACCESS_KEY || DEFAULT_ACCESS_KEY; +export const REFRESH_KEY = process.env.HD_AUTH_REFRESH_KEY || DEFAULT_REFRESH_KEY;