8000 Show token expiration date in options (#8847) · refined-github/refined-github@e20eff5 · GitHub
[go: up one dir, main page]

Skip to content

Commit e20eff5

Browse files
Copilotfregante
andauthored
Show token expiration date in options (#8847)
Co-authored-by: Federico <me@fregante.com>
1 parent dcca4d0 commit e20eff5

File tree

2 files changed

+29
-8
lines changed

2 files changed

+29
-8
lines changed

source/github-helpers/github-token.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,17 @@ function parseTokenScopes(headers: Headers): string[] {
9494
return scopes;
9595
}
9696

97-
export async function getTokenScopes(apiBase: string, personalToken: string): Promise<string[]> {
97+
type TokenInfo = {
98+
scopes: string[];
99+
expiration?: string;
100+
};
101+
102+
export async function getTokenInfo(apiBase: string, personalToken: string): Promise<TokenInfo> {
98103
const response = await baseApiFetch({apiBase, token: personalToken, path: ''});
99-
return parseTokenScopes(response.headers);
104+
return {
105+
scopes: parseTokenScopes(response.headers),
106+
expiration: response.headers.get('GitHub-Authentication-Token-Expiration') ?? undefined,
107+
};
100108
}
101109

102110
export async function expectTokenScope(scope: string): Promise<void> {
@@ -105,7 +113,7 @@ export async function expectTokenScope(scope: string): Promise<void> {
105113
? `${location.origin}/api/v3/`
106114
: 'https://api.github.com/';
107115

108-
const tokenScopes = await getTokenScopes(api, token);
116+
const {scopes: tokenScopes} = await getTokenInfo(api, token);
109117
if (!tokenScopes.includes(scope)) {
110118
throw new Error('The token you provided does not have ' + (tokenScopes.length > 0 ? `the \`${scope}\` scope. It only includes \`${tokenScopes.join(', ')}\`.` : 'any scope. You can change the scope of your token at https://github.com/settings/tokens'));
111119
}

source/options/token-validation.tsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import {$} from 'select-dom/strict.js';
44
import {assertError} from 'ts-extras';
55
import type {SyncedForm} from 'webext-options-sync-per-domain';
66

7-
import {getTokenScopes, tokenUser} from '../github-helpers/github-token.js';
7+
import {getTokenInfo, tokenUser} from '../github-helpers/github-token.js';
88
import delay from '../helpers/delay.js';
99

10+
const rtf = new Intl.RelativeTimeFormat('en', {numeric: 'auto'});
11+
1012
type Status = {
1113
error?: true;
1214
text?: string;
@@ -57,13 +59,24 @@ async function validateToken(): Promise<void> {
5759

5860
try {
5961
const base = getApiUrl();
60-
const [scopes, user] = await Promise.all([
61-
getTokenScopes(base, tokenField.value),
62+
const [tokenInfo, user] = await Promise.all([
63+
getTokenInfo(base, tokenField.value),
6264
tokenUser.get(base, tokenField.value),
6365
]);
66+
67+
// Build status message with user and expiration
68+
let statusMessage = `👤 @${user}`;
69+
if (tokenInfo.expiration) {
70+
const msUntilExpiration = new Date(tokenInfo.expiration).getTime() - Date.now();
71+
const daysUntilExpiration = Math.ceil(msUntilExpiration / (1000 * 60 * 60 * 24));
72+
statusMessage += `, expires ${rtf.format(daysUntilExpiration, 'day')}`;
73+
} else {
74+
statusMessage += ', no expiration';
75+
}
76+
6477
reportStatus({
65-
text: `👤 @${user}`,
66-
scopes,
78+
text: statusMessage,
79+
scopes: tokenInfo.scopes,
6780
});
6881
} catch (error) {
6982
assertError(error);

0 commit comments

Comments
 (0)
0