Skip to content

Commit 5d89be8

Browse files
author
Lasim
committed
feat(gateway): implement automatic device registration during OAuth2 flow
1 parent 481ee39 commit 5d89be8

File tree

3 files changed

+37
-35
lines changed

3 files changed

+37
-35
lines changed

services/gateway/src/commands/login.ts

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { DeployStackAPI } from '../core/auth/api-client';
77
import { MCPConfigService } from '../core/mcp';
88
import { AuthenticationError } from '../types/auth';
99
import { ServerStartService } from '../services/server-start-service';
10-
import { detectDeviceInfo } from '../utils/device-detection';
1110

1211
export function registerLoginCommand(program: Command) {
1312
program
@@ -44,33 +43,13 @@ export function registerLoginCommand(program: Command) {
4443
timeout: 120000 // 2 minutes
4544
});
4645

47-
spinner = ora('Registering device...').start();
46+
spinner = ora('Storing credentials securely...').start();
4847

49-
// NEW: Automatic device registration
50-
try {
51-
const api = new DeployStackAPI(authResult.credentials, options.url);
52-
const deviceInfo = await detectDeviceInfo();
53-
const device = await api.registerOrUpdateDevice(deviceInfo);
54-
55-
spinner.text = 'Storing credentials securely...';
56-
57-
// Store credentials with device context
58-
await storage.storeCredentials({
59-
...authResult.credentials,
60-
deviceId: device.id
61-
});
62-
63-
console.log(chalk.green(`📱 Device registered: ${device.device_name}`));
64-
} catch (deviceError) {
65-
// If device registration fails, continue without it
66-
spinner.text = 'Storing credentials securely...';
67-
await storage.storeCredentials(authResult.credentials);
68-
69-
console.log(chalk.yellow('⚠️ Device registration failed - continuing without device context'));
70-
if (deviceError instanceof Error) {
71-
console.log(chalk.gray(` Device Error: ${deviceError.message}`));
72-
}
73-
}
48+
// Device registration now happens automatically during OAuth2 token exchange
49+
// Store credentials with device context if available from OAuth2 response
50+
const credentialsToStore = authResult.credentials;
51+
52+
await storage.storeCredentials(credentialsToStore);
7453

7554
// Set default team as selected team and download MCP config
7655
spinner.text = 'Setting up default team...';

services/gateway/src/core/auth/oauth.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { generateCodeVerifier, generateCodeChallenge, generateState } from '../.
44
import { buildAuthConfig } from '../../utils/auth-config';
55
import { CallbackServer } from './callback-server';
66
import { BrowserManager } from './browser';
7+
import { detectDeviceInfo } from '../../utils/device-detection';
78
import {
89
AuthenticationResult,
910
AuthenticationOptions,
@@ -158,19 +159,31 @@ export class OAuth2Client {
158159
codeVerifier: string;
159160
}): Promise<TokenResponse> {
160161
try {
162+
// Detect device information for automatic registration
163+
const deviceInfo = await detectDeviceInfo();
164+
165+
// Add device_name field required by backend schema
166+
const deviceInfoWithName = {
167+
device_name: deviceInfo.hostname, // Use hostname as default device name
168+
...deviceInfo
169+
};
170+
171+
const requestBody = {
172+
grant_type: 'authorization_code',
173+
code: params.code,
174+
redirect_uri: this.config.redirectUri,
175+
client_id: this.config.clientId,
176+
code_verifier: params.codeVerifier,
177+
device_info: deviceInfoWithName
178+
};
179+
161180
const response = await fetch(this.config.tokenUrl, {
162181
method: 'POST',
163182
headers: {
164183
'Content-Type': 'application/json',
165184
'User-Agent': 'DeployStack-Gateway-CLI/0.2.0'
166185
},
167-
body: JSON.stringify({
168-
grant_type: 'authorization_code',
169-
code: params.code,
170-
redirect_uri: this.config.redirectUri,
171-
client_id: this.config.clientId,
172-
code_verifier: params.codeVerifier
173-
})
186+
body: JSON.stringify(requestBody)
174187
});
175188

176189
if (!response.ok) {
@@ -182,7 +195,14 @@ export class OAuth2Client {
182195
);
183196
}
184197

185-
return await response.json() as TokenResponse;
198+
const tokenResponse = await response.json() as TokenResponse;
199+
200+
// Log device registration success if device info is included in response
201+
if (tokenResponse.device) {
202+
console.log(chalk.green(`📱 Device registered: ${tokenResponse.device.device_name}`));
203+
}
204+
205+
return tokenResponse;
186206
} catch (error) {
187207
if (error instanceof AuthenticationError) {
188208
throw error;

services/gateway/src/types/auth.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Device } from '../utils/device-detection';
2+
13
export interface StoredCredentials {
24
accessToken: string;
35
refreshToken: string;
@@ -33,6 +35,7 @@ export interface TokenResponse {
3335
expires_in: number;
3436
refresh_token: string;
3537
scope: string;
38+
device?: Device;
3639
}
3740

3841
export interface TokenInfo {

0 commit comments

Comments
 (0)