Skip to content

Commit

Permalink
Fix header parsing (#34)
Browse files Browse the repository at this point in the history
* Fix header parsing

* Adjust set cookie regex

* Bump version

---------

Co-authored-by: itaihanski <itaihanski@gmail.com>
  • Loading branch information
shilgapira and itaihanski authored Oct 5, 2023
1 parent 75d6b0a commit 36c5767
Show file tree
Hide file tree
Showing 15 changed files with 65 additions and 21 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 0.7.2

- Fix issue with parsing cookie headers

# 0.7.1

- Add support for custom OAuth providers
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
compileSdk 33
compileSdk 34

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
Expand Down
33 changes: 30 additions & 3 deletions lib/src/internal/http/responses.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
import 'dart:typed_data';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:json_annotation/json_annotation.dart';
import 'http_client.dart';

part 'responses.g.dart';

const sessionCookieName = 'DS';
const refreshCookieName = 'DSR';

@JsonSerializable(createToJson: false)
class JWTServerResponse {
String sessionJwt;
String? sessionJwt;
String? refreshJwt;
UserResponse? user;
bool firstSeen;

JWTServerResponse(this.sessionJwt, this.refreshJwt, this.user, this.firstSeen);
static var fromJson = _$JWTServerResponseFromJson;
static var decoder = _parseHeaders(fromJson, (response, headers) {
response.refreshJwt = headers[refreshCookieName] ?? response.refreshJwt;
final cookies = _cookiesFromHeaders(headers);

final sessionJwt = cookies[sessionCookieName];
if (sessionJwt != null && sessionJwt.isNotEmpty) {
response.sessionJwt = sessionJwt;
}

final refreshJwt = cookies[refreshCookieName];
if (refreshJwt != null && refreshJwt.isNotEmpty) {
response.refreshJwt = refreshJwt;
}
});
}

Expand Down Expand Up @@ -127,3 +139,18 @@ ResponseDecoder<T> _parseHeaders<T>(T Function(Map<String, dynamic>) fromJson, v
return response;
};
}

final _cookiesPattern = RegExp(r',(?!\s)');

Map<String, String> _cookiesFromHeaders(Map<String, String> headers) {
final header = headers['set-cookie'] ?? "";
var cookies = <String, String>{};
if (!kIsWeb && header.isNotEmpty) {
final values = header.split(_cookiesPattern);
for (final value in values) {
final cookie = Cookie.fromSetCookieValue(value);
cookies[cookie.name] = cookie.value;
}
}
return cookies;
}
2 changes: 1 addition & 1 deletion lib/src/internal/http/responses.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/src/internal/routes/enchanted_link.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class EnchantedLink implements DescopeEnchantedLink {

@override
Future<AuthenticationResponse> checkForSession({required String pendingRef}) async {
return (await client.enchantedLinkPendingSession(pendingRef)).convert();
return (await client.enchantedLinkPendingSession(pendingRef)).toAuthenticationResponse();
}

@override
Expand Down
2 changes: 1 addition & 1 deletion lib/src/internal/routes/flow.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class Flow extends DescopeFlow {
}

Future<void> _exchange(String authorizationCode, String codeVerifier, Completer<AuthenticationResponse> completer) async {
final authResponse = (await client.flowExchange(authorizationCode, codeVerifier)).convert();
final authResponse = (await client.flowExchange(authorizationCode, codeVerifier)).toAuthenticationResponse();
completer.complete(authResponse);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/src/internal/routes/magic_link.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ class MagicLink implements DescopeMagicLink {

@override
Future<AuthenticationResponse> verify({required String token}) async {
return (await client.magicLinkVerify(token)).convert();
return (await client.magicLinkVerify(token)).toAuthenticationResponse();
}
}
2 changes: 1 addition & 1 deletion lib/src/internal/routes/oauth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ class OAuth implements DescopeOAuth {

@override
Future<AuthenticationResponse> exchange({required String code}) async {
return (await client.oauthExchange(code)).convert();
return (await client.oauthExchange(code)).toAuthenticationResponse();
}
}
2 changes: 1 addition & 1 deletion lib/src/internal/routes/otp.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Otp implements DescopeOtp {

@override
Future<AuthenticationResponse> verify({required DeliveryMethod method, required String loginId, required String code}) async {
return (await client.otpVerify(method, loginId, code)).convert();
return (await client.otpVerify(method, loginId, code)).toAuthenticationResponse();
}

@override
Expand Down
4 changes: 2 additions & 2 deletions lib/src/internal/routes/password.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ class Password implements DescopePassword {

@override
Future<AuthenticationResponse> signUp({required String loginId, required String password, SignUpDetails? details}) async {
return (await client.passwordSignUp(loginId, password, details)).convert();
return (await client.passwordSignUp(loginId, password, details)).toAuthenticationResponse();
}

@override
Future<AuthenticationResponse> signIn({required String loginId, required String password}) async {
return (await client.passwordSignIn(loginId, password)).convert();
return (await client.passwordSignIn(loginId, password)).toAuthenticationResponse();
}

@override
Expand Down
23 changes: 18 additions & 5 deletions lib/src/internal/routes/shared.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,49 @@ extension ConvertUserResponse on UserResponse {
DescopeUser convert() {
final emailValue = (email ?? '').isNotEmpty ? email : null;
final phoneValue = (phone ?? '').isNotEmpty ? phone : null;

Uri? uri;
final pic = picture;
if (pic != null && pic.isNotEmpty) {
uri = Uri.parse(pic);
}

return DescopeUser(userId, loginIds, createdTime, name, uri, emailValue, verifiedEmail, phoneValue, verifiedPhone);
}
}

extension ConvertJWTResponse on JWTServerResponse {
AuthenticationResponse convert() {
AuthenticationResponse toAuthenticationResponse() {
final sessionJwt = this.sessionJwt;
if (sessionJwt == null || sessionJwt.isEmpty) {
throw InternalErrors.decodeError.add(message: 'Missing session JWT');
}

final refreshJwt = this.refreshJwt;
if (refreshJwt == null) {
if (refreshJwt == null || refreshJwt.isEmpty) {
throw InternalErrors.decodeError.add(message: 'Missing refresh JWT');
}

final user = this.user;
if (user == null) {
throw InternalErrors.decodeError.add(message: 'Missing user details');
}

return AuthenticationResponse(Token.decode(sessionJwt), Token.decode(refreshJwt), firstSeen, user.convert());
}
}

extension ConvertJWTResponseToRefresh on JWTServerResponse {
RefreshResponse toRefreshResponse() {
final sessionJwt = this.sessionJwt;
if (sessionJwt == null || sessionJwt.isEmpty) {
throw InternalErrors.decodeError.add(message: 'Missing session JWT');
}

Token? refreshToken;
final refreshJwt = this.refreshJwt;
if (refreshJwt != null) {
if (refreshJwt != null && refreshJwt.isNotEmpty) {
refreshToken = Token.decode(refreshJwt);
}

return RefreshResponse(Token.decode(sessionJwt), refreshToken);
}
}
2 changes: 1 addition & 1 deletion lib/src/internal/routes/sso.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ class Sso implements DescopeSso {

@override
Future<AuthenticationResponse> exchange({required String code}) async {
return (await client.oauthExchange(code)).convert();
return (await client.oauthExchange(code)).toAuthenticationResponse();
}
}
2 changes: 1 addition & 1 deletion lib/src/internal/routes/totp.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Totp implements DescopeTotp {

@override
Future<AuthenticationResponse> verify({required String loginId, required String code, SignInOptions? options}) async {
return (await client.totpVerify(loginId, code, options)).convert();
return (await client.totpVerify(loginId, code, options)).toAuthenticationResponse();
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/src/sdk/sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class DescopeSdk {
static const name = 'DescopeFlutter';

/// The Descope SDK version
static const version = '0.7.1';
static const version = '0.7.2';

/// The configuration of the [DescopeSdk] instance.
final DescopeConfig config;
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: descope
description: A Flutter package for working with the Descope API.
version: 0.7.1
version: 0.7.2
homepage: https://www.descope.com
repository: https://github.com/descope/descope-flutter
issue_tracker: https://github.com/descope/descope-flutter/issues
Expand Down

0 comments on commit 36c5767

Please sign in to comment.