feat: add support for extracting jwt from http headers (#3109)
This commit is contained in:
@@ -59,6 +59,48 @@ class JwtVerificationFailure implements Exception {
|
||||
String toString() => 'JwtVerificationFailure: $reason';
|
||||
}
|
||||
|
||||
/// {@template jwt_extraction_failure}
|
||||
/// An exception thrown during JWT extraction.
|
||||
/// {@endtemplate}
|
||||
class JwtExtractionFailure implements Exception {
|
||||
/// {@macro jwt_extraction_failure}
|
||||
const JwtExtractionFailure(this.reason);
|
||||
|
||||
/// The reason for the extraction failure.
|
||||
final String reason;
|
||||
|
||||
@override
|
||||
String toString() => 'JwtExtractionFailure: $reason';
|
||||
}
|
||||
|
||||
/// Extracts a JWT from the Authorization header of the request. Throws a
|
||||
/// [JwtExtractionFailure] if the header is missing or the token is malformed.
|
||||
/// Does NOT verify the JWT.
|
||||
Jwt extractFromRequestHeaders(Map<String, String> headers) {
|
||||
final caseInsensitiveHeaders = headers.map(
|
||||
(key, value) => MapEntry(key.toLowerCase(), value),
|
||||
);
|
||||
final authorization = caseInsensitiveHeaders[HttpHeaders.authorizationHeader];
|
||||
if (authorization == null) {
|
||||
throw const JwtExtractionFailure('Missing authorization header');
|
||||
}
|
||||
|
||||
final tokenRegExp = RegExp('^Bearer (.*)');
|
||||
final token = tokenRegExp.firstMatch(authorization)?.group(1);
|
||||
if (token == null) {
|
||||
throw const JwtExtractionFailure('Malformed authorization header');
|
||||
}
|
||||
|
||||
final Jwt jwt;
|
||||
try {
|
||||
jwt = Jwt.parse(token);
|
||||
} on Exception {
|
||||
throw const JwtExtractionFailure('Malformed JWT');
|
||||
}
|
||||
|
||||
return jwt;
|
||||
}
|
||||
|
||||
/// Verify the encoded [encodedJwt].
|
||||
Future<Jwt> verify(
|
||||
String encodedJwt, {
|
||||
|
||||
@@ -43,6 +43,80 @@ void main() {
|
||||
};
|
||||
});
|
||||
|
||||
group(JwtExtractionFailure, () {
|
||||
group('toString', () {
|
||||
test('returns the reason', () {
|
||||
const reason = 'reason';
|
||||
const failure = JwtExtractionFailure(reason);
|
||||
expect(failure.toString(), equals('JwtExtractionFailure: $reason'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('extractFromRequestHeaders', () {
|
||||
group('when no authorization header is provided', () {
|
||||
test('throws JwtExtractionFailure', () {
|
||||
expect(
|
||||
() => extractFromRequestHeaders({}),
|
||||
throwsA(
|
||||
isA<JwtExtractionFailure>().having(
|
||||
(e) => e.reason,
|
||||
'reason',
|
||||
'Missing authorization header',
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('when authorization header is malformed', () {
|
||||
test('throws JwtExtractionFailure', () {
|
||||
expect(
|
||||
() => extractFromRequestHeaders({
|
||||
HttpHeaders.authorizationHeader: 'not-a-jwt',
|
||||
}),
|
||||
throwsA(
|
||||
isA<JwtExtractionFailure>().having(
|
||||
(e) => e.reason,
|
||||
'reason',
|
||||
'Malformed authorization header',
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('when authorization header cannot be parsed into jwt', () {
|
||||
test('throws JwtExtractionFailure', () {
|
||||
expect(
|
||||
() => extractFromRequestHeaders({
|
||||
HttpHeaders.authorizationHeader: 'Bearer not-a-jwt',
|
||||
}),
|
||||
throwsA(
|
||||
isA<JwtExtractionFailure>().having(
|
||||
(e) => e.reason,
|
||||
'reason',
|
||||
'Malformed JWT',
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('when authorization header contains a valid jwt', () {
|
||||
test('returns a jwt', () {
|
||||
final jwt = extractFromRequestHeaders({
|
||||
HttpHeaders.authorizationHeader: 'Bearer $token',
|
||||
});
|
||||
expect(jwt.payload.aud, equals('my-app'));
|
||||
expect(
|
||||
jwt.payload.iss,
|
||||
equals('https://securetoken.google.com/my-app'),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('verify', () {
|
||||
group('when key store is key-value', () {
|
||||
const issuer = 'https://securetoken.google.com/my-app';
|
||||
|
||||
Reference in New Issue
Block a user