authenticate method

Future<ConnectanumHttpAuthGrant> authenticate({
  1. required String realm,
  2. required String authId,
  3. required AbstractAuthentication authentication,
  4. String? authMethod,
  5. Map<String, Object?> authextra = const <String, Object?>{},
  6. Map<String, String> headers = const <String, String>{},
})

Implementation

Future<ConnectanumHttpAuthGrant> authenticate({
  required String realm,
  required String authId,
  required AbstractAuthentication authentication,
  String? authMethod,
  Map<String, Object?> authextra = const <String, Object?>{},
  Map<String, String> headers = const <String, String>{},
}) {
  return _runTrackedOperation((openRequest) async {
    try {
      final requestRealm = _nonEmptyArgument(realm, 'realm');
      final requestAuthId = _nonEmptyArgument(authId, 'authId');
      final method = _httpAuthMethodName(
        authMethod ?? authentication.getName(),
      );
      final details = Details.forHello()
        ..authid = requestAuthId
        ..authmethods = <String>[method];
      if (authextra.isNotEmpty) {
        details.authextra = Map<String, dynamic>.from(authextra);
      }
      await authentication.hello(requestRealm, details);
      final normalizedAuthId = details.authid ?? requestAuthId;

      final startBody = <String, Object?>{
        'realm': requestRealm,
        'authmethod': method,
        'authid': normalizedAuthId,
        if (details.authextra != null && details.authextra!.isNotEmpty)
          'authextra': Map<String, Object?>.from(details.authextra!),
      };
      final challenge = await _postJsonObject(
        startBody,
        expectedStatus: HttpStatus.unauthorized,
        label: 'HTTP auth challenge',
        extraHeaders: headers,
        openRequest: openRequest,
      );
      _validateAuthResponseIdentity(
        challenge,
        label: 'HTTP auth challenge',
        realm: requestRealm,
        authMethod: method,
      );
      final state = _nonEmptyString(challenge['state'], 'state');
      final authenticate = await authentication.challenge(
        _challengeExtraFrom(challenge['challenge']),
      );

      final grant = await _postJsonObject(
        <String, Object?>{
          'state': state,
          if (authenticate.signature != null)
            'signature': authenticate.signature,
          if (authenticate.extra != null)
            'extra': Map<String, Object?>.from(authenticate.extra!),
        },
        expectedStatus: HttpStatus.ok,
        label: 'HTTP auth token request',
        extraHeaders: headers,
        openRequest: openRequest,
      );
      final parsedGrant = ConnectanumHttpAuthGrant.fromJson(
        grant,
        authEndpoint: endpoint,
      );
      _validateAuthResponseIdentity(
        grant,
        label: 'HTTP auth grant',
        realm: requestRealm,
        authMethod: method,
        authId: normalizedAuthId,
      );
      final finalAuthExtra = parsedGrant.details['authextra'];
      Map<String, Object?>? verifierFields;
      if (finalAuthExtra is Map) {
        verifierFields = <String, Object?>{
          for (final entry in finalAuthExtra.entries)
            if (entry.key is String) entry.key as String: entry.value,
        };
      }
      await authentication.verifyFinal(
        authId: parsedGrant.authId,
        authMethod: authentication.getName(),
        authExtra: verifierFields,
      );
      return parsedGrant;
    } on Object {
      await authentication.cancelPendingChallenge();
      rethrow;
    }
  });
}