diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_auth.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_auth.dart index 00080c63..b43e28f1 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_auth.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_auth.dart @@ -24,46 +24,48 @@ class EditAuthType extends ConsumerWidget { ); final currentAuthData = selectedRequest.authData; - return Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Authentication Type", - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - SizedBox( - height: 8, - ), - DropdownButtonFormField( - value: currentAuthType, - elevation: 4, - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(9), + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Authentication Type", + style: TextStyle( + fontWeight: FontWeight.bold, ), ), - items: APIAuthType.values.map((type) { - return DropdownMenuItem( - value: type, - child: Text(type.name.capitalize()), - ); - }).toList(), - onChanged: (APIAuthType? newType) { - if (newType != null) { - ref.read(collectionStateNotifierProvider.notifier).update( - authData: currentAuthData?.copyWith(type: newType) ?? - ApiAuthModel(type: newType), - ); - } - }, - ), - const SizedBox(height: 48), - _buildAuthFields(context, ref, currentAuthData), - ], + SizedBox( + height: 8, + ), + DropdownButtonFormField( + value: currentAuthType, + elevation: 4, + decoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(9), + ), + ), + items: APIAuthType.values.map((type) { + return DropdownMenuItem( + value: type, + child: Text(type.name.capitalize()), + ); + }).toList(), + onChanged: (APIAuthType? newType) { + if (newType != null) { + ref.read(collectionStateNotifierProvider.notifier).update( + authData: currentAuthData?.copyWith(type: newType) ?? + ApiAuthModel(type: newType), + ); + } + }, + ), + const SizedBox(height: 48), + _buildAuthFields(context, ref, currentAuthData), + ], + ), ), ); } @@ -325,29 +327,139 @@ class EditAuthType extends ConsumerWidget { ); case APIAuthType.jwt: - final jwtController = TextEditingController( - text: authData?.jwt?.jwt ?? '', + final jwtSecretController = TextEditingController( + text: authData?.jwt?.secret ?? '', ); + final jwtPayloadController = TextEditingController( + text: authData?.jwt?.payload ?? '', + ); + final jwtHeaderPrefixController = TextEditingController( + text: authData?.jwt?.headerPrefix ?? 'Bearer', + ); + final jwtQueryParamKeyController = TextEditingController( + text: authData?.jwt?.queryParamKey ?? 'token', + ); + final jwtHeaderController = TextEditingController( + text: authData?.jwt?.header ?? '', + ); + + final currentAddTokenTo = authData?.jwt?.addTokenTo ?? 'header'; + final currentAlgorithm = authData?.jwt?.algorithm ?? 'HS256'; + final isSecretBase64Encoded = + authData?.jwt?.isSecretBase64Encoded ?? false; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "JWT Token", + "Add JWT token to", style: TextStyle( fontWeight: FontWeight.bold, ), ), - SizedBox( - height: 4, - ), - TextField( - controller: jwtController, + SizedBox(height: 4), + DropdownButtonFormField( + value: currentAddTokenTo, decoration: InputDecoration( constraints: BoxConstraints( maxWidth: MediaQuery.sizeOf(context).width - 100, ), contentPadding: const EdgeInsets.all(18), - hintText: "JWT Token", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + items: [ + DropdownMenuItem( + value: 'header', + child: Text('Request Header'), + ), + DropdownMenuItem( + value: 'query', + child: Text('Query Parameters'), + ), + ], + onChanged: (String? newAddTokenTo) { + if (newAddTokenTo != null) { + updateAuth(ApiAuthModel( + type: APIAuthType.jwt, + jwt: AuthJwtModel( + secret: jwtSecretController.text.trim(), + payload: jwtPayloadController.text.trim(), + addTokenTo: newAddTokenTo, + algorithm: currentAlgorithm, + isSecretBase64Encoded: isSecretBase64Encoded, + headerPrefix: jwtHeaderPrefixController.text.trim(), + queryParamKey: jwtQueryParamKeyController.text.trim(), + header: jwtHeaderController.text.trim(), + ), + )); + } + }, + ), + const SizedBox(height: 16), + Text( + "Algorithm", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 4), + DropdownButtonFormField( + value: currentAlgorithm, + decoration: InputDecoration( + constraints: BoxConstraints( + maxWidth: MediaQuery.sizeOf(context).width - 100, + ), + contentPadding: const EdgeInsets.all(18), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + items: [ + 'HS256', + 'HS384', + 'HS512', + ].map((algorithm) { + return DropdownMenuItem( + value: algorithm, + child: Text(algorithm), + ); + }).toList(), + onChanged: (String? newAlgorithm) { + if (newAlgorithm != null) { + updateAuth(ApiAuthModel( + type: APIAuthType.jwt, + jwt: AuthJwtModel( + secret: jwtSecretController.text.trim(), + payload: jwtPayloadController.text.trim(), + addTokenTo: currentAddTokenTo, + algorithm: newAlgorithm, + isSecretBase64Encoded: isSecretBase64Encoded, + headerPrefix: jwtHeaderPrefixController.text.trim(), + queryParamKey: jwtQueryParamKeyController.text.trim(), + header: jwtHeaderController.text.trim(), + ), + )); + } + }, + ), + const SizedBox(height: 16), + Text( + "Secret", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 4), + TextField( + controller: jwtSecretController, + decoration: InputDecoration( + constraints: BoxConstraints( + maxWidth: MediaQuery.sizeOf(context).width - 100, + ), + contentPadding: const EdgeInsets.all(18), + hintText: "Secret key", hintStyle: Theme.of(context).textTheme.bodyMedium, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), @@ -355,9 +467,194 @@ class EditAuthType extends ConsumerWidget { ), onChanged: (value) => updateAuth(ApiAuthModel( type: APIAuthType.jwt, - jwt: AuthJwtModel(jwt: jwtController.text.trim()), + jwt: AuthJwtModel( + secret: jwtSecretController.text.trim(), + payload: jwtPayloadController.text.trim(), + addTokenTo: currentAddTokenTo, + algorithm: currentAlgorithm, + isSecretBase64Encoded: isSecretBase64Encoded, + headerPrefix: jwtHeaderPrefixController.text.trim(), + queryParamKey: jwtQueryParamKeyController.text.trim(), + header: jwtHeaderController.text.trim(), + ), )), ), + const SizedBox(height: 16), + CheckboxListTile( + title: Text( + "Secret is Base64 encoded", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + value: isSecretBase64Encoded, + contentPadding: EdgeInsets.zero, + controlAffinity: ListTileControlAffinity.leading, + onChanged: (bool? value) { + updateAuth(ApiAuthModel( + type: APIAuthType.jwt, + jwt: AuthJwtModel( + secret: jwtSecretController.text.trim(), + payload: jwtPayloadController.text.trim(), + addTokenTo: currentAddTokenTo, + algorithm: currentAlgorithm, + isSecretBase64Encoded: value ?? false, + headerPrefix: jwtHeaderPrefixController.text.trim(), + queryParamKey: jwtQueryParamKeyController.text.trim(), + header: jwtHeaderController.text.trim(), + ), + )); + }, + ), + const SizedBox(height: 16), + Text( + "Payload (JSON format)", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 4), + TextField( + controller: jwtPayloadController, + maxLines: 4, + decoration: InputDecoration( + constraints: BoxConstraints( + maxWidth: MediaQuery.sizeOf(context).width - 100, + ), + contentPadding: const EdgeInsets.all(18), + hintText: + '{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}', + hintStyle: Theme.of(context).textTheme.bodyMedium, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + onChanged: (value) => updateAuth(ApiAuthModel( + type: APIAuthType.jwt, + jwt: AuthJwtModel( + secret: jwtSecretController.text.trim(), + payload: jwtPayloadController.text.trim(), + addTokenTo: currentAddTokenTo, + algorithm: currentAlgorithm, + isSecretBase64Encoded: isSecretBase64Encoded, + headerPrefix: jwtHeaderPrefixController.text.trim(), + queryParamKey: jwtQueryParamKeyController.text.trim(), + header: jwtHeaderController.text.trim(), + ), + )), + ), + // const SizedBox(height: 16), + // if (currentAddTokenTo == 'header') ...[ + // Text( + // "Header Prefix", + // style: TextStyle( + // fontWeight: FontWeight.bold, + // ), + // ), + // SizedBox(height: 4), + // TextField( + // controller: jwtHeaderPrefixController, + // decoration: InputDecoration( + // constraints: BoxConstraints( + // maxWidth: MediaQuery.sizeOf(context).width - 100, + // ), + // contentPadding: const EdgeInsets.all(18), + // hintText: "Bearer", + // hintStyle: Theme.of(context).textTheme.bodyMedium, + // border: OutlineInputBorder( + // borderRadius: BorderRadius.circular(8), + // ), + // ), + // onChanged: (value) => updateAuth(ApiAuthModel( + // type: APIAuthType.jwt, + // jwt: AuthJwtModel( + // secret: jwtSecretController.text.trim(), + // payload: jwtPayloadController.text.trim(), + // addTokenTo: currentAddTokenTo, + // algorithm: currentAlgorithm, + // isSecretBase64Encoded: isSecretBase64Encoded, + // headerPrefix: jwtHeaderPrefixController.text.trim(), + // queryParamKey: jwtQueryParamKeyController.text.trim(), + // header: jwtHeaderController.text.trim(), + // ), + // )), + // ), + // const SizedBox(height: 16), + // ], + // if (currentAddTokenTo == 'query') ...[ + // Text( + // "Query Parameter Key", + // style: TextStyle( + // fontWeight: FontWeight.bold, + // ), + // ), + // SizedBox(height: 4), + // TextField( + // controller: jwtQueryParamKeyController, + // decoration: InputDecoration( + // constraints: BoxConstraints( + // maxWidth: MediaQuery.sizeOf(context).width - 100, + // ), + // contentPadding: const EdgeInsets.all(18), + // hintText: "token", + // hintStyle: Theme.of(context).textTheme.bodyMedium, + // border: OutlineInputBorder( + // borderRadius: BorderRadius.circular(8), + // ), + // ), + // onChanged: (value) => updateAuth(ApiAuthModel( + // type: APIAuthType.jwt, + // jwt: AuthJwtModel( + // secret: jwtSecretController.text.trim(), + // payload: jwtPayloadController.text.trim(), + // addTokenTo: currentAddTokenTo, + // algorithm: currentAlgorithm, + // isSecretBase64Encoded: isSecretBase64Encoded, + // headerPrefix: jwtHeaderPrefixController.text.trim(), + // queryParamKey: jwtQueryParamKeyController.text.trim(), + // header: jwtHeaderController.text.trim(), + // ), + // )), + // ), + // const SizedBox(height: 16), + // ], + // Text( + // "JWT Headers (JSON format)", + // style: TextStyle( + // fontWeight: FontWeight.bold, + // ), + // ), + // SizedBox(height: 4), + // TextField( + // controller: jwtHeaderController, + // maxLines: 3, + // decoration: InputDecoration( + // constraints: BoxConstraints( + // maxWidth: MediaQuery.sizeOf(context).width - 100, + // ), + // contentPadding: const EdgeInsets.all(18), + // hintText: '{"typ": "JWT", "alg": "HS256"}', + // hintStyle: Theme.of(context).textTheme.bodyMedium, + // border: OutlineInputBorder( + // borderRadius: BorderRadius.circular(8), + // ), + // ), + // onChanged: (value) => updateAuth( + // ApiAuthModel( + // type: APIAuthType.jwt, + // jwt: AuthJwtModel( + // secret: jwtSecretController.text.trim(), + // payload: jwtPayloadController.text.trim(), + // addTokenTo: currentAddTokenTo, + // algorithm: currentAlgorithm, + // isSecretBase64Encoded: isSecretBase64Encoded, + // headerPrefix: jwtHeaderPrefixController.text.trim(), + // queryParamKey: jwtQueryParamKeyController.text.trim(), + // header: jwtHeaderController.text.trim(), + // ), + // ), + // ), + // ), ], ); diff --git a/packages/apidash_core/lib/models/auth/auth_jwt_model.dart b/packages/apidash_core/lib/models/auth/auth_jwt_model.dart index 4551b370..61b165a5 100644 --- a/packages/apidash_core/lib/models/auth/auth_jwt_model.dart +++ b/packages/apidash_core/lib/models/auth/auth_jwt_model.dart @@ -1,12 +1,19 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -part 'auth_jwt_model.g.dart'; part 'auth_jwt_model.freezed.dart'; +part 'auth_jwt_model.g.dart'; @freezed class AuthJwtModel with _$AuthJwtModel { const factory AuthJwtModel({ - required String jwt, + required String secret, + required String payload, + required String addTokenTo, + required String algorithm, + required bool isSecretBase64Encoded, + required String headerPrefix, + required String queryParamKey, + required String header, }) = _AuthJwtModel; factory AuthJwtModel.fromJson(Map json) => diff --git a/packages/apidash_core/lib/models/auth/auth_jwt_model.freezed.dart b/packages/apidash_core/lib/models/auth/auth_jwt_model.freezed.dart index c9688185..cf35ed41 100644 --- a/packages/apidash_core/lib/models/auth/auth_jwt_model.freezed.dart +++ b/packages/apidash_core/lib/models/auth/auth_jwt_model.freezed.dart @@ -20,7 +20,14 @@ AuthJwtModel _$AuthJwtModelFromJson(Map json) { /// @nodoc mixin _$AuthJwtModel { - String get jwt => throw _privateConstructorUsedError; + String get secret => throw _privateConstructorUsedError; + String get payload => throw _privateConstructorUsedError; + String get addTokenTo => throw _privateConstructorUsedError; + String get algorithm => throw _privateConstructorUsedError; + bool get isSecretBase64Encoded => throw _privateConstructorUsedError; + String get headerPrefix => throw _privateConstructorUsedError; + String get queryParamKey => throw _privateConstructorUsedError; + String get header => throw _privateConstructorUsedError; /// Serializes this AuthJwtModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -38,7 +45,15 @@ abstract class $AuthJwtModelCopyWith<$Res> { AuthJwtModel value, $Res Function(AuthJwtModel) then) = _$AuthJwtModelCopyWithImpl<$Res, AuthJwtModel>; @useResult - $Res call({String jwt}); + $Res call( + {String secret, + String payload, + String addTokenTo, + String algorithm, + bool isSecretBase64Encoded, + String headerPrefix, + String queryParamKey, + String header}); } /// @nodoc @@ -56,12 +71,47 @@ class _$AuthJwtModelCopyWithImpl<$Res, $Val extends AuthJwtModel> @pragma('vm:prefer-inline') @override $Res call({ - Object? jwt = null, + Object? secret = null, + Object? payload = null, + Object? addTokenTo = null, + Object? algorithm = null, + Object? isSecretBase64Encoded = null, + Object? headerPrefix = null, + Object? queryParamKey = null, + Object? header = null, }) { return _then(_value.copyWith( - jwt: null == jwt - ? _value.jwt - : jwt // ignore: cast_nullable_to_non_nullable + secret: null == secret + ? _value.secret + : secret // ignore: cast_nullable_to_non_nullable + as String, + payload: null == payload + ? _value.payload + : payload // ignore: cast_nullable_to_non_nullable + as String, + addTokenTo: null == addTokenTo + ? _value.addTokenTo + : addTokenTo // ignore: cast_nullable_to_non_nullable + as String, + algorithm: null == algorithm + ? _value.algorithm + : algorithm // ignore: cast_nullable_to_non_nullable + as String, + isSecretBase64Encoded: null == isSecretBase64Encoded + ? _value.isSecretBase64Encoded + : isSecretBase64Encoded // ignore: cast_nullable_to_non_nullable + as bool, + headerPrefix: null == headerPrefix + ? _value.headerPrefix + : headerPrefix // ignore: cast_nullable_to_non_nullable + as String, + queryParamKey: null == queryParamKey + ? _value.queryParamKey + : queryParamKey // ignore: cast_nullable_to_non_nullable + as String, + header: null == header + ? _value.header + : header // ignore: cast_nullable_to_non_nullable as String, ) as $Val); } @@ -75,7 +125,15 @@ abstract class _$$AuthJwtModelImplCopyWith<$Res> __$$AuthJwtModelImplCopyWithImpl<$Res>; @override @useResult - $Res call({String jwt}); + $Res call( + {String secret, + String payload, + String addTokenTo, + String algorithm, + bool isSecretBase64Encoded, + String headerPrefix, + String queryParamKey, + String header}); } /// @nodoc @@ -91,12 +149,47 @@ class __$$AuthJwtModelImplCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ - Object? jwt = null, + Object? secret = null, + Object? payload = null, + Object? addTokenTo = null, + Object? algorithm = null, + Object? isSecretBase64Encoded = null, + Object? headerPrefix = null, + Object? queryParamKey = null, + Object? header = null, }) { return _then(_$AuthJwtModelImpl( - jwt: null == jwt - ? _value.jwt - : jwt // ignore: cast_nullable_to_non_nullable + secret: null == secret + ? _value.secret + : secret // ignore: cast_nullable_to_non_nullable + as String, + payload: null == payload + ? _value.payload + : payload // ignore: cast_nullable_to_non_nullable + as String, + addTokenTo: null == addTokenTo + ? _value.addTokenTo + : addTokenTo // ignore: cast_nullable_to_non_nullable + as String, + algorithm: null == algorithm + ? _value.algorithm + : algorithm // ignore: cast_nullable_to_non_nullable + as String, + isSecretBase64Encoded: null == isSecretBase64Encoded + ? _value.isSecretBase64Encoded + : isSecretBase64Encoded // ignore: cast_nullable_to_non_nullable + as bool, + headerPrefix: null == headerPrefix + ? _value.headerPrefix + : headerPrefix // ignore: cast_nullable_to_non_nullable + as String, + queryParamKey: null == queryParamKey + ? _value.queryParamKey + : queryParamKey // ignore: cast_nullable_to_non_nullable + as String, + header: null == header + ? _value.header + : header // ignore: cast_nullable_to_non_nullable as String, )); } @@ -105,17 +198,39 @@ class __$$AuthJwtModelImplCopyWithImpl<$Res> /// @nodoc @JsonSerializable() class _$AuthJwtModelImpl implements _AuthJwtModel { - const _$AuthJwtModelImpl({required this.jwt}); + const _$AuthJwtModelImpl( + {required this.secret, + required this.payload, + required this.addTokenTo, + required this.algorithm, + required this.isSecretBase64Encoded, + required this.headerPrefix, + required this.queryParamKey, + required this.header}); factory _$AuthJwtModelImpl.fromJson(Map json) => _$$AuthJwtModelImplFromJson(json); @override - final String jwt; + final String secret; + @override + final String payload; + @override + final String addTokenTo; + @override + final String algorithm; + @override + final bool isSecretBase64Encoded; + @override + final String headerPrefix; + @override + final String queryParamKey; + @override + final String header; @override String toString() { - return 'AuthJwtModel(jwt: $jwt)'; + return 'AuthJwtModel(secret: $secret, payload: $payload, addTokenTo: $addTokenTo, algorithm: $algorithm, isSecretBase64Encoded: $isSecretBase64Encoded, headerPrefix: $headerPrefix, queryParamKey: $queryParamKey, header: $header)'; } @override @@ -123,12 +238,25 @@ class _$AuthJwtModelImpl implements _AuthJwtModel { return identical(this, other) || (other.runtimeType == runtimeType && other is _$AuthJwtModelImpl && - (identical(other.jwt, jwt) || other.jwt == jwt)); + (identical(other.secret, secret) || other.secret == secret) && + (identical(other.payload, payload) || other.payload == payload) && + (identical(other.addTokenTo, addTokenTo) || + other.addTokenTo == addTokenTo) && + (identical(other.algorithm, algorithm) || + other.algorithm == algorithm) && + (identical(other.isSecretBase64Encoded, isSecretBase64Encoded) || + other.isSecretBase64Encoded == isSecretBase64Encoded) && + (identical(other.headerPrefix, headerPrefix) || + other.headerPrefix == headerPrefix) && + (identical(other.queryParamKey, queryParamKey) || + other.queryParamKey == queryParamKey) && + (identical(other.header, header) || other.header == header)); } @JsonKey(includeFromJson: false, includeToJson: false) @override - int get hashCode => Object.hash(runtimeType, jwt); + int get hashCode => Object.hash(runtimeType, secret, payload, addTokenTo, + algorithm, isSecretBase64Encoded, headerPrefix, queryParamKey, header); /// Create a copy of AuthJwtModel /// with the given fields replaced by the non-null parameter values. @@ -147,13 +275,35 @@ class _$AuthJwtModelImpl implements _AuthJwtModel { } abstract class _AuthJwtModel implements AuthJwtModel { - const factory _AuthJwtModel({required final String jwt}) = _$AuthJwtModelImpl; + const factory _AuthJwtModel( + {required final String secret, + required final String payload, + required final String addTokenTo, + required final String algorithm, + required final bool isSecretBase64Encoded, + required final String headerPrefix, + required final String queryParamKey, + required final String header}) = _$AuthJwtModelImpl; factory _AuthJwtModel.fromJson(Map json) = _$AuthJwtModelImpl.fromJson; @override - String get jwt; + String get secret; + @override + String get payload; + @override + String get addTokenTo; + @override + String get algorithm; + @override + bool get isSecretBase64Encoded; + @override + String get headerPrefix; + @override + String get queryParamKey; + @override + String get header; /// Create a copy of AuthJwtModel /// with the given fields replaced by the non-null parameter values. diff --git a/packages/apidash_core/lib/models/auth/auth_jwt_model.g.dart b/packages/apidash_core/lib/models/auth/auth_jwt_model.g.dart index 73bc2615..4e415d10 100644 --- a/packages/apidash_core/lib/models/auth/auth_jwt_model.g.dart +++ b/packages/apidash_core/lib/models/auth/auth_jwt_model.g.dart @@ -8,10 +8,24 @@ part of 'auth_jwt_model.dart'; _$AuthJwtModelImpl _$$AuthJwtModelImplFromJson(Map json) => _$AuthJwtModelImpl( - jwt: json['jwt'] as String, + secret: json['secret'] as String, + payload: json['payload'] as String, + addTokenTo: json['addTokenTo'] as String, + algorithm: json['algorithm'] as String, + isSecretBase64Encoded: json['isSecretBase64Encoded'] as bool, + headerPrefix: json['headerPrefix'] as String, + queryParamKey: json['queryParamKey'] as String, + header: json['header'] as String, ); Map _$$AuthJwtModelImplToJson(_$AuthJwtModelImpl instance) => { - 'jwt': instance.jwt, + 'secret': instance.secret, + 'payload': instance.payload, + 'addTokenTo': instance.addTokenTo, + 'algorithm': instance.algorithm, + 'isSecretBase64Encoded': instance.isSecretBase64Encoded, + 'headerPrefix': instance.headerPrefix, + 'queryParamKey': instance.queryParamKey, + 'header': instance.header, }; diff --git a/packages/apidash_core/lib/utils/auth_utils.dart b/packages/apidash_core/lib/utils/auth_utils.dart new file mode 100644 index 00000000..4e03b144 --- /dev/null +++ b/packages/apidash_core/lib/utils/auth_utils.dart @@ -0,0 +1,95 @@ +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:apidash_core/models/auth/auth_jwt_model.dart'; +import 'package:crypto/crypto.dart'; + +String generateJWT(AuthJwtModel jwtAuth) { + try { + Map header; + if (jwtAuth.header.isNotEmpty) { + try { + header = json.decode(jwtAuth.header) as Map; + } catch (e) { + header = {}; + } + } else { + header = {}; + } + header['typ'] = header['typ'] ?? 'JWT'; + header['alg'] = jwtAuth.algorithm; + Map payload; + if (jwtAuth.payload.isNotEmpty) { + try { + payload = json.decode(jwtAuth.payload) as Map; + } catch (e) { + payload = {}; + } + } else { + payload = {}; + } + if (!payload.containsKey('iat')) { + payload['iat'] = DateTime.now().millisecondsSinceEpoch ~/ 1000; + } + + // Encode header and payload + final encodedHeader = _base64UrlEncode(utf8.encode(json.encode(header))); + final encodedPayload = _base64UrlEncode(utf8.encode(json.encode(payload))); + + // Create signature + final signature = _createSignature( + '$encodedHeader.$encodedPayload', + jwtAuth.secret, + jwtAuth.algorithm, + jwtAuth.isSecretBase64Encoded, + ); + + return '$encodedHeader.$encodedPayload.$signature'; + } catch (e) { + // Return a basic JWT if generation fails + return 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'; + } +} + +String _createSignature( + String data, String secret, String algorithm, bool isSecretBase64Encoded) { + try { + Uint8List secretBytes; + if (isSecretBase64Encoded) { + secretBytes = base64.decode(secret); + } else { + secretBytes = utf8.encode(secret); + } + + final dataBytes = utf8.encode(data); + + switch (algorithm) { + case 'HS256': + final hmac = Hmac(sha256, secretBytes); + final digest = hmac.convert(dataBytes); + return _base64UrlEncode(digest.bytes); + + case 'HS384': + final hmac = Hmac(sha384, secretBytes); + final digest = hmac.convert(dataBytes); + return _base64UrlEncode(digest.bytes); + + case 'HS512': + final hmac = Hmac(sha512, secretBytes); + final digest = hmac.convert(dataBytes); + return _base64UrlEncode(digest.bytes); + + default: + // Default to HS256 + final hmac = Hmac(sha256, secretBytes); + final digest = hmac.convert(dataBytes); + return _base64UrlEncode(digest.bytes); + } + } catch (e) { + // Return placeholder signature if creation fails + return _base64UrlEncode(utf8.encode('signature_generation_failed')); + } +} + +String _base64UrlEncode(List bytes) { + return base64Url.encode(bytes).replaceAll('=', ''); +} diff --git a/packages/apidash_core/lib/utils/handle_auth.dart b/packages/apidash_core/lib/utils/handle_auth.dart index 9b4ec0ba..bad494f9 100644 --- a/packages/apidash_core/lib/utils/handle_auth.dart +++ b/packages/apidash_core/lib/utils/handle_auth.dart @@ -2,9 +2,11 @@ import 'dart:convert'; import 'package:apidash_core/consts.dart'; import 'package:apidash_core/models/auth/api_auth_model.dart'; import 'package:apidash_core/models/http_request_model.dart'; +import 'package:apidash_core/utils/auth_utils.dart'; import 'package:seed/seed.dart'; -HttpRequestModel handleAuth(HttpRequestModel httpRequestModel, ApiAuthModel? auth) { +HttpRequestModel handleAuth( + HttpRequestModel httpRequestModel, ApiAuthModel? auth) { if (auth == null || auth.type == APIAuthType.none) { return httpRequestModel; } @@ -41,9 +43,26 @@ HttpRequestModel handleAuth(HttpRequestModel httpRequestModel, ApiAuthModel? aut case APIAuthType.jwt: if (auth.jwt != null) { final jwtAuth = auth.jwt!; - updatedHeaders.add(NameValueModel( - name: 'Authorization', value: 'Bearer ${jwtAuth.jwt}')); - updatedHeaderEnabledList.add(true); + + // Generate JWT token + final jwtToken = generateJWT(jwtAuth); + + if (jwtAuth.addTokenTo == 'header') { + // Add to request header with prefix + final headerValue = jwtAuth.headerPrefix.isNotEmpty + ? '${jwtAuth.headerPrefix} $jwtToken' + : jwtToken; + updatedHeaders + .add(NameValueModel(name: 'Authorization', value: headerValue)); + updatedHeaderEnabledList.add(true); + } else if (jwtAuth.addTokenTo == 'query') { + // Add to query parameters(if selected) + final paramKey = jwtAuth.queryParamKey.isNotEmpty + ? jwtAuth.queryParamKey + : 'token'; + updatedParams.add(NameValueModel(name: paramKey, value: jwtToken)); + updatedParamEnabledList.add(true); + } } break; diff --git a/packages/apidash_core/pubspec.yaml b/packages/apidash_core/pubspec.yaml index 2c010a07..ca73b279 100644 --- a/packages/apidash_core/pubspec.yaml +++ b/packages/apidash_core/pubspec.yaml @@ -23,6 +23,7 @@ dependencies: postman: path: ../postman xml: ^6.3.0 + crypto: ^3.0.6 dev_dependencies: flutter_test: