Skip to content
This repository was archived by the owner on Mar 16, 2025. It is now read-only.
Open
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
6 changes: 6 additions & 0 deletions android/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>
8 changes: 8 additions & 0 deletions android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
23 changes: 23 additions & 0 deletions android/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>flutter_web_auth_2</name>
<comment>Project flutter_web_auth_2 created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>
2 changes: 2 additions & 0 deletions android/.settings/org.eclipse.buildship.core.prefs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
connection.project.dir=../example/android
eclipse.preferences.version=1
48 changes: 48 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
group 'com.teranet.oauth2_client'
version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:7.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 31

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
minSdkVersion 16
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}

dependencies {
implementation 'androidx.browser:browser:1.4.0'
}
}

dependencies {
implementation 'androidx.wear:wear-phone-interactions:1.0.1'
}
3 changes: 3 additions & 0 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
5 changes: 5 additions & 0 deletions android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
1 change: 1 addition & 0 deletions android/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'flutter_web_auth_2'
5 changes: 5 additions & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.teranet.oauth2_client">
<uses-sdk tools:overrideLibrary="androidx.security.ktx androidx.wear.phone.interactions" />
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.teranet.oauth2_client

import android.content.Context
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build

import androidx.wear.phone.interactions.authentication.*

import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.PluginRegistry.Registrar
import java.util.concurrent.Executors

class OAuth2ClientPlugin(private var context: Context? = null, private var channel: MethodChannel? = null): MethodCallHandler, FlutterPlugin {
private lateinit var remoteAuthClient: RemoteAuthClient

companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val plugin = OAuth2ClientPlugin()
plugin.initInstance(registrar.messenger(), registrar.context())
}

}

fun initInstance(messenger: BinaryMessenger, context: Context) {
this.context = context
remoteAuthClient = RemoteAuthClient.create(context)
channel = MethodChannel(messenger, "oauth2_client")
channel?.setMethodCallHandler(this)
}

override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
initInstance(binding.binaryMessenger, binding.applicationContext)
}

override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
context = null
channel = null
}

private fun buildWatchAuthUri(url: Uri): Uri? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && context != null &&
context!!.packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
val context = context!!
val request = OAuthRequest.Builder(context)
.setAuthProviderUrl(url)
.build()
request.requestUrl
} else {
null
}
}

override fun onMethodCall(call: MethodCall, resultCallback: MethodChannel.Result) {
when (call.method) {
"authUrl" -> {
val url = call.argument<String>("url")
val authUri = buildWatchAuthUri(Uri.parse(url))
if (authUri == null) {
resultCallback.notImplemented()
return
}
resultCallback.success(authUri.toString())
}
"authenticate" -> {
val url = Uri.parse(call.argument("url")!!)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && context != null &&
context!!.packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
val context = context!!
val request = OAuthRequest.Builder(context)
.setAuthProviderUrl(url)
.build()
remoteAuthClient.sendAuthorizationRequest(request,
Executors.newSingleThreadExecutor(),
object : RemoteAuthClient.Callback() {
override fun onAuthorizationResponse(
request: OAuthRequest,
response: OAuthResponse
) {
resultCallback.success(response.responseUrl.toString())
}

override fun onAuthorizationError(request: OAuthRequest, errorCode: Int) {
val message = when (errorCode) {
RemoteAuthClient.ERROR_UNSUPPORTED -> "Auth not supported"
RemoteAuthClient.ERROR_PHONE_UNAVAILABLE -> "Phone unavailable"
else -> "Unknown error: $errorCode"
}
resultCallback.error(errorCode.toString(), message, "No details")
}
}
)
return
} else {
// Pass through if not compatible
resultCallback.notImplemented()
}
}
else -> resultCallback.notImplemented()
}
}
}
10 changes: 10 additions & 0 deletions lib/authorization_response.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:flutter/services.dart';

/// Represents the response to an Authorization Request.
/// see https://tools.ietf.org/html/rfc6749#page-26
class AuthorizationResponse {
Expand All @@ -8,6 +10,14 @@ class AuthorizationResponse {
String? error;
String? errorDescription;

AuthorizationResponse.fromError(PlatformException exception) {
code = exception.code;
state = null;
queryParams = {};
error = exception.code;
errorDescription = exception.message;
}

AuthorizationResponse.fromRedirectUri(
String redirectUri, String? checkState) {
queryParams = Uri.parse(redirectUri).queryParameters;
Expand Down
40 changes: 32 additions & 8 deletions lib/oauth2_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import 'package:oauth2_client/access_token_response.dart';
import 'package:oauth2_client/authorization_response.dart';
import 'package:oauth2_client/oauth2_response.dart';
import 'package:oauth2_client/src/oauth2_utils.dart';
import 'package:oauth2_client/src/wear_auth.dart';
import 'package:random_string/random_string.dart';

// import 'package:oauth2_client/src/web_auth.dart';

import 'src/base_web_auth.dart';
import 'src/web_auth.dart'
// ignore: uri_does_not_exist
Expand All @@ -19,6 +18,15 @@ import 'src/web_auth.dart'

enum CredentialsLocation { header, body }

class AuthorizationWrapper {
final AuthorizationResponse authResponse;
final String? redirectUrl;
AuthorizationWrapper({
required this.authResponse,
required this.redirectUrl
});
}

/// Base class that implements OAuth2 authorization flows.
///
/// It currently supports the following grants:
Expand Down Expand Up @@ -153,7 +161,7 @@ class OAuth2Client {
}

try {
var authResp = await requestAuthorization(
var authRespWrapper = await requestAuthorization(
webAuthClient: webAuthClient,
clientId: clientId,
scopes: scopes,
Expand All @@ -162,12 +170,18 @@ class OAuth2Client {
state: state,
customParams: authCodeParams,
webAuthOpts: webAuthOpts);
var authResp = authRespWrapper.authResponse;

if (authResp.isAccessGranted()) {
if (afterAuthorizationCodeCb != null) {
afterAuthorizationCodeCb(authResp);
}

final newTokenParams = Map<String, dynamic>.from(accessTokenParams ?? {});
if (authRespWrapper.redirectUrl != null) {
newTokenParams['redirect_uri'] = authRespWrapper.redirectUrl;
}

tknResp = await requestAccessToken(
httpClient: httpClient,
//If the authorization request was successfull, the code must be set
Expand All @@ -177,7 +191,7 @@ class OAuth2Client {
scopes: scopes,
clientSecret: clientSecret,
codeVerifier: codeVerifier,
customParams: accessTokenParams,
customParams: newTokenParams,
customHeaders: accessTokenHeaders);
} else {
tknResp = AccessTokenResponse.errorResponse();
Expand Down Expand Up @@ -214,7 +228,7 @@ class OAuth2Client {
}

/// Requests an Authorization Code to be used in the Authorization Code grant.
Future<AuthorizationResponse> requestAuthorization(
Future<AuthorizationWrapper> requestAuthorization(
{required String clientId,
List<String>? scopes,
String? codeChallenge,
Expand All @@ -229,7 +243,7 @@ class OAuth2Client {
state ??= randomAlphaNumeric(25);
}

final authorizeUrl = getAuthorizeUrl(
final authorizeUrlStem = getAuthorizeUrl(
clientId: clientId,
redirectUri: redirectUri,
scopes: scopes,
Expand All @@ -238,14 +252,24 @@ class OAuth2Client {
codeChallenge: codeChallenge,
customParams: customParams);

final authorizeUrl = await WearAuth.authUrl(url: authorizeUrlStem);
// Present the dialog to the user
final result = await webAuthClient.authenticate(
AuthorizationResponse authResp;
try {
final result = await webAuthClient.authenticate(
url: authorizeUrl,
callbackUrlScheme: customUriScheme,
redirectUrl: redirectUri,
opts: webAuthOpts);

return AuthorizationResponse.fromRedirectUri(result, state);
authResp = AuthorizationResponse.fromRedirectUri(result, state);
} on PlatformException catch(err) {
authResp = AuthorizationResponse.fromError(err);
}
return AuthorizationWrapper(
authResponse: authResp,
redirectUrl: Uri.parse(authorizeUrl).queryParameters['redirect_uri'],
);
}

/// Requests and Access Token using the provided Authorization [code].
Expand Down
8 changes: 8 additions & 0 deletions lib/src/io_web_auth.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:flutter_web_auth_2/flutter_web_auth_2.dart';

import 'base_web_auth.dart';
import 'wear_auth.dart';
import 'package:wear_bridge/wear_bridge.dart';

BaseWebAuth createWebAuth() => IoWebAuth();

Expand All @@ -11,6 +13,12 @@ class IoWebAuth implements BaseWebAuth {
required String url,
required String redirectUrl,
Map<String, dynamic>? opts}) async {
if (await WearBridge.isWatch()) {
return await WearAuth.authenticate(
url: url,
redirectUrl: redirectUrl,
);
}
return await FlutterWebAuth2.authenticate(
callbackUrlScheme: callbackUrlScheme,
url: url,
Expand Down
Loading