Skip to content

Commit 58a431d

Browse files
authored
feat(auth): add -y to login (#272)
so that I can suppress the prompt asking me if I want to login when I'm already logged in
1 parent fc039af commit 58a431d

4 files changed

Lines changed: 60 additions & 7 deletions

File tree

.changeset/auth-login-yes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"clerk": minor
3+
---
4+
5+
Add `-y` / `--yes` to `clerk auth login` and `clerk login` to suppress prompt "You're already logged in... Re-Authenticate?"

packages/cli-core/src/cli-program.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,16 @@ Give AI agents better Clerk context: install the Clerk skills
217217
.command("login")
218218
.aliases(["signup", "signin", "sign-in"])
219219
.description("Log in to your Clerk account")
220-
.setExamples([{ command: "clerk auth login", description: "Log in via browser (OAuth)" }])
221-
.action(async () => {
222-
await login();
220+
.option("-y, --yes", "Proceed with OAuth without prompting when already logged in")
221+
.setExamples([
222+
{ command: "clerk auth login", description: "Log in via browser (OAuth)" },
223+
{
224+
command: "clerk auth login -y",
225+
description: "Re-authenticate via OAuth without confirmation when already signed in",
226+
},
227+
])
228+
.action(async (opts) => {
229+
await login(opts);
223230
});
224231

225232
auth
@@ -232,8 +239,9 @@ Give AI agents better Clerk context: install the Clerk skills
232239
program
233240
.command("login", { hidden: true })
234241
.description("Log in to your Clerk account")
235-
.action(async () => {
236-
await login();
242+
.option("-y, --yes", "Proceed with OAuth without prompting when already logged in")
243+
.action(async (opts) => {
244+
await login(opts);
237245
});
238246

239247
program

packages/cli-core/src/commands/auth/login.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,45 @@ describe("login", () => {
374374
expect(mockStartAuthServer).toHaveBeenCalled();
375375
});
376376

377+
test("in human mode, skips prompt and runs OAuth when yes is true", async () => {
378+
mockIsHuman.mockReturnValue(true);
379+
mockGetValidToken.mockResolvedValue("existing-token");
380+
mockGetAuth.mockResolvedValue({ userId: "user_123" });
381+
mockFetchUserInfo
382+
.mockResolvedValueOnce({ userId: "user_123", email: "old@example.com" })
383+
.mockResolvedValueOnce({ userId: "user_new", email: "new@example.com" });
384+
mockBunSpawn();
385+
386+
const mockServer = {
387+
port: 54321,
388+
waitForCallback: mock().mockResolvedValue({ code: "reauth-code" }),
389+
stop: mock(),
390+
};
391+
mockStartAuthServer.mockReturnValue(mockServer);
392+
393+
mockExchangeCodeForToken.mockResolvedValue({
394+
access_token: "new-token",
395+
token_type: "Bearer",
396+
expires_in: 3600,
397+
refresh_token: "new-refresh-token",
398+
});
399+
mockCreateOAuthSession.mockReturnValue({
400+
accessToken: "new-token",
401+
refreshToken: "new-refresh-token",
402+
expiresAt: 999,
403+
tokenType: "Bearer",
404+
});
405+
mockStoreToken.mockResolvedValue(undefined);
406+
mockSetAuth.mockResolvedValue(undefined);
407+
408+
consoleSpy = spyOn(console, "log").mockImplementation(() => {});
409+
const result = await runLogin({ yes: true });
410+
411+
expect(mockConfirm).not.toHaveBeenCalled();
412+
expect(mockStartAuthServer).toHaveBeenCalled();
413+
expect(result).toEqual({ userId: "user_new", email: "new@example.com" });
414+
});
415+
377416
test("in human mode, throws UserAbortError when user declines re-auth", async () => {
378417
mockIsHuman.mockReturnValue(true);
379418
mockGetValidToken.mockResolvedValue("existing-token");

packages/cli-core/src/commands/auth/login.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ensureFirstApplication } from "../../lib/first-application.ts";
1818

1919
interface LoginOptions {
2020
showNextSteps?: boolean;
21+
yes?: boolean;
2122
}
2223

2324
async function getExistingSession(): Promise<UserInfo | null> {
@@ -90,7 +91,7 @@ async function performOAuthFlow(): Promise<UserInfo> {
9091
}
9192

9293
export async function login(options: LoginOptions = {}): Promise<UserInfo> {
93-
const { showNextSteps = true } = options;
94+
const { showNextSteps = true, yes } = options;
9495
intro("clerk auth login");
9596
const existingSession = await withSpinner("Checking session...", () => getExistingSession());
9697

@@ -105,7 +106,7 @@ export async function login(options: LoginOptions = {}): Promise<UserInfo> {
105106
return existingSession;
106107
}
107108

108-
if (existingSession) {
109+
if (existingSession && isHuman() && !yes) {
109110
const reauthenticate = await confirm({
110111
message: `You're already logged in as ${existingSession.email}. Re-authenticate?`,
111112
default: false,

0 commit comments

Comments
 (0)