Skip to content

Add GitHub/Google OAuth sign-in and show-password toggle to Clerk login dialog#645

Draft
Copilot wants to merge 12 commits intomasterfrom
copilot/enhance-clerk-login-dialog
Draft

Add GitHub/Google OAuth sign-in and show-password toggle to Clerk login dialog#645
Copilot wants to merge 12 commits intomasterfrom
copilot/enhance-clerk-login-dialog

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 12, 2026

  • Replace base64-hostname-derivation approach with Clerk's standard Accounts Portal URL (https://accounts.clerk.com/sign-in?__publishable_key=...&redirect_url=...)
Original prompt

Overview

Enhance the Clerk-based login dialog with two features:

  1. GitHub and Google OAuth sign-in buttons — allowing users to sign in via OAuth instead of email/password.
  2. "Show password" toggle checkbox — allowing users to toggle password visibility in the password field.

Files to Modify

1. gui-js/libs/core/src/lib/services/clerk/clerk.service.ts

Add a new signInWithOAuth method that initiates OAuth sign-in via Clerk for a given OAuth provider (GitHub or Google). Clerk's JS SDK supports this via clerk.client.signIn.authenticateWithRedirect(...) or clerk.redirectToOAuth(...) / clerk.authenticateWithMetamask depending on version.

For Clerk JS, the correct way to initiate an OAuth flow is:

await this.clerk.signIn.authenticateWithRedirect({
  strategy: 'oauth_github', // or 'oauth_google'
  redirectUrl: '<callback-url>',
  redirectUrlComplete: '<completion-url>',
});

However, since this is an Electron app (not a browser web app), the OAuth redirect approach won't work cleanly with Electron's custom protocol or file:// URLs. Instead, use the Clerk openSignIn UI or use clerk.client.signIn.create with the oauth_github/oauth_google strategy and handle the OAuth flow via an external browser.

The recommended approach for Electron is to open an external browser for OAuth and handle the callback through a deep link or a local HTTP server. However, looking at the existing codebase, the simplest workable approach that mirrors how Clerk recommends OAuth for non-browser environments is:

Use clerk.client.signIn.authenticateWithRedirect with a redirect URL pointing to a local Clerk-hosted page, and then have Electron intercept/handle it.

For now, implement the method to open OAuth via the system browser using Clerk's openSignIn approach or authenticateWithRedirect. Use the following signature and implementation pattern that is consistent with how the existing signInWithEmailPassword method is structured:

async signInWithOAuth(provider: 'oauth_github' | 'oauth_google'): Promise<void> {
  if (!this.clerk) throw new Error('Clerk is not initialized.');
  await this.clerk.client.signIn.authenticateWithRedirect({
    strategy: provider,
    redirectUrl: AppConfig.clerkOAuthRedirectUrl,
    redirectUrlComplete: AppConfig.clerkOAuthRedirectUrl,
  });
}

Also check gui-js/libs/environment/src/lib/environment.ts or app-config.ts to see if clerkOAuthRedirectUrl or similar already exists. If not, note the field should be added to AppConfig — add it in the same file/object where clerkPublishableKey is defined, with a sensible placeholder value like 'https://your-app.clerk.accounts.dev/v1/oauth_callback'.

2. gui-js/libs/ui-components/src/lib/login/login.component.ts

Current content:

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormControl, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { ClerkService } from '@minsky/core';
import { ElectronService } from '@minsky/core';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';

@Component({
  selector: 'minsky-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss'],
  standalone: true,
  imports: [
    CommonModule,
    ReactiveFormsModule,
    MatButtonModule,
    MatInputModule,
    MatFormFieldModule,
    MatProgressSpinnerModule,
  ],
})
export class LoginComponent implements OnInit {
  loginForm = new FormGroup({
    email: new FormControl('', [Validators.required, Validators.email]),
    password: new FormControl('', [Validators.required]),
  });

  isLoading = false;
  errorMessage = '';
  isAuthenticated = false;

  constructor(
    private clerkService: ClerkService,
    private electronService: ElectronService,
    private route: ActivatedRoute
  ) {}

  async ngOnInit() {
    this.route.queryParams.pipe(take(1)).subscribe((params) => {
      this.initializeSession(params['authToken']);
    });
  }

  private async initializeSession(authToken: string | undefined) {
    try {
      await this.clerkService.initialize();
      if (authToken) {
        await this.clerkService.setSession(authToken);
      }
      this.isAuthenticated = await this.clerkService.isSignedIn();
    } catch (err) {
      this.errorMessage = 'Session expired. Please sign in again.';
      this.isAuthenticated = false;
    }
  }

  get email() { return this.loginForm.get('email'); }
  get password() { return this.loginForm.get('password'); }

  async onSubmit() {
    if (this.loginForm.invalid) return;
    this.isLoading = true;
    this.errorMessage = '';
 ...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- Reviewable:start -->
- - -
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/highperformancecoder/minsky/645)
<!-- Reviewable:end -->

…ogin dialog

Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/93883e56-cb4c-463a-838b-565280e1743a

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
Copilot AI changed the title [WIP] Add GitHub and Google OAuth sign-in to Clerk dialog Add GitHub/Google OAuth sign-in and show-password toggle to Clerk login dialog Apr 12, 2026
Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/d709eaa8-072b-4e05-ad66-96a5dbfd2e48

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
@highperformancecoder
Copy link
Copy Markdown
Owner

@copilot LoginComponent.clerkSignInEl is undefined. I would expect it to point to the div labelled #clerkSignIn on construction of LoginComponent using the @ViewChild directive, but I'm not overly familar with these Angularisms.

Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/ec328585-869b-44a9-bb1e-250cded94739

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 12, 2026

@copilot LoginComponent.clerkSignInEl is undefined. I would expect it to point to the div labelled #clerkSignIn on construction of LoginComponent using the @ViewChild directive, but I'm not overly fam...

You're right — the root cause is that #clerkSignIn lives inside *ngIf="!isLoading", so it isn't in the DOM when Angular first resolves @ViewChild. By the time *ngIf renders it (after isLoading becomes false), ngAfterViewInit has already run, leaving clerkSignInEl permanently undefined.

Fixed in 36b5452 by replacing the @ViewChild property + ngAfterViewInit pattern with a @ViewChild setter. Angular calls the setter each time the reference enters or leaves the DOM, so the moment *ngIf renders the div, the setter fires with the live ElementRef and immediately calls mountClerkUI().

@highperformancecoder
Copy link
Copy Markdown
Owner

@copilot ERROR Error: Clerk was not loaded with Ui components
at _ClerkService.mountSignIn (clerk.service.ts:36:16)
at _LoginComponent.mountClerkUI (login.component.ts:93:23)
at set clerkSignInEl (login.component.ts:29:12)
at LoginComponent_Query (login.component.ts:21:28)

…ling

Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/ea0b4096-a9bc-42da-9917-0e8a6054c4d8

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 12, 2026

@copilot ERROR Error: Clerk was not loaded with Ui components
at _ClerkService.mountSignIn (clerk.service.ts:36:16)
at _LoginComponent.mountClerkUI (login.component.ts:93:23)
...

Fixed in 55794f6 with two changes:

Root cause: Clerk v6 uses an internal inBrowser() check during load() to decide whether to initialise the UI component renderer (#componentControls). In Electron's renderer process this detection can return false (e.g. when the page is loaded from file://, giving a null origin), which leaves #componentControls null while #loaded is still set — so mountSignIn throws "not loaded with Ui components" even though load() appeared to succeed.

Fix 1 — clerk.service.ts: Pass { standardBrowser: true } to clerk.load(). This explicitly overrides the environment detection and always initialises the UI component renderer.

Fix 2 — login.component.ts: Separated the single try/catch into two: one for initialize() (if Clerk itself fails to load, we bail immediately rather than still trying to mount UI), and one for the session/auth logic. Also added a try/catch inside mountClerkUI() so any remaining Clerk-internal errors surface as a friendly error message instead of an unhandled exception.

Copilot AI and others added 2 commits April 13, 2026 00:18
…tron

Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/d833ade0-d70c-484f-abc9-cb1e3ad7bda9

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
…ndow.Clerk

Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/d833ade0-d70c-484f-abc9-cb1e3ad7bda9

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/683c09f3-9471-4c6c-b16d-218c6a2dfa72

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/1482d1eb-94a0-43bb-bafc-69ec8f02418d

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
…ron auth

Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/ef399f0f-200a-4aeb-81c0-f9660582d2c5

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
…d with Ui components')

Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/7da5d79d-7c5f-4968-8943-58398993784d

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
… derivation)

Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/7a238887-8850-4b05-95ab-e2adf8d60497

Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants