From 6391443d09d4fa20c1a0c257dbf8b364041cf691 Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Mon, 21 Jul 2025 00:40:28 +0530 Subject: [PATCH] feat: implement resource owner password grant --- .../lib/utils/auth/handle_auth.dart | 20 +++++- .../lib/utils/auth/oauth2_utils.dart | 68 +++++++++++++++++-- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/packages/better_networking/lib/utils/auth/handle_auth.dart b/packages/better_networking/lib/utils/auth/handle_auth.dart index 2ed3e4fa..c0ba15f2 100644 --- a/packages/better_networking/lib/utils/auth/handle_auth.dart +++ b/packages/better_networking/lib/utils/auth/handle_auth.dart @@ -202,7 +202,7 @@ Future handleAuth( break; case OAuth2GrantType.clientCredentials: - final client = await oAuth2ClientCredentialsHandler( + final client = await oAuth2ClientCredentialsGrantHandler( oauth2Model: oauth2, credentialsFile: credentialsFile, ); @@ -218,8 +218,22 @@ Future handleAuth( updatedHeaderEnabledList.add(true); break; case OAuth2GrantType.resourceOwnerPassword: - // TODO: Handle this case. - throw UnimplementedError(); + debugPrint("==Resource Owner Password=="); + final client = await oAuth2ResourceOwnerPasswordGrantHandler( + oauth2Model: oauth2, + credentialsFile: credentialsFile, + ); + debugPrint(client.credentials.accessToken); + + // Add the access token to the request headers + updatedHeaders.add( + NameValueModel( + name: 'Authorization', + value: 'Bearer ${client.credentials.accessToken}', + ), + ); + updatedHeaderEnabledList.add(true); + break; } } diff --git a/packages/better_networking/lib/utils/auth/oauth2_utils.dart b/packages/better_networking/lib/utils/auth/oauth2_utils.dart index 7a76f678..9a080cdc 100644 --- a/packages/better_networking/lib/utils/auth/oauth2_utils.dart +++ b/packages/better_networking/lib/utils/auth/oauth2_utils.dart @@ -76,7 +76,7 @@ Future oAuth2AuthorizationCodeGrantHandler({ } } -Future oAuth2ClientCredentialsHandler({ +Future oAuth2ClientCredentialsGrantHandler({ required AuthOAuth2Model oauth2Model, required File credentialsFile, }) async { @@ -88,12 +88,10 @@ Future oAuth2ClientCredentialsHandler({ if (credentials.accessToken.isNotEmpty && !credentials.isExpired) { log('Using existing valid credentials'); - // TODO: This adds the client_id parameter to the body instead of the header - return oauth2.clientCredentialsGrant( - Uri.parse(oauth2Model.authorizationUrl), - oauth2Model.clientId, - oauth2Model.clientSecret, - scopes: oauth2Model.scope != null ? [oauth2Model.scope!] : null, + return oauth2.Client( + credentials, + identifier: oauth2Model.clientId, + secret: oauth2Model.clientSecret, ); } } catch (e) { @@ -109,6 +107,62 @@ Future oAuth2ClientCredentialsHandler({ oauth2Model.clientId, oauth2Model.clientSecret, scopes: oauth2Model.scope != null ? [oauth2Model.scope!] : null, + basicAuth: false, + ); + log("Created Client with id: ${client.identifier}"); + log("Created Client with sec: ${client.secret}"); + log("Created Client with sec: ${client.credentials.toJson()}"); + + log('Successfully authenticated via client credentials grant'); + + try { + await credentialsFile.writeAsString(client.credentials.toJson()); + log('Saved credentials to file'); + } catch (e) { + log('Failed to save credentials: $e'); + } + + return client; +} + +Future oAuth2ResourceOwnerPasswordGrantHandler({ + required AuthOAuth2Model oauth2Model, + required File credentialsFile, +}) async { + // Try to use saved credentials + if (await credentialsFile.exists()) { + try { + final json = await credentialsFile.readAsString(); + final credentials = oauth2.Credentials.fromJson(json); + + if (credentials.accessToken.isNotEmpty && !credentials.isExpired) { + log('Using existing valid credentials'); + return oauth2.Client( + credentials, + identifier: oauth2Model.clientId, + secret: oauth2Model.clientSecret, + ); + } + } catch (e) { + log('Error reading existing credentials: $e'); + } + } + if ((oauth2Model.username == null || oauth2Model.username!.isEmpty) || + (oauth2Model.password == null || oauth2Model.password!.isEmpty)) { + throw Exception("Username or Password cannot be empty"); + } + log("Creating Client with id: ${oauth2Model.clientId}"); + log("Creating Client with sec: ${oauth2Model.clientSecret}"); + + // Otherwise, perform the owner password grant + final client = await oauth2.resourceOwnerPasswordGrant( + Uri.parse(oauth2Model.authorizationUrl), + oauth2Model.username!, + oauth2Model.password!, + identifier: oauth2Model.clientId, + secret: oauth2Model.clientSecret, + scopes: oauth2Model.scope != null ? [oauth2Model.scope!] : null, + basicAuth: false, ); log("Created Client with id: ${client.identifier}"); log("Created Client with sec: ${client.secret}");