fix: validate clientMetadataUrl at construction time (fail-fast) (#1653)
Co-authored-by: Luis <reche@Luiss-MacBook-Pro.local> Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
This commit is contained in:
parent
595652ceca
commit
6bec24a14c
1 changed files with 113 additions and 2 deletions
|
|
@ -18,7 +18,8 @@ import {
|
|||
refreshAuthorization,
|
||||
registerClient,
|
||||
selectClientAuthMethod,
|
||||
startAuthorization
|
||||
startAuthorization,
|
||||
validateClientMetadataUrl
|
||||
} from '../../src/client/auth.js';
|
||||
import { createPrivateKeyJwtAuth } from '../../src/client/authExtensions.js';
|
||||
|
||||
|
|
@ -3833,6 +3834,80 @@ describe('OAuth Authorization', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('validateClientMetadataUrl', () => {
|
||||
it('passes for valid HTTPS URL with path', () => {
|
||||
expect(() => validateClientMetadataUrl('https://client.example.com/.well-known/oauth-client')).not.toThrow();
|
||||
});
|
||||
|
||||
it('passes for valid HTTPS URL with multi-segment path', () => {
|
||||
expect(() => validateClientMetadataUrl('https://example.com/clients/metadata.json')).not.toThrow();
|
||||
});
|
||||
|
||||
it('throws OAuthError for HTTP URL', () => {
|
||||
expect(() => validateClientMetadataUrl('http://client.example.com/.well-known/oauth-client')).toThrow(OAuthError);
|
||||
try {
|
||||
validateClientMetadataUrl('http://client.example.com/.well-known/oauth-client');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(OAuthError);
|
||||
expect((error as OAuthError).code).toBe(OAuthErrorCode.InvalidClientMetadata);
|
||||
expect((error as OAuthError).message).toContain('http://client.example.com/.well-known/oauth-client');
|
||||
}
|
||||
});
|
||||
|
||||
it('throws OAuthError for non-URL string', () => {
|
||||
expect(() => validateClientMetadataUrl('not-a-url')).toThrow(OAuthError);
|
||||
try {
|
||||
validateClientMetadataUrl('not-a-url');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(OAuthError);
|
||||
expect((error as OAuthError).code).toBe(OAuthErrorCode.InvalidClientMetadata);
|
||||
expect((error as OAuthError).message).toContain('not-a-url');
|
||||
}
|
||||
});
|
||||
|
||||
it('passes silently for empty string', () => {
|
||||
expect(() => validateClientMetadataUrl('')).not.toThrow();
|
||||
});
|
||||
|
||||
it('throws OAuthError for root-path HTTPS URL with trailing slash', () => {
|
||||
expect(() => validateClientMetadataUrl('https://client.example.com/')).toThrow(OAuthError);
|
||||
try {
|
||||
validateClientMetadataUrl('https://client.example.com/');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(OAuthError);
|
||||
expect((error as OAuthError).code).toBe(OAuthErrorCode.InvalidClientMetadata);
|
||||
expect((error as OAuthError).message).toContain('https://client.example.com/');
|
||||
}
|
||||
});
|
||||
|
||||
it('throws OAuthError for root-path HTTPS URL without trailing slash', () => {
|
||||
expect(() => validateClientMetadataUrl('https://client.example.com')).toThrow(OAuthError);
|
||||
try {
|
||||
validateClientMetadataUrl('https://client.example.com');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(OAuthError);
|
||||
expect((error as OAuthError).code).toBe(OAuthErrorCode.InvalidClientMetadata);
|
||||
expect((error as OAuthError).message).toContain('https://client.example.com');
|
||||
}
|
||||
});
|
||||
|
||||
it('passes silently for undefined', () => {
|
||||
expect(() => validateClientMetadataUrl(undefined)).not.toThrow();
|
||||
});
|
||||
|
||||
it('error message matches expected format', () => {
|
||||
expect(() => validateClientMetadataUrl('http://example.com/path')).toThrow(OAuthError);
|
||||
try {
|
||||
validateClientMetadataUrl('http://example.com/path');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(OAuthError);
|
||||
expect((error as OAuthError).message).toBe(
|
||||
'clientMetadataUrl must be a valid HTTPS URL with a non-root pathname, got: http://example.com/path'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('determineScope', () => {
|
||||
const baseClientMetadata = {
|
||||
redirect_uris: ['http://localhost:3000/callback'],
|
||||
|
|
|
|||
Loading…
Reference in a new issue