authenticationHandler method
Looks up the AuthenticationInfo belonging to the key.
Only looks at keys created with this package (by checking the prefix),
returns null for all other inputs.
In case the session looks like it was created with this package, but
does not resolve to a valid authentication info (anymore), this will
return null, and log details of the reason for rejection.
Implementation
Future<AuthenticationInfo?> authenticationHandler(
final Session session,
final String key,
) async {
final sessionKeyParts = tryParseServerSideSessionToken(session, key);
if (sessionKeyParts == null) {
return null;
}
final (:serverSideSessionId, :secret) = sessionKeyParts;
var serverSideSession = await ServerSideSession.db.findById(
session,
serverSideSessionId,
);
if (serverSideSession == null) {
session.log(
'Did not find server side session with ID "$serverSideSessionId"',
level: LogLevel.debug,
);
return null;
}
if (_isSessionExpired(serverSideSession)) {
session.log(
'Got session after its expiration.',
level: LogLevel.debug,
);
return null;
}
final keyHashValidation = _sessionKeyHash.validateSessionKeyHash(
secret: secret,
hash: Uint8List.sublistView(serverSideSession.sessionKeyHash),
salt: Uint8List.sublistView(serverSideSession.sessionKeySalt),
);
if (keyHashValidation == SessionKeyHashValidation.invalid) {
session.log(
'Provided `secret` did not result in correct session key hash.',
level: LogLevel.debug,
);
return null;
}
if (keyHashValidation == SessionKeyHashValidation.validButOutdated) {
// Upgrade-on-verify: the secret is genuine but the stored hash predates
// the versioned, salt-in-digest scheme. Re-hash with the existing salt and
// persist, so the row validates under the current scheme from now on -
// this is what keeps the salt fix from signing existing sessions out.
final rehashed = _sessionKeyHash.rehashSessionKeyHash(
secret: secret,
salt: Uint8List.sublistView(serverSideSession.sessionKeySalt),
);
serverSideSession = await ServerSideSession.db.updateRow(
session,
serverSideSession.copyWith(
sessionKeyHash: ByteData.sublistView(rehashed),
),
);
}
if (serverSideSession.lastUsedAt.isBefore(
clock.now().subtract(const Duration(minutes: 1)),
)) {
serverSideSession = await ServerSideSession.db.updateRow(
session,
serverSideSession.copyWith(lastUsedAt: clock.now()),
);
}
return AuthenticationInfo(
serverSideSession.authUserId.uuid,
serverSideSession.scopeNames.map(Scope.new).toSet(),
authId: serverSideSessionId.toString(),
);
}