forked from zulip/zulip-flutter
-
Notifications
You must be signed in to change notification settings - Fork 0
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: zulip#36
- Loading branch information
1 parent
2f9bedb
commit bb76802
Showing
8 changed files
with
453 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 String otpEncryptedApiKey; | ||
final String email; | ||
final int? userId; // TODO(server-5) new in FL 108 | ||
final Uri realm; | ||
|
||
WebAuthPayload._({ | ||
required this.otpEncryptedApiKey, | ||
required this.email, | ||
required this.userId, | ||
required this.realm, | ||
}); | ||
|
||
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, | ||
}, | ||
) | ||
) { | ||
// 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(); | ||
} | ||
|
||
final Uri? realm = Uri.tryParse(realmStr); | ||
if (realm == 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
Oops, something went wrong.