Skip to content

Commit b1d222b

Browse files
Merge pull request #642 from highperformancecoder/copilot/preserve-clerk-session-login-window
Preserve Clerk session between login window opens
2 parents 5936fe1 + 7d51337 commit b1d222b

3 files changed

Lines changed: 46 additions & 5 deletions

File tree

gui-js/apps/minsky-electron/src/app/managers/WindowManager.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,12 +389,14 @@ export class WindowManager {
389389
WindowManager._resolveAuthToken = resolve;
390390
});
391391

392+
const existingToken = StoreManager.store.get('authToken') || '';
393+
392394
const loginWindow = WindowManager.createPopupWindowWithRouting({
393395
width: 420,
394396
height: 500,
395397
title: 'Login',
396398
modal: false,
397-
url: '#/headless/login',
399+
url: `#/headless/login?authToken=${encodeURIComponent(existingToken)}`,
398400
});
399401

400402
// Resolve with null if the user closes the window before authenticating

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,18 @@ export class ClerkService {
5959
const token = await this.getToken();
6060
await this.electronService.invoke(events.SET_AUTH_TOKEN, token);
6161
}
62+
63+
async setSession(token: string): Promise<void> {
64+
if (!this.clerk) throw new Error('Clerk not initialized');
65+
// clerk.load() in initialize() restores the session from browser storage if still valid.
66+
// If no session is active but sessions exist on the client, activate the first available one.
67+
// The token parameter is a hint that the user previously authenticated.
68+
if (!token) return;
69+
if (!this.clerk.session && this.clerk.client?.sessions?.length > 0) {
70+
await this.clerk.setActive({ session: this.clerk.client.sessions[0].id });
71+
}
72+
if (!this.clerk.session) {
73+
throw new Error('Session expired or invalid');
74+
}
75+
}
6276
}

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

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, OnInit } from '@angular/core';
1+
import { Component, OnInit, OnDestroy } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { FormControl, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
44
import { MatButtonModule } from '@angular/material/button';
@@ -7,6 +7,8 @@ import { MatFormFieldModule } from '@angular/material/form-field';
77
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
88
import { ClerkService } from '@minsky/core';
99
import { ElectronService } from '@minsky/core';
10+
import { ActivatedRoute } from '@angular/router';
11+
import { Subject, take } from 'rxjs';
1012

1113
@Component({
1214
selector: 'minsky-login',
@@ -22,7 +24,7 @@ import { ElectronService } from '@minsky/core';
2224
MatProgressSpinnerModule,
2325
],
2426
})
25-
export class LoginComponent implements OnInit {
27+
export class LoginComponent implements OnInit, OnDestroy {
2628
loginForm = new FormGroup({
2729
email: new FormControl('', [Validators.required, Validators.email]),
2830
password: new FormControl('', [Validators.required]),
@@ -32,17 +34,40 @@ export class LoginComponent implements OnInit {
3234
errorMessage = '';
3335
isAuthenticated = false;
3436

35-
constructor(private clerkService: ClerkService, private electronService: ElectronService) {}
37+
private destroy$ = new Subject<void>();
38+
39+
constructor(
40+
private clerkService: ClerkService,
41+
private electronService: ElectronService,
42+
private route: ActivatedRoute
43+
) {}
3644

3745
async ngOnInit() {
46+
this.route.queryParams.pipe(take(1)).subscribe((params) => {
47+
this.initializeSession(params['authToken']);
48+
});
49+
}
50+
51+
private async initializeSession(authToken: string | undefined) {
3852
try {
3953
await this.clerkService.initialize();
54+
55+
if (authToken) {
56+
await this.clerkService.setSession(authToken);
57+
}
58+
4059
this.isAuthenticated = await this.clerkService.isSignedIn();
4160
} catch (err) {
42-
this.errorMessage = 'Failed to initialize authentication.';
61+
this.errorMessage = 'Session expired. Please sign in again.';
62+
this.isAuthenticated = false;
4363
}
4464
}
4565

66+
ngOnDestroy() {
67+
this.destroy$.next();
68+
this.destroy$.complete();
69+
}
70+
4671
get email() {
4772
return this.loginForm.get('email');
4873
}

0 commit comments

Comments
 (0)