Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions packages/gotrue/lib/src/gotrue_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -607,17 +607,20 @@ class GoTrueClient {

final authResponse = AuthResponse.fromJson(response);

if (authResponse.session == null) {
throw AuthException('An error occurred on token verification.');
// A secure email or phone change verifies in two steps: the server accepts
// the first OTP without returning a session and only issues one once the
// second OTP is verified. In that case there is nothing to persist yet, so
// return the intermediate response instead of treating it as an error.
final session = authResponse.session;
if (session != null) {
_saveSession(session);
notifyAllSubscribers(
type == OtpType.recovery
? AuthChangeEvent.passwordRecovery
: AuthChangeEvent.signedIn,
);
}

_saveSession(authResponse.session!);
notifyAllSubscribers(
type == OtpType.recovery
? AuthChangeEvent.passwordRecovery
: AuthChangeEvent.signedIn,
);

return authResponse;
}

Expand Down
20 changes: 11 additions & 9 deletions packages/gotrue/test/otp_mock_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -475,22 +475,24 @@ void main() {
);
});

test('response with null session', () async {
test('response with null session returns the intermediate response',
() async {
final client = GoTrueClient(
url: 'https://example.com',
httpClient: NullSessionClient(testEmail),
asyncStorage: TestAsyncStorage(),
);

await expectLater(
() => client.verifyOTP(
email: testEmail,
token: '123456',
type: OtpType.email,
),
throwsA(isA<AuthException>().having((e) => e.message, 'message',
'An error occurred on token verification.')),
// Verifying the first OTP of a secure email change does not return a
// session. This should not throw, the intermediate response is returned
// so the second OTP can subsequently be verified.
final response = await client.verifyOTP(
email: testEmail,
token: '123456',
type: OtpType.emailChange,
);

expect(response.session, isNull);
});
});

Expand Down
Loading