loginWithPassword method
Future<AuthLoginResult>
loginWithPassword({
- required Dio apiDio,
- required AuthRestClient auth,
- required String email,
- required String password,
- required String keyName,
Order matters: verification is checked, then mustChangePassword,
before anything destructive — an unverified or must-change-password
account must never lose a working key on its way to being rejected.
Rotation follows: the existing cli:<hostname> key is revoked before
the new one is minted, because a duplicate active label 400s the
create call.
Implementation
Future<AuthLoginResult> loginWithPassword({
required Dio apiDio,
required AuthRestClient auth,
required String email,
required String password,
required String keyName,
}) async {
final SignInResult signIn;
try {
signIn = await auth.signInWithPassword(email, password);
} on AuthRestException catch (e) {
throw _mapSignInFailure(e);
}
final bool verified;
try {
verified = await auth.lookup(signIn.idToken);
} on AuthRestException catch (e) {
throw AuthCliFault(
ExitCodes.usage,
'Could not verify account status: ${e.message}',
errorCode: 'unknown',
);
}
if (!verified) {
throw const AuthCliFault(
ExitCodes.authRejected,
_emailNotVerifiedMessage,
errorCode: 'email_not_verified',
);
}
final options = Options(
headers: {'Authorization': 'Bearer ${signIn.idToken}'},
);
final who = await _whoami(apiDio, options);
if (who.mustChangePassword) {
throw const AuthCliFault(
ExitCodes.authRejected,
'Set a password in the AppFlight app before using the CLI: '
'Settings → Account.\n'
' Get the app: ${AppLinks.playStore}',
errorCode: 'must_change_password',
);
}
final existing = await _findKeyByName(apiDio, options, keyName);
await _revokeIfPresent(apiDio, options, existing);
final created = await _createKey(apiDio, options, keyName);
return AuthLoginResult(
apiKey: created['rawKey'] as String,
uid: who.uid,
email: who.email,
keyId: created['id'] as String,
keyName: created['name'] as String,
);
}