Skip to content

add basic authentication features #855

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 71 commits into from
Jul 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
171a118
feat: add authentication support with APIAuthType and APIAuthModel
Udhay-Adithya Jun 7, 2025
4177065
feat: improve authentication model with BearerToken, APIKey, and JWTB…
Udhay-Adithya Jun 7, 2025
51fe211
feat: implement authentication handling in HTTP requests
Udhay-Adithya Jun 7, 2025
6a2d1fd
feat: implement new authentication models
Udhay-Adithya Jun 12, 2025
449e325
feat: refactor authentication handling to use new ApiAuthModel and up…
Udhay-Adithya Jun 12, 2025
efaebd0
fix: input text field error
Udhay-Adithya Jun 12, 2025
1540d84
fix:prevent text field rebuild on watch
Udhay-Adithya Jun 13, 2025
52a1feb
feat: enhance JWT authentication handling with new fields and JWT gen…
Udhay-Adithya Jun 13, 2025
a48d059
fix: state persistance upon api type switch
Udhay-Adithya Jun 14, 2025
88017a9
chore: generate lock files
Udhay-Adithya Jun 15, 2025
0d7f1a9
fix: update authorization label and reorder request pane
Udhay-Adithya Jun 15, 2025
fd92206
refactor: reorganize import statements
Udhay-Adithya Jun 15, 2025
d046d50
refactor: rename ApiAuthModel to AuthModel and authData to authModel
Udhay-Adithya Jun 15, 2025
74c0959
refactor: rename authData to authModel and update related tests
Udhay-Adithya Jun 15, 2025
bb10ad6
feat: split authentication related fields into seperate files
Udhay-Adithya Jun 17, 2025
ff23413
feat: change text field design
Udhay-Adithya Jun 18, 2025
0ec2120
feat: replace DropdownButtonFormField with ADPopupMenu for improved U…
Udhay-Adithya Jun 19, 2025
5be9ffd
feat: update AuthModel serialization to use explicitToJson for nested…
Udhay-Adithya Jun 19, 2025
99e5fa7
feat: enhance AuthModel serialization with anyMap support and add def…
Udhay-Adithya Jun 19, 2025
987d9ca
feat: enhance EditAuthType to support read-only in history view
Udhay-Adithya Jun 19, 2025
620c275
fix: change JWT generation failure handling to throw an exception ins…
Udhay-Adithya Jun 19, 2025
5a6e2b1
feat: add authentication tab for graphql requests
Udhay-Adithya Jun 19, 2025
e06cb2e
feat: add read-only authentication tab to graphql history request pane
Udhay-Adithya Jun 19, 2025
bf170e1
feat: remove AuthModel from HttpRequestModel and integrate into Histo…
Udhay-Adithya Jun 25, 2025
8d8940d
feat: add read-only support to authentication fields
Udhay-Adithya Jun 25, 2025
a17d6fd
chore: remove logging in EditAuthType
Udhay-Adithya Jun 25, 2025
3903277
chore: resolve merge conflicts
Udhay-Adithya Jul 2, 2025
a11c833
refactor: move auth models to better_networking package
Udhay-Adithya Jul 2, 2025
4284f47
fix: add missing crypto dependency to better_networking package
Udhay-Adithya Jul 2, 2025
6c5862c
refactor: move authModel to HttpRequestModel
Udhay-Adithya Jul 3, 2025
92af4fb
feat: add digest authentication
Udhay-Adithya Jul 3, 2025
d5ca13b
feat: enhance JWT support with private key handling and additional al…
Udhay-Adithya Jul 6, 2025
13bf054
refactor: remove unused digest authentication model import and clean …
Udhay-Adithya Jul 6, 2025
c39b8fb
refactor: remove unused import and logging from JWT generation
Udhay-Adithya Jul 6, 2025
32855fd
refactor: update authentication handling to check authData instead of…
Udhay-Adithya Jul 6, 2025
8d4eedc
tests: add authentication unit tests
Udhay-Adithya Jul 6, 2025
a13d3d5
tests: add auth fields widget tests(coverage 98.4%)
Udhay-Adithya Jul 7, 2025
dcccc2b
tests: add api auth model tests(coverage 100%)
Udhay-Adithya Jul 7, 2025
739afca
tests: improve auth utils tests(coverage 90%)
Udhay-Adithya Jul 7, 2025
e838654
feat: enhance authentication fields with additional info
Udhay-Adithya Jul 7, 2025
38d8e8e
fix: update tooltip icon color and size
Udhay-Adithya Jul 7, 2025
127b2c1
fix: disable onChanged for read-only auth fields
Udhay-Adithya Jul 7, 2025
38eb527
feat: update APIAuthType enum to include display types for authentica…
Udhay-Adithya Jul 7, 2025
65f375c
Merge branch 'foss42:main' into basic-auth
Udhay-Adithya Jul 11, 2025
db93e82
fix: make authModel optional in HistoryRequestModel
Udhay-Adithya Jul 11, 2025
20c7107
fix: update qop label in DigestAuthFields test
Udhay-Adithya Jul 11, 2025
3ca5255
fix: pass null for authData in sendHttpRequest for digest authenticat…
Udhay-Adithya Jul 11, 2025
51c44a3
fix: update label for private key field in JWT auth fields test
Udhay-Adithya Jul 11, 2025
db2f5af
refactor: move auth_textfield to widgets folder
Udhay-Adithya Jul 11, 2025
69e7f7b
docs: add authentication testing links
Udhay-Adithya Jul 11, 2025
9850b7a
Update auth widgets export
animator Jul 12, 2025
99c38c4
refactor API Key auth
animator Jul 12, 2025
5afce74
Update consts.dart
animator Jul 12, 2025
759e906
Update basic_auth_fields.dart
animator Jul 12, 2025
7925a38
Update bearer_auth_fields.dart
animator Jul 12, 2025
5d89dfd
Update consts.dart
animator Jul 12, 2025
568927b
rename AuthTextField
animator Jul 12, 2025
13a4000
refactor imports
animator Jul 12, 2025
4f11185
Update imports in tests
animator Jul 12, 2025
33acbc8
Update pubspec.yaml
animator Jul 12, 2025
447cbd4
refactor for digest
animator Jul 12, 2025
1e10606
Remove comments from JWT
animator Jul 12, 2025
32dcbda
JWT refactor
animator Jul 12, 2025
b8d0d03
reinstate highlight
animator Jul 12, 2025
3948351
turns fields optional
animator Jul 13, 2025
972d8be
Add AuthPage
animator Jul 13, 2025
862319b
Update hisotry rwquest pane auth
animator Jul 13, 2025
245a7b7
Fix EditGraphQLRequestPane provider
animator Jul 13, 2025
351131f
Update pubspec.yaml
animator Jul 13, 2025
744b8c0
Fix dropdown labels
animator Jul 13, 2025
1dea1b4
Fix test
animator Jul 13, 2025
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
11 changes: 11 additions & 0 deletions doc/dev_guide/api_endpoints_for_testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,14 @@ A List of API endpoints that can be used for testing API Dash
## SSE

- https://sse.dev

## Auth
- **Bearer**
- https://httpbin.org/bearer

- **Basic Auth**
- https://httpbin.org/basic-auth/{username}/{password}

- **Digest Auth**
- https://httpbin.org/digest-auth/{qop}/{usenamer}/{password}/{algorithm}

1 change: 1 addition & 0 deletions lib/consts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ const kLabelURLParams = "Params";
const kLabelHeaders = "Headers";
const kLabelBody = "Body";
const kLabelScripts = "Scripts";
const kLabelAuth = "Auth";
const kLabelQuery = "Query";
const kNameCheckbox = "Checkbox";
const kNameURLParam = "URL Parameter";
Expand Down
1 change: 1 addition & 0 deletions lib/models/history_request_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class HistoryRequestModel with _$HistoryRequestModel {
required HttpResponseModel httpResponseModel,
String? preRequestScript,
String? postRequestScript,
AuthModel? authModel,
}) = _HistoryRequestModel;

factory HistoryRequestModel.fromJson(Map<String, Object?> json) =>
Expand Down
61 changes: 53 additions & 8 deletions lib/models/history_request_model.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mixin _$HistoryRequestModel {
HttpResponseModel get httpResponseModel => throw _privateConstructorUsedError;
String? get preRequestScript => throw _privateConstructorUsedError;
String? get postRequestScript => throw _privateConstructorUsedError;
AuthModel? get authModel => throw _privateConstructorUsedError;

/// Serializes this HistoryRequestModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
Expand All @@ -49,11 +50,13 @@ abstract class $HistoryRequestModelCopyWith<$Res> {
HttpRequestModel httpRequestModel,
HttpResponseModel httpResponseModel,
String? preRequestScript,
String? postRequestScript});
String? postRequestScript,
AuthModel? authModel});

$HistoryMetaModelCopyWith<$Res> get metaData;
$HttpRequestModelCopyWith<$Res> get httpRequestModel;
$HttpResponseModelCopyWith<$Res> get httpResponseModel;
$AuthModelCopyWith<$Res>? get authModel;
}

/// @nodoc
Expand All @@ -77,6 +80,7 @@ class _$HistoryRequestModelCopyWithImpl<$Res, $Val extends HistoryRequestModel>
Object? httpResponseModel = null,
Object? preRequestScript = freezed,
Object? postRequestScript = freezed,
Object? authModel = freezed,
}) {
return _then(_value.copyWith(
historyId: null == historyId
Expand All @@ -103,6 +107,10 @@ class _$HistoryRequestModelCopyWithImpl<$Res, $Val extends HistoryRequestModel>
? _value.postRequestScript
: postRequestScript // ignore: cast_nullable_to_non_nullable
as String?,
authModel: freezed == authModel
? _value.authModel
: authModel // ignore: cast_nullable_to_non_nullable
as AuthModel?,
) as $Val);
}

Expand Down Expand Up @@ -135,6 +143,20 @@ class _$HistoryRequestModelCopyWithImpl<$Res, $Val extends HistoryRequestModel>
return _then(_value.copyWith(httpResponseModel: value) as $Val);
});
}

/// Create a copy of HistoryRequestModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$AuthModelCopyWith<$Res>? get authModel {
if (_value.authModel == null) {
return null;
}

return $AuthModelCopyWith<$Res>(_value.authModel!, (value) {
return _then(_value.copyWith(authModel: value) as $Val);
});
}
}

/// @nodoc
Expand All @@ -151,14 +173,17 @@ abstract class _$$HistoryRequestModelImplCopyWith<$Res>
HttpRequestModel httpRequestModel,
HttpResponseModel httpResponseModel,
String? preRequestScript,
String? postRequestScript});
String? postRequestScript,
AuthModel? authModel});

@override
$HistoryMetaModelCopyWith<$Res> get metaData;
@override
$HttpRequestModelCopyWith<$Res> get httpRequestModel;
@override
$HttpResponseModelCopyWith<$Res> get httpResponseModel;
@override
$AuthModelCopyWith<$Res>? get authModel;
}

/// @nodoc
Expand All @@ -180,6 +205,7 @@ class __$$HistoryRequestModelImplCopyWithImpl<$Res>
Object? httpResponseModel = null,
Object? preRequestScript = freezed,
Object? postRequestScript = freezed,
Object? authModel = freezed,
}) {
return _then(_$HistoryRequestModelImpl(
historyId: null == historyId
Expand All @@ -206,6 +232,10 @@ class __$$HistoryRequestModelImplCopyWithImpl<$Res>
? _value.postRequestScript
: postRequestScript // ignore: cast_nullable_to_non_nullable
as String?,
authModel: freezed == authModel
? _value.authModel
: authModel // ignore: cast_nullable_to_non_nullable
as AuthModel?,
));
}
}
Expand All @@ -220,7 +250,8 @@ class _$HistoryRequestModelImpl implements _HistoryRequestModel {
required this.httpRequestModel,
required this.httpResponseModel,
this.preRequestScript,
this.postRequestScript});
this.postRequestScript,
this.authModel});

factory _$HistoryRequestModelImpl.fromJson(Map<String, dynamic> json) =>
_$$HistoryRequestModelImplFromJson(json);
Expand All @@ -237,10 +268,12 @@ class _$HistoryRequestModelImpl implements _HistoryRequestModel {
final String? preRequestScript;
@override
final String? postRequestScript;
@override
final AuthModel? authModel;

@override
String toString() {
return 'HistoryRequestModel(historyId: $historyId, metaData: $metaData, httpRequestModel: $httpRequestModel, httpResponseModel: $httpResponseModel, preRequestScript: $preRequestScript, postRequestScript: $postRequestScript)';
return 'HistoryRequestModel(historyId: $historyId, metaData: $metaData, httpRequestModel: $httpRequestModel, httpResponseModel: $httpResponseModel, preRequestScript: $preRequestScript, postRequestScript: $postRequestScript, authModel: $authModel)';
}

@override
Expand All @@ -259,13 +292,22 @@ class _$HistoryRequestModelImpl implements _HistoryRequestModel {
(identical(other.preRequestScript, preRequestScript) ||
other.preRequestScript == preRequestScript) &&
(identical(other.postRequestScript, postRequestScript) ||
other.postRequestScript == postRequestScript));
other.postRequestScript == postRequestScript) &&
(identical(other.authModel, authModel) ||
other.authModel == authModel));
}

@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, historyId, metaData,
httpRequestModel, httpResponseModel, preRequestScript, postRequestScript);
int get hashCode => Object.hash(
runtimeType,
historyId,
metaData,
httpRequestModel,
httpResponseModel,
preRequestScript,
postRequestScript,
authModel);

/// Create a copy of HistoryRequestModel
/// with the given fields replaced by the non-null parameter values.
Expand All @@ -291,7 +333,8 @@ abstract class _HistoryRequestModel implements HistoryRequestModel {
required final HttpRequestModel httpRequestModel,
required final HttpResponseModel httpResponseModel,
final String? preRequestScript,
final String? postRequestScript}) = _$HistoryRequestModelImpl;
final String? postRequestScript,
final AuthModel? authModel}) = _$HistoryRequestModelImpl;

factory _HistoryRequestModel.fromJson(Map<String, dynamic> json) =
_$HistoryRequestModelImpl.fromJson;
Expand All @@ -308,6 +351,8 @@ abstract class _HistoryRequestModel implements HistoryRequestModel {
String? get preRequestScript;
@override
String? get postRequestScript;
@override
AuthModel? get authModel;

/// Create a copy of HistoryRequestModel
/// with the given fields replaced by the non-null parameter values.
Expand Down
5 changes: 5 additions & 0 deletions lib/models/history_request_model.g.dart

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

8 changes: 7 additions & 1 deletion lib/providers/collection_providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ class CollectionStateNotifier
String? id,
HTTPVerb? method,
APIType? apiType,
AuthModel? authModel,
String? url,
String? name,
String? description,
Expand Down Expand Up @@ -242,6 +243,7 @@ class CollectionStateNotifier
url: url ?? currentHttpRequestModel.url,
headers: headers ?? currentHttpRequestModel.headers,
params: params ?? currentHttpRequestModel.params,
authModel: authModel ?? currentHttpRequestModel.authModel,
isHeaderEnabledList:
isHeaderEnabledList ?? currentHttpRequestModel.isHeaderEnabledList,
isParamEnabledList:
Expand Down Expand Up @@ -315,6 +317,7 @@ class CollectionStateNotifier
var responseRec = await sendHttpRequest(
requestId,
apiType,
requestModel.httpRequestModel?.authModel,
substitutedHttpRequestModel,
defaultUriScheme: defaultUriScheme,
noSSL: noSSL,
Expand Down Expand Up @@ -356,8 +359,11 @@ class CollectionStateNotifier
httpResponseModel: httpResponseModel,
preRequestScript: requestModel.preRequestScript,
postRequestScript: requestModel.postRequestScript,
authModel: requestModel.httpRequestModel?.authModel,
);

ref.read(historyMetaStateNotifier.notifier).addHistoryRequest(model);

if (!requestModel.postRequestScript.isNullOrEmpty()) {
newRequestModel = await handlePostResponseScript(
newRequestModel,
Expand All @@ -373,7 +379,6 @@ class CollectionStateNotifier
},
);
}
ref.read(historyMetaStateNotifier.notifier).addHistoryRequest(model);
}

// update state with response data
Expand Down Expand Up @@ -444,6 +449,7 @@ class CollectionStateNotifier
: (state?[id]?.copyWith(httpResponseModel: null))?.toJson(),
);
}

await hiveHandler.removeUnused();
ref.read(saveDataStateProvider.notifier).state = false;
ref.read(hasUnsavedChangesProvider.notifier).state = false;
Expand Down
103 changes: 103 additions & 0 deletions lib/screens/common_widgets/auth/api_key_auth_fields.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import 'package:flutter/material.dart';
import 'package:apidash_core/apidash_core.dart';
import 'package:apidash_design_system/apidash_design_system.dart';
import 'package:apidash/widgets/widgets.dart';
import 'consts.dart';

class ApiKeyAuthFields extends StatefulWidget {
final AuthModel? authData;
final bool readOnly;
final Function(AuthModel?)? updateAuth;

const ApiKeyAuthFields(
{super.key,
required this.authData,
this.updateAuth,
this.readOnly = false});

@override
State<ApiKeyAuthFields> createState() => _ApiKeyAuthFieldsState();
}

class _ApiKeyAuthFieldsState extends State<ApiKeyAuthFields> {
late TextEditingController _keyController;
late TextEditingController _nameController;
late String _addKeyTo;

@override
void initState() {
super.initState();
final apiAuth = widget.authData?.apikey;
_keyController = TextEditingController(text: apiAuth?.key ?? '');
_nameController =
TextEditingController(text: apiAuth?.name ?? kApiKeyHeaderName);
_addKeyTo = apiAuth?.location ?? kAddToDefaultLocation;
}

@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
kLabelAddTo,
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: 14,
),
),
SizedBox(
height: 4,
),
ADPopupMenu<String>(
value: kAddToLocationsMap[_addKeyTo],
values: kAddToLocations,
tooltip: kTooltipApiKeyAuth,
isOutlined: true,
onChanged: widget.readOnly
? null
: (String? newLocation) {
if (newLocation != null) {
setState(() {
_addKeyTo = newLocation;
});
_updateApiKeyAuth();
}
},
),
const SizedBox(height: 16),
AuthTextField(
readOnly: widget.readOnly,
controller: _nameController,
hintText: kHintTextFieldName,
onChanged: (value) => _updateApiKeyAuth(),
),
const SizedBox(height: 16),
AuthTextField(
readOnly: widget.readOnly,
controller: _keyController,
title: kLabelApiKey,
hintText: kHintTextKey,
isObscureText: true,
onChanged: (value) => _updateApiKeyAuth(),
),
],
);
}

void _updateApiKeyAuth() {
final apiKey = AuthApiKeyModel(
key: _keyController.text.trim(),
name: _nameController.text.trim(),
location: _addKeyTo,
);
widget.updateAuth?.call(widget.authData?.copyWith(
type: APIAuthType.apiKey,
apikey: apiKey,
) ??
AuthModel(
type: APIAuthType.apiKey,
apikey: apiKey,
));
}
}
6 changes: 6 additions & 0 deletions lib/screens/common_widgets/auth/auth.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export 'api_key_auth_fields.dart';
export 'auth_page.dart';
export 'basic_auth_fields.dart';
export 'bearer_auth_fields.dart';
export 'digest_auth_fields.dart';
export 'jwt_auth_fields.dart';
Loading