-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
login: Support web-based auth methods
Fixes: #36
- Loading branch information
1 parent
60c5245
commit bd2fc3d
Showing
8 changed files
with
473 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import 'dart:math'; | ||
|
||
import 'package:convert/convert.dart'; | ||
import 'package:flutter/foundation.dart'; | ||
|
||
/// The authentication information contained in the zulip:// redirect URL. | ||
class WebAuthPayload { | ||
final Uri realm; | ||
final String email; | ||
final int? userId; // TODO(server-5) new in FL 108 | ||
final String otpEncryptedApiKey; | ||
|
||
WebAuthPayload._({ | ||
required this.realm, | ||
required this.email, | ||
required this.userId, | ||
required this.otpEncryptedApiKey, | ||
}); | ||
|
||
factory WebAuthPayload.parse(Uri url) { | ||
if ( | ||
url case Uri( | ||
scheme: 'zulip', | ||
host: 'login', | ||
queryParameters: { | ||
'realm': String realmStr, | ||
'email': String email, | ||
// 'user_id' handled below | ||
'otp_encrypted_api_key': String otpEncryptedApiKey, | ||
}, | ||
) | ||
) { | ||
final Uri? realm = Uri.tryParse(realmStr); | ||
if (realm == null) throw const FormatException(); | ||
|
||
// TODO(server-5) require in queryParameters (new in FL 108) | ||
final userIdStr = url.queryParameters['user_id']; | ||
int? userId; | ||
if (userIdStr != null) { | ||
userId = int.tryParse(userIdStr, radix: 10); | ||
if (userId == null) throw const FormatException(); | ||
} | ||
|
||
if (!RegExp(r'^[0-9a-fA-F]{64}$').hasMatch(otpEncryptedApiKey)) { | ||
throw const FormatException(); | ||
} | ||
|
||
return WebAuthPayload._( | ||
otpEncryptedApiKey: otpEncryptedApiKey, | ||
email: email, | ||
userId: userId, | ||
realm: realm, | ||
); | ||
} else { | ||
// TODO(dart): simplify after https://github.com/dart-lang/language/issues/2537 | ||
throw const FormatException(); | ||
} | ||
} | ||
|
||
String decodeApiKey(String otp) { | ||
final otpBytes = hex.decode(otp); | ||
final otpEncryptedApiKeyBytes = hex.decode(otpEncryptedApiKey); | ||
if (otpBytes.length != otpEncryptedApiKeyBytes.length) { | ||
throw const FormatException(); | ||
} | ||
return String.fromCharCodes(Iterable.generate(otpBytes.length, | ||
(i) => otpBytes[i] ^ otpEncryptedApiKeyBytes[i])); | ||
} | ||
} | ||
|
||
String generateOtp() { | ||
final rand = Random.secure(); | ||
final Uint8List bytes = Uint8List.fromList( | ||
List.generate(32, (_) => rand.nextInt(256))); | ||
return hex.encode(bytes); | ||
} | ||
|
||
/// For tests, create an OTP-encrypted API key. | ||
@visibleForTesting | ||
String debugEncodeApiKey(String apiKey, String otp) { | ||
final apiKeyBytes = apiKey.codeUnits; | ||
assert(apiKeyBytes.every((byte) => byte <= 0xff)); | ||
final otpBytes = hex.decode(otp); | ||
assert(apiKeyBytes.length == otpBytes.length); | ||
return hex.encode(List.generate(otpBytes.length, | ||
(i) => apiKeyBytes[i] ^ otpBytes[i])); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.