Skip to content
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

feat : Added Basic Authorization in API request #680

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions lib/consts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ const kLabelRequest = "Request";
const kLabelHideCode = "Hide Code";
const kLabelViewCode = "View Code";
const kLabelURLParams = "URL Params";
const kLabelAuth = "Authorization";
const kLabelHeaders = "Headers";
const kLabelBody = "Body";
const kLabelQuery = "Query";
Expand Down
42 changes: 42 additions & 0 deletions lib/providers/auth_providers.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'dart:convert';
import 'package:apidash_core/consts.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

// Provider for selected auth type
final authTypeProvider = StateProvider<AuthType>((ref) => AuthType.none);

// Provider for auth data (e.g., username/password for Basic Auth)
final authDataProvider = StateProvider<Map<String, String>?>((ref) => null);

// Provider to compute the Authorization header
final authHeaderProvider = Provider<String?>((ref) {
final authType = ref.watch(authTypeProvider);
final authData = ref.watch(authDataProvider);

if (authType == AuthType.none || authData == null || authData.isEmpty) {
return null;
}

switch (authType) {
case AuthType.oAuth2:
return null;
case AuthType.oAuth1:
return null;
case AuthType.digest:
return null;
case AuthType.jwtBearer:
return null;
case AuthType.bearer:
return null;
case AuthType.apiKey:
return null;
case AuthType.basic:
final username = authData['username'] ?? '';
final password = authData['password'] ?? '';
if (username.isEmpty && password.isEmpty) return null;
final authString = base64Encode(utf8.encode('$username:$password'));
return 'Basic $authString';
case AuthType.none:
return null;
}
});
3 changes: 3 additions & 0 deletions lib/providers/collection_providers.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:apidash/providers/auth_providers.dart';
import 'package:apidash_core/apidash_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Expand Down Expand Up @@ -279,6 +280,7 @@ class CollectionStateNotifier
APIType apiType = requestModel!.apiType;
HttpRequestModel substitutedHttpRequestModel =
getSubstitutedHttpRequestModel(requestModel.httpRequestModel!);
final authHeader = ref.read(authHeaderProvider);

// set current model's isWorking to true and update state
var map = {...state!};
Expand All @@ -293,6 +295,7 @@ class CollectionStateNotifier
requestId,
apiType,
substitutedHttpRequestModel,
authHeader: authHeader,
defaultUriScheme: defaultUriScheme,
noSSL: noSSL,
);
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/dashboard.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/consts.dart';
import 'package:apidash/dashbot/dashbot.dart';
//import 'package:apidash/dashbot/dashbot.dart';
import 'common_widgets/common_widgets.dart';
import 'envvar/environment_page.dart';
import 'home_page/home_page.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import 'package:apidash/consts.dart';
import 'package:apidash/providers/auth_providers.dart';
import 'package:apidash/widgets/dropdown_auth_type.dart';
import 'package:apidash/widgets/text_auth.dart';
import 'package:apidash_core/apidash_core.dart';
import 'package:apidash_design_system/tokens/measurements.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class EditRequestAuth extends ConsumerStatefulWidget {
const EditRequestAuth({super.key});

@override
ConsumerState<EditRequestAuth> createState() => _EditRequestAuthState();
}

class _EditRequestAuthState extends ConsumerState<EditRequestAuth> {
late final TextEditingController usernameController;
late final TextEditingController passwordController;

@override
void initState() {
super.initState();
usernameController = TextEditingController();
passwordController = TextEditingController();
}

@override
void dispose() {
usernameController.dispose();
passwordController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
final authData = ref.watch(authDataProvider) ?? {};
final authType = ref.watch(authTypeProvider);

return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: kHeaderHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
const Text('Select Authorization Type:'),
DropdownButtonAuthType(
authType: authType,
onChanged: (AuthType? value) {
if (value != null) {
ref.read(authTypeProvider.notifier).state = value;
if (value == AuthType.none) {
ref.read(authDataProvider.notifier).state = null;
}
}
},
),
],
),
),
if (authType == AuthType.basic) ...[
Padding(
padding: kPt5o10,
child: AuthTextField(
controller: usernameController,
labelText: 'Username',
obscureText: false,
onChanged: (value) {
_updateAuth(ref, usernameController, passwordController);
},
),
),
Padding(
padding: kPt5o10,
child: AuthTextField(
controller: passwordController,
labelText: 'Password',
obscureText: true,
onChanged: (value) {
_updateAuth(ref, usernameController, passwordController);
},
),
),
],
],
),
);
}

void _updateAuth(WidgetRef ref, TextEditingController usernameController,
TextEditingController passwordController) {
ref.read(authTypeProvider.notifier).state = AuthType.basic;
ref.read(authDataProvider.notifier).state = {
'username': usernameController.text,
'password': passwordController.text,
};
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import 'package:apidash/consts.dart';
import 'package:apidash/providers/auth_providers.dart';
import 'package:apidash/screens/home_page/editor_pane/details_card/request_pane/request_auth.dart';
import 'package:apidash_core/consts.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart';
Expand All @@ -18,6 +21,8 @@ class EditGraphQLRequestPane extends ConsumerWidget {
final headerLength = ref.watch(selectedRequestModelProvider
.select((value) => value?.httpRequestModel?.headersMap.length)) ??
0;
final hasAuth = ref.watch(authTypeProvider) != AuthType.none &&
(ref.watch(authDataProvider)?.isNotEmpty ?? false);
final hasQuery = ref.watch(selectedRequestModelProvider
.select((value) => value?.httpRequestModel?.hasQuery)) ??
false;
Expand All @@ -39,14 +44,17 @@ class EditGraphQLRequestPane extends ConsumerWidget {
},
showIndicators: [
headerLength > 0,
hasAuth,
hasQuery,
],
tabLabels: const [
kLabelHeaders,
kLabelAuth,
kLabelQuery,
],
children: const [
EditRequestHeaders(),
EditRequestAuth(),
EditRequestBody(),
],
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import 'package:apidash/consts.dart';
import 'package:apidash/providers/auth_providers.dart';
import 'package:apidash/screens/home_page/editor_pane/details_card/request_pane/request_auth.dart';
import 'package:apidash_core/consts.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart';
Expand All @@ -20,6 +23,9 @@ class EditRestRequestPane extends ConsumerWidget {
final headerLength = ref.watch(selectedRequestModelProvider
.select((value) => value?.httpRequestModel?.headersMap.length)) ??
0;
final hasAuth = ref.watch(authTypeProvider) !=
AuthType.none &&
(ref.watch(authDataProvider)?.isNotEmpty ?? false);
final paramLength = ref.watch(selectedRequestModelProvider
.select((value) => value?.httpRequestModel?.paramsMap.length)) ??
0;
Expand All @@ -42,16 +48,19 @@ class EditRestRequestPane extends ConsumerWidget {
},
showIndicators: [
paramLength > 0,
hasAuth,
headerLength > 0,
hasBody,
],
tabLabels: const [
kLabelURLParams,
kLabelAuth,
kLabelHeaders,
kLabelBody,
],
children: const [
EditRequestURLParams(),
EditRequestAuth(),
EditRequestHeaders(),
EditRequestBody(),
],
Expand Down
61 changes: 61 additions & 0 deletions lib/widgets/dropdown_auth_type.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import 'package:apidash_core/apidash_core.dart';
import 'package:flutter/material.dart';

class DropdownButtonAuthType extends StatelessWidget {
final AuthType authType;
final ValueChanged<AuthType?> onChanged;

const DropdownButtonAuthType({
super.key,
required this.authType,
required this.onChanged,
});

@override
Widget build(BuildContext context) {
return DropdownButton<AuthType>(
value: authType,
onChanged: onChanged,
dropdownColor: Colors.white,
icon: const Icon(Icons.arrow_drop_down, color: Colors.blue),
underline: Container(height: 0),
padding: const EdgeInsets.symmetric(horizontal: 12.0),
borderRadius: BorderRadius.circular(8.0),
elevation: 4,
items: const [
DropdownMenuItem(
value: AuthType.none,
child: Text('None'),
),
DropdownMenuItem(
value: AuthType.basic,
child: Text('Basic'),
),
DropdownMenuItem(
value: AuthType.apiKey,
child: Text('API Key'),
),
DropdownMenuItem(
value: AuthType.bearer,
child: Text('Bearer Token'),
),
DropdownMenuItem(
value: AuthType.jwtBearer,
child: Text('JWT Bearer'),
),
DropdownMenuItem(
value: AuthType.digest,
child: Text('Digest Auth'),
),
DropdownMenuItem(
value: AuthType.oAuth1,
child: Text('OAuth1.0'),
),
DropdownMenuItem(
value: AuthType.oAuth2,
child: Text('OAuth2.0'),
),
],
);
}
}
45 changes: 45 additions & 0 deletions lib/widgets/text_auth.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
class AuthTextField extends StatelessWidget {
// ignore: prefer_typing_uninitialized_variables
final controller;
final String labelText;
final bool obscureText;
final void Function(String)? onChanged;

const AuthTextField({
super.key,
required this.controller,
required this.labelText,
required this.obscureText,
required this.onChanged,
});

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(4),
child: TextField(
onChanged: onChanged,
controller: controller,
obscureText: obscureText,
decoration: InputDecoration(
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
borderRadius: BorderRadius.all(
Radius.circular(16),
),
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
borderRadius: BorderRadius.all(
Radius.circular(16),
),
),
fillColor: Colors.white,
filled: true,
labelText: labelText,
),
),
);
}
}
13 changes: 13 additions & 0 deletions packages/apidash_core/lib/consts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ enum ContentType {
final String header;
}

enum AuthType {
none("None"),
basic("Basic Auth"),
apiKey("API Key"),
bearer("Bearer Token"),
jwtBearer("JWT Bearer"),
digest("Digest Auth"),
oAuth1("OAuth 1.0"),
oAuth2("OAuth 2.0");
const AuthType(this.header);
final String header;
}

const JsonEncoder kJsonEncoder = JsonEncoder.withIndent(' ');
const JsonDecoder kJsonDecoder = JsonDecoder();
const LineSplitter kSplitter = LineSplitter();
Expand Down
Loading