feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its first as it sets a password. The handle is private until its person goes public (D5). - `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle` says whether a sign-up could take one, only while that sign-up's mailed code waits (`codeExpired` otherwise), counted per code request (30 an hour) and per network (60) (D29). Members ask the register. - `password.set` takes the first handle of an account without one (D28); `SignInWays.handle` tells the app whether there is one. - Each checks the handle before the code is used, so a refusal leaves the code for another try. An existing account at the address keeps its own handle where it has one, and the typed handle is still checked, so the answer says nothing about the address. - `first_handle.dart` holds what SimpleLogin, the keys and passwords share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`. - halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked 300 ms after typing pauses, only about the handle typed last; the instance's suggestion is free without asking) (D30). The password setup asks for the handle between the phrase check and the code, and a refusal at the code sends back to it. SimpleLogin's sign-up and keys steps use the same field and now check as it is typed. - The module's README and ADR 0020 (D29, D30, the built state) amended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
87671208df
commit
630965d920
2 changed files with 2305 additions and 395 deletions
|
|
@ -21,6 +21,7 @@ abstract class SignInWays
|
|||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? handle,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -30,6 +31,7 @@ abstract class SignInWays
|
|||
}) : simpleLogin = simpleLogin ?? false,
|
||||
password = password ?? false,
|
||||
keys = keys ?? false,
|
||||
handle = handle ?? false,
|
||||
authenticator = authenticator ?? false,
|
||||
passwordOnly = passwordOnly ?? false,
|
||||
secondFactorWorks = secondFactorWorks ?? false;
|
||||
|
|
@ -40,6 +42,7 @@ abstract class SignInWays
|
|||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? handle,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -61,6 +64,9 @@ abstract class SignInWays
|
|||
keys: jsonSerialization['keys'] == null
|
||||
? null
|
||||
: _isc.BoolJsonExtension.fromJson(jsonSerialization['keys']),
|
||||
handle: jsonSerialization['handle'] == null
|
||||
? null
|
||||
: _isc.BoolJsonExtension.fromJson(jsonSerialization['handle']),
|
||||
authenticator: jsonSerialization['authenticator'] == null
|
||||
? null
|
||||
: _isc.BoolJsonExtension.fromJson(jsonSerialization['authenticator']),
|
||||
|
|
@ -107,6 +113,10 @@ abstract class SignInWays
|
|||
/// that only opens them, as for SimpleLogin (ADR 0020, D19).
|
||||
bool keys;
|
||||
|
||||
/// A handle (ADR 0020, D5), private or public: an account of before
|
||||
/// without one is asked for it as it takes a password or keys (D28).
|
||||
bool handle;
|
||||
|
||||
/// A confirmed authenticator: the password signs in only with one.
|
||||
bool authenticator;
|
||||
|
||||
|
|
@ -142,6 +152,7 @@ abstract class SignInWays
|
|||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? handle,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -158,6 +169,7 @@ abstract class SignInWays
|
|||
'simpleLogin': simpleLogin,
|
||||
'password': password,
|
||||
'keys': keys,
|
||||
'handle': handle,
|
||||
'authenticator': authenticator,
|
||||
'passwordOnly': passwordOnly,
|
||||
'secondFactorWorks': secondFactorWorks,
|
||||
|
|
@ -178,6 +190,7 @@ abstract class SignInWays
|
|||
'simpleLogin': simpleLogin,
|
||||
'password': password,
|
||||
'keys': keys,
|
||||
'handle': handle,
|
||||
'authenticator': authenticator,
|
||||
'passwordOnly': passwordOnly,
|
||||
'secondFactorWorks': secondFactorWorks,
|
||||
|
|
@ -204,6 +217,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? handle,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -216,6 +230,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
simpleLogin: simpleLogin,
|
||||
password: password,
|
||||
keys: keys,
|
||||
handle: handle,
|
||||
authenticator: authenticator,
|
||||
passwordOnly: passwordOnly,
|
||||
secondFactorWorks: secondFactorWorks,
|
||||
|
|
@ -234,6 +249,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? handle,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -247,6 +263,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
simpleLogin: simpleLogin ?? this.simpleLogin,
|
||||
password: password ?? this.password,
|
||||
keys: keys ?? this.keys,
|
||||
handle: handle ?? this.handle,
|
||||
authenticator: authenticator ?? this.authenticator,
|
||||
passwordOnly: passwordOnly ?? this.passwordOnly,
|
||||
secondFactorWorks: secondFactorWorks ?? this.secondFactorWorks,
|
||||
|
|
|
|||
|
|
@ -541,12 +541,15 @@ class EndpointPassword extends _isc.EndpointRef {
|
|||
/// (`EmailCodePurpose.firstPassword`), else the one they add
|
||||
/// (`EmailCodePurpose.signUp`); someone who signs in with GitHub alone
|
||||
/// does it within [gitHubSignInFreshFor] of signing in with GitHub on this
|
||||
/// session. Refusals as `setPassword`'s. It signs in once an authenticator
|
||||
/// is confirmed (`authenticator.confirm`).
|
||||
/// session. [handle] where the caller has none (`account.signInWays` says
|
||||
/// `handle`; ADR 0020, D28), null otherwise, counted as `profile.checkHandle`
|
||||
/// counts it. Refusals as `setPassword`'s. It signs in once an
|
||||
/// authenticator is confirmed (`authenticator.confirm`).
|
||||
_ida.Future<void> set({
|
||||
required _ipn3lqh9.PasswordSetup setup,
|
||||
_isc.UuidValue? request,
|
||||
String? code,
|
||||
String? handle,
|
||||
}) => caller.callServerEndpoint<void>(
|
||||
'halkyon_base.password',
|
||||
'set',
|
||||
|
|
@ -554,6 +557,7 @@ class EndpointPassword extends _isc.EndpointRef {
|
|||
'setup': setup,
|
||||
'request': request,
|
||||
'code': code,
|
||||
'handle': handle,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -691,16 +695,41 @@ class EndpointPasswordSignIn extends _isc.EndpointRef {
|
|||
},
|
||||
);
|
||||
|
||||
/// Whether someone signing up with the code mailed for [request]
|
||||
/// (`emailCode.sendCode`, for `EmailCodePurpose.signUp`) could take
|
||||
/// [handle], while the code is on its way (ADR 0020, D29): null where they
|
||||
/// could, else why not, as saving a profile would say. For the app to say
|
||||
/// so while it is typed; [signUp] checks again. At most 30 for a request
|
||||
/// and 60 from a network in an hour, with every other answer about a
|
||||
/// handle (`ProfileRefusal.tooManyHandleChecks`, thrown): each tells which
|
||||
/// handles exist. Refused (`EmailSignInRefusal.codeExpired`) where no such
|
||||
/// code waits, and as `checkSignUpHandle` refuses.
|
||||
_ida.Future<_ih722tf9.ProfileRefusal?> checkHandle({
|
||||
required _isc.UuidValue request,
|
||||
required String handle,
|
||||
}) => caller.callServerEndpoint<_ih722tf9.ProfileRefusal?>(
|
||||
'halkyon_base.passwordSignIn',
|
||||
'checkHandle',
|
||||
{
|
||||
'request': request,
|
||||
'handle': handle,
|
||||
},
|
||||
);
|
||||
|
||||
/// Signs up with the address a [code] mailed for [request]
|
||||
/// (`emailCode.sendCode`) proves and the password [setup] describes (all
|
||||
/// of it made on the device): a new account, or the account with that
|
||||
/// address that has no password yet. Signed in, the device sets up an
|
||||
/// authenticator next, before the password signs in elsewhere. Refusals as
|
||||
/// `signUpWithCode`'s.
|
||||
/// (`emailCode.sendCode`) proves, the password [setup] describes (all of
|
||||
/// it made on the device) and [handle] (ADR 0020, D26): a new account,
|
||||
/// whose profile stays private until its person goes public, or the
|
||||
/// account with that address that has no password yet, which keeps a
|
||||
/// handle it has. Signed in, the device sets up an authenticator next,
|
||||
/// before the password signs in elsewhere. Refusals as `signUpWithCode`'s:
|
||||
/// a handle refused (`ProfileRefusedException`, counted as [checkHandle]
|
||||
/// counts it) before the code is used.
|
||||
_ida.Future<_iacc.AuthSuccess> signUp({
|
||||
required _isc.UuidValue request,
|
||||
required String code,
|
||||
required _ipn3lqh9.PasswordSetup setup,
|
||||
required String handle,
|
||||
}) => caller.callServerEndpoint<_iacc.AuthSuccess>(
|
||||
'halkyon_base.passwordSignIn',
|
||||
'signUp',
|
||||
|
|
@ -708,6 +737,7 @@ class EndpointPasswordSignIn extends _isc.EndpointRef {
|
|||
'request': request,
|
||||
'code': code,
|
||||
'setup': setup,
|
||||
'handle': handle,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue