Skip to content

Commit b225233

Browse files
authored
show-authorized-user: display OAuth client ID and source (#1116)
* feat: show OAuth client details in show-authorized-user Extend show-authorized-user output to include the active OAuth client ID and classify it as google-provided or user-provided for quick visibility. Centralize default OAuth client constants so auth creation, legacy credential loading, and command-level classification stay aligned over time. * docs: document show-authorized-user client output Add show-authorized-user to the command list and reference section in README. Document the new client ID and clientType output, including json behavior. Keep command docs aligned with the implemented authorization status output.
1 parent 732db4c commit b225233

7 files changed

Lines changed: 106 additions & 4 deletions

File tree

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ clasp
116116

117117
- [`clasp login [--no-localhost] [--creds <file>] [--redirect-port]`](#login)
118118
- [`clasp logout`](#logout)
119+
- [`clasp show-authorized-user [--json]`](#show-authorized-user)
119120
- [`clasp create-script [--title <title>] [--type <type>] [--rootDir <dir>] [--parentId <id>]`](#create)
120121
- [`clasp clone-script <scriptId | scriptURL> [versionNumber] [--rootDir <dir>]`](#clone)
121122
- [`clasp delete-script [--force]`](#delete)
@@ -381,6 +382,21 @@ Logs out the user by deleting client credentials.
381382

382383
- `clasp logout`
383384

385+
### Show Authorized User
386+
387+
Shows the current authorization status. When logged in, the output includes
388+
the OAuth client ID and whether the client is `google-provided` (clasp default)
389+
or `user-provided` (custom credentials).
390+
391+
#### Options
392+
393+
- `--json`: Output authorization details as JSON, including `clientId` and `clientType`.
394+
395+
#### Examples
396+
397+
- `clasp show-authorized-user`
398+
- `clasp show-authorized-user --json`
399+
384400
### Create
385401

386402
Creates a new script project. Prompts the user for the script type if not specified.

src/auth/auth.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {AuthorizationCodeFlow} from './auth_code_flow.js';
2525
import {CredentialStore} from './credential_store.js';
2626
import {FileCredentialStore} from './file_credential_store.js';
2727
import {LocalServerAuthorizationCodeFlow} from './localhost_auth_code_flow.js';
28+
import {DEFAULT_CLASP_OAUTH_CLIENT_ID, DEFAULT_CLASP_OAUTH_CLIENT_SECRET} from './oauth_client.js';
2829
import {ServerlessAuthorizationCodeFlow} from './serverless_auth_code_flow.js';
2930

3031
const debug = Debug('clasp:auth');
@@ -266,8 +267,8 @@ function createOauthClient(clientSecretPath: string) {
266267
function createDefaultOAuthClient() {
267268
// Default client
268269
const client = new OAuth2Client({
269-
clientId: '1072944905499-vm2v2i5dvn0a0d2o4ca36i1vge8cvbn0.apps.googleusercontent.com',
270-
clientSecret: 'v6V3fKV_zWU7iw1DrpO1rknX',
270+
clientId: DEFAULT_CLASP_OAUTH_CLIENT_ID,
271+
clientSecret: DEFAULT_CLASP_OAUTH_CLIENT_SECRET,
271272
redirectUri: 'http://localhost',
272273
});
273274
debug('Created built-in oauth client, id: %s', client._clientId);

src/auth/file_credential_store.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import fs from 'fs';
2020
import {CredentialStore, StoredCredential} from './credential_store.js';
21+
import {DEFAULT_CLASP_OAUTH_CLIENT_ID, DEFAULT_CLASP_OAUTH_CLIENT_SECRET} from './oauth_client.js';
2122

2223
// Initial .clasprc.json format, single credential per file
2324
type V1LocalFileFormat = {
@@ -163,8 +164,8 @@ export class FileCredentialStore implements CredentialStore {
163164
refresh_token: store.refresh_token,
164165
expiry_date: store.exprity_date,
165166
token_type: store.token_type,
166-
client_id: '1072944905499-vm2v2i5dvn0a0d2o4ca36i1vge8cvbn0.apps.googleusercontent.com',
167-
client_secret: 'v6V3fKV_zWU7iw1DrpO1rknX',
167+
client_id: DEFAULT_CLASP_OAUTH_CLIENT_ID,
168+
client_secret: DEFAULT_CLASP_OAUTH_CLIENT_SECRET,
168169
};
169170
}
170171
return null;

src/auth/oauth_client.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Shared constants and helpers for identifying clasp OAuth clients.
16+
17+
export const DEFAULT_CLASP_OAUTH_CLIENT_ID =
18+
'1072944905499-vm2v2i5dvn0a0d2o4ca36i1vge8cvbn0.apps.googleusercontent.com';
19+
export const DEFAULT_CLASP_OAUTH_CLIENT_SECRET = 'v6V3fKV_zWU7iw1DrpO1rknX';
20+
21+
export type OAuthClientType = 'google-provided' | 'user-provided';
22+
23+
export function getOAuthClientType(clientId?: string): OAuthClientType | undefined {
24+
if (!clientId) {
25+
return undefined;
26+
}
27+
return clientId === DEFAULT_CLASP_OAUTH_CLIENT_ID ? 'google-provided' : 'user-provided';
28+
}

src/commands/show-authorized-user.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import {Command} from 'commander';
1818
import {AuthInfo, getUserInfo} from '../auth/auth.js';
19+
import {getOAuthClientType} from '../auth/oauth_client.js';
1920
import {intl} from '../intl.js';
2021
import {GlobalOptions} from './utils.js';
2122

@@ -33,10 +34,15 @@ export const command = new Command('show-authorized-user')
3334
user = await getUserInfo(auth.credentials);
3435
}
3536

37+
const clientId = auth.credentials?._clientId;
38+
const clientType = getOAuthClientType(clientId);
39+
3640
if (options.json) {
3741
const output = {
3842
loggedIn: auth.credentials ? true : false,
3943
email: user?.email ?? undefined,
44+
clientId: clientId ?? undefined,
45+
clientType: clientType ?? undefined,
4046
};
4147
console.log(JSON.stringify(output, null, 2));
4248
return;
@@ -61,4 +67,14 @@ export const command = new Command('show-authorized-user')
6167
},
6268
);
6369
console.log(msg);
70+
const clientMsg = intl.formatMessage(
71+
{
72+
defaultMessage: 'OAuth client ID: {clientId} ({clientType}).',
73+
},
74+
{
75+
clientId: clientId ?? 'unknown',
76+
clientType: clientType ?? 'unknown',
77+
},
78+
);
79+
console.log(clientMsg);
6480
});

test/commands/show-authorized-user.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {fileURLToPath} from 'url';
2020
import {expect} from 'chai';
2121
import {afterEach, beforeEach, describe, it} from 'mocha';
2222
import mockfs from 'mock-fs';
23+
import {DEFAULT_CLASP_OAUTH_CLIENT_ID} from '../../src/auth/oauth_client.js';
2324
import {useChaiExtensions} from '../helpers.js';
2425
import {mockOAuthRefreshRequest, resetMocks, setupMocks} from '../mocks.js';
2526
import {runCommand} from './utils.js';
@@ -53,6 +54,31 @@ describe('Show authorized user command', function () {
5354
const out = await runCommand(['show-authorized-user', '--json']);
5455
const json = JSON.parse(out.stdout);
5556
expect(json.loggedIn).to.be.true;
57+
expect(json.clientId).to.equal(DEFAULT_CLASP_OAUTH_CLIENT_ID);
58+
expect(json.clientType).to.equal('google-provided');
59+
});
60+
61+
it('should show the oauth client id in text output', async function () {
62+
const out = await runCommand(['show-authorized-user']);
63+
expect(out.stdout).to.contain(`OAuth client ID: ${DEFAULT_CLASP_OAUTH_CLIENT_ID} (google-provided).`);
64+
});
65+
});
66+
67+
describe('With project, authenticated with user-provided client', function () {
68+
beforeEach(function () {
69+
mockfs({
70+
'.clasp.json': mockfs.load(path.resolve(__dirname, '../fixtures/dot-clasp-no-settings.json')),
71+
[path.resolve(os.homedir(), '.clasprc.json')]: mockfs.load(
72+
path.resolve(__dirname, '../fixtures/dot-clasprc-authenticated-custom-client.json'),
73+
),
74+
});
75+
});
76+
77+
it('should classify the oauth client as user-provided', async function () {
78+
const out = await runCommand(['show-authorized-user', '--json']);
79+
const json = JSON.parse(out.stdout);
80+
expect(json.loggedIn).to.be.true;
81+
expect(json.clientType).to.equal('user-provided');
5682
});
5783
});
5884
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"tokens": {
3+
"default": {
4+
"client_id": "123456789000-custom.apps.googleusercontent.com",
5+
"client_secret": "mock-secret",
6+
"type": "authorized_user",
7+
"refresh_token": "mock-refresh-token",
8+
"access_token": "mock-access-token",
9+
"token_type": "Bearer",
10+
"expiry_date": 1737951765115,
11+
"id_token": "mock-id-token"
12+
}
13+
}
14+
}

0 commit comments

Comments
 (0)