Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 48 additions & 8 deletions src/cli/validateToken.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { API as APIInterface } from '@metacall/protocol/protocol';
import {
API as APIInterface,
isProtocolError,
ProtocolError
} from '@metacall/protocol/protocol';
import { unlink } from 'fs/promises';
import { configFilePath, save } from '../config';
import { exists } from '../utils';
import args from './args';
import { error, info } from './messages';
import { error, info, warn } from './messages';

const handleValidateToken = async (api: APIInterface): Promise<void> => {
const validToken = await api.validate();
Expand All @@ -14,6 +18,11 @@ const handleValidateToken = async (api: APIInterface): Promise<void> => {
}
};

const invalidateConfig = async (): Promise<void> => {
const configFile = configFilePath();
(await exists(configFile)) && (await unlink(configFile));
};

const validateToken = async (api: APIInterface): Promise<void> => {
try {
await handleValidateToken(api);
Expand All @@ -26,18 +35,49 @@ const validateToken = async (api: APIInterface): Promise<void> => {
return error('FaaS is not serving locally.');
}

// Removing cache such that user will have to login again.
// Check if this is a transient network/server error (non-auth)
if (!isProtocolError(err)) {
// Network error, DNS failure, etc. — preserve config
warn('Unable to reach the server. Using cached credentials.');
return;
}

const configFile = configFilePath();
const protocolErr = err;
const status = protocolErr.status;

(await exists(configFile)) && (await unlink(configFile));
// Only delete config for auth-specific errors (401, 403)
// or JWT-specific errors (invalid signature, expired)
const isAuthError =
status === 401 ||
status === 403 ||
(status === undefined && isJwtError(protocolErr));

info('Try to login again!');
if (isAuthError) {
await invalidateConfig();
info('Try to login again!');
return error(
`Token validation failed, potential causes include:\n\t1) The JWT may be mistranslated (Invalid Signature).\n\t2) JWT might have expired.`
);
}

return error(
`Token validation failed, potential causes include:\n\t1) The JWT may be mistranslated (Invalid Signature).\n\t2) JWT might have expired.`
// Server error (5xx) or other non-auth failure — preserve config
warn(
`Server error (${status ?? 'unknown'}). Using cached credentials.`
);
}
};

const isJwtError = (err: ProtocolError): boolean => {
const message = (err.message ?? '').toLowerCase();
const data = String(err.data ?? '').toLowerCase();
return (
message.includes('jwt') ||
message.includes('token') ||
data.includes('jwt') ||
data.includes('token') ||
data.includes('malformed') ||
data.includes('expired')
);
};

export default validateToken;