OidcBrowser opens the page of an attempt the server began (OidcStart), waits until the browser comes back by an OidcReturn (the app's scheme, a verified https address, or the loopback) and returns the provider's code (OidcCode), its refusal (OidcDenied), or nothing when the person closed the browser. It checks the returned state against the attempt's, as the server never compares it; a wrong state, neither one code nor one refusal, no browser or a failed way back are OidcCallbackExceptions. halkyon_app's LoopbackWebAuth moves in with its tests, relicensed from AGPL v3 to BSD-3-Clause by its author, the developer; GitHub's sign-in keeps using it from there until it ends. The package is BSD-3-Clause, publish_to: none, as the module (ADR 0023, D2, D3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
80 lines
2.2 KiB
Dart
80 lines
2.2 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:halkyon_oidc_flutter/halkyon_oidc_flutter.dart';
|
|
|
|
void main() {
|
|
const state = 'app-scheme.q3J0aGVyZQ';
|
|
|
|
OidcCallback parse(String query, {String base = 'my.app://oidc-callback'}) =>
|
|
OidcCallback.parse('$base?$query', state: state);
|
|
|
|
test('reads the code that came back for the sign-in', () {
|
|
expect(parse('code=abc&state=$state'), const OidcCode('abc'));
|
|
});
|
|
|
|
test('reads it from the loopback and from an https address alike', () {
|
|
for (final base in [
|
|
'http://127.0.0.1:43823/oidc-callback',
|
|
'https://example.org/oidc-callback',
|
|
]) {
|
|
expect(
|
|
parse('state=$state&code=abc', base: base),
|
|
const OidcCode('abc'),
|
|
reason: base,
|
|
);
|
|
}
|
|
});
|
|
|
|
test("reads the provider's refusal", () {
|
|
expect(
|
|
parse('error=access_denied&state=$state'),
|
|
const OidcDenied('access_denied'),
|
|
);
|
|
// SimpleLogin's own.
|
|
expect(parse('error=deny&state=$state'), const OidcDenied('deny'));
|
|
});
|
|
|
|
test('refuses what came back for another sign-in, or for none', () {
|
|
for (final query in [
|
|
'code=abc&state=app-scheme.other',
|
|
'code=abc&state=',
|
|
'code=abc',
|
|
'error=deny&state=app-scheme.other',
|
|
'error=deny',
|
|
]) {
|
|
expect(
|
|
() => parse(query),
|
|
throwsA(_problem(OidcCallbackProblem.wrongState)),
|
|
reason: query,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('refuses what is neither a code nor a refusal', () {
|
|
for (final query in [
|
|
'state=$state',
|
|
'code=&state=$state',
|
|
'error=&state=$state',
|
|
'code=abc&error=deny&state=$state',
|
|
'code=a&code=b&state=$state',
|
|
'error=deny&error=deny&state=$state',
|
|
'code=abc&state=$state&state=$state',
|
|
]) {
|
|
expect(
|
|
() => parse(query),
|
|
throwsA(_problem(OidcCallbackProblem.malformed)),
|
|
reason: query,
|
|
);
|
|
}
|
|
});
|
|
|
|
test("refuses an address that can't be read", () {
|
|
// A UTF-8 sequence cut short.
|
|
expect(
|
|
() => parse('code=%E0%A4&state=$state'),
|
|
throwsA(_problem(OidcCallbackProblem.malformed)),
|
|
);
|
|
});
|
|
}
|
|
|
|
Matcher _problem(OidcCallbackProblem problem) => isA<OidcCallbackException>()
|
|
.having((error) => error.problem, 'problem', problem);
|