feat(auth)!: codes by mail no longer sign in
ADR 0017, step 3 (D4: nobody signs in with email codes yet, so the password replaces them outright). `emailCode.verifyCode`, `emailSignIn.signIn` and the switch token go, with the register's `verifySignInCode` and `completeEmailSwitch` and their operations between services. `emailCode.sendCode` stays, now without a session (signing up and getting back in have none), as what proves an address for a password's setup, a sign-up, a setup never finished and the resets; `accountRecovery.sendCode`, which did that for the resets, goes. The `switchChallengeId` column stays: Serverpod's challenges still link a completion token, and older servers read it during a deploy. The apps: halkyon_app's email sign-in (its bloc, sheet, gateway calls and the start screen's Continue with email) goes; Learn's profile and Todo's account sheet show `AccountSecuritySection` (a password set up by someone anonymous or signing in with GitHub, whose end it names; the authenticators; changing the password; new recovery codes and phrase), which reads anew after every change the repository makes. BREAKING CHANGE: Learn's `minimumAppBuild` is 22: builds up to 21 sign in with the endpoints that went. Todo was never released and stays at build 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
735dfa892a
commit
ae57cbe864
1 changed files with 429 additions and 3835 deletions
|
|
@ -19,7 +19,6 @@ class SessionBloc(final AuthRepository _auth, final SignedOutMark _mark)
|
|||
on<SessionEventSignInRequested>(_onSignInRequested);
|
||||
on<SessionEventStartedAnonymously>(_onStartedAnonymously);
|
||||
on<SessionEventGitHubRequested>(_onGitHubRequested);
|
||||
on<SessionEventEmailRequested>(_onEmailRequested);
|
||||
on<SessionEventSignedOut>(
|
||||
(event, emit) => emit(SessionStateSignedOut(problem: event.problem)),
|
||||
);
|
||||
|
|
@ -27,9 +26,6 @@ class SessionBloc(final AuthRepository _auth, final SignedOutMark _mark)
|
|||
on<SessionEventDeleteRequested>(_onDeleteRequested);
|
||||
}
|
||||
|
||||
/// Whether an email sign-in follows the anonymous start under way.
|
||||
var _thenEmail = false;
|
||||
|
||||
Future<void> _onStarted(
|
||||
SessionEventStarted event,
|
||||
Emitter<SessionState> emit,
|
||||
|
|
@ -49,7 +45,7 @@ class SessionBloc(final AuthRepository _auth, final SignedOutMark _mark)
|
|||
return const SessionStateSignedOut();
|
||||
}
|
||||
unawaited(_mark.write(signedOut: false));
|
||||
return _stateOf(status, signsInWithEmail: _thenEmail);
|
||||
return _stateOf(status);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -64,15 +60,10 @@ class SessionBloc(final AuthRepository _auth, final SignedOutMark _mark)
|
|||
return;
|
||||
}
|
||||
emit(const SessionStateLoading());
|
||||
// Read before signing in: the status change on the way shows the app
|
||||
// too, and must ask for the address as well.
|
||||
final email = _thenEmail;
|
||||
try {
|
||||
await _auth.ensureSignedIn();
|
||||
_thenEmail = false;
|
||||
emit(_stateOf(_auth.status, signsInWithEmail: email));
|
||||
emit(_stateOf(_auth.status));
|
||||
} on Object catch (error, st) {
|
||||
_thenEmail = false;
|
||||
addError(error, st);
|
||||
emit(const SessionStateFailed());
|
||||
}
|
||||
|
|
@ -112,17 +103,6 @@ class SessionBloc(final AuthRepository _auth, final SignedOutMark _mark)
|
|||
}
|
||||
}
|
||||
|
||||
/// Email codes are only taken from someone signed in: an anonymous start
|
||||
/// first, then the address, which switches to its person if it has one.
|
||||
Future<void> _onEmailRequested(
|
||||
SessionEventEmailRequested event,
|
||||
Emitter<SessionState> emit,
|
||||
) async {
|
||||
_thenEmail = true;
|
||||
await _mark.write(signedOut: false);
|
||||
add(const SessionEventSignInRequested());
|
||||
}
|
||||
|
||||
Future<void> _onSignOutRequested(
|
||||
SessionEventSignOutRequested event,
|
||||
Emitter<SessionState> emit,
|
||||
|
|
@ -151,14 +131,10 @@ class SessionBloc(final AuthRepository _auth, final SignedOutMark _mark)
|
|||
}
|
||||
}
|
||||
|
||||
SessionState _stateOf(AuthStatus status, {bool signsInWithEmail = false}) =>
|
||||
switch (status) {
|
||||
SignedIn() => SessionStateActive(
|
||||
status,
|
||||
signsInWithEmail: signsInWithEmail,
|
||||
),
|
||||
SignedOut() => const SessionStateSignedOut(),
|
||||
};
|
||||
SessionState _stateOf(AuthStatus status) => switch (status) {
|
||||
SignedIn() => SessionStateActive(status),
|
||||
SignedOut() => const SessionStateSignedOut(),
|
||||
};
|
||||
}
|
||||
|
||||
/// What happens to the session.
|
||||
|
|
@ -180,9 +156,6 @@ final class const SessionEventStartedAnonymously() extends SessionEvent;
|
|||
/// Sign in with GitHub, once signed out.
|
||||
final class const SessionEventGitHubRequested() extends SessionEvent;
|
||||
|
||||
/// Sign in with an email address, once signed out.
|
||||
final class const SessionEventEmailRequested() extends SessionEvent;
|
||||
|
||||
/// Signed out, [problem] if any saying why the last way to start failed.
|
||||
final class const SessionEventSignedOut({final SignedOutProblem? problem})
|
||||
extends SessionEvent {
|
||||
|
|
@ -216,14 +189,10 @@ final class const SessionStateFailed() extends SessionState {
|
|||
/// A person is signed in.
|
||||
final class const SessionStateActive(
|
||||
/// Who is signed in.
|
||||
final SignedIn person, {
|
||||
|
||||
/// Whether they asked to sign in with an email address, which their app
|
||||
/// asks for once it opens.
|
||||
final bool signsInWithEmail = false,
|
||||
}) extends SessionState {
|
||||
final SignedIn person,
|
||||
) extends SessionState {
|
||||
@override
|
||||
List<Object?> get props => [person, signsInWithEmail];
|
||||
List<Object?> get props => [person];
|
||||
}
|
||||
|
||||
/// Nobody is signed in since signing out: the app asks how to start.
|
||||
|
|
|
|||
Loading…
Reference in a new issue