Skip to content

Commit

Permalink
Close change password page on success authgear#32
Browse files Browse the repository at this point in the history
  • Loading branch information
IniZio committed Feb 28, 2024
1 parent a4861ae commit 41761e9
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 21 deletions.
4 changes: 4 additions & 0 deletions example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
android:host="host"
android:pathPrefix="/open_wechat_app"
android:scheme="com.authgear.exampleapp.flutter" />
<data
android:host="host"
android:pathPrefix="/after-changing-password"
android:scheme="com.authgear.exampleapp.flutter" />
</intent-filter>
</activity>
<activity
Expand Down
30 changes: 30 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:shared_preferences/shared_preferences.dart'
import 'package:flutter_authgear/flutter_authgear.dart';

const redirectURI = "com.authgear.exampleapp.flutter://host/path";
const settingsActionRedirectURI = "com.authgear.exampleapp.flutter://host/after-changing-password";
var wechatRedirectURI = "";

const _nativeMethodChannel = MethodChannel("example");
Expand Down Expand Up @@ -515,6 +516,16 @@ class _MyAppState extends State<MyApp> {
_onPressOpenSettings(context);
},
),
SessionStateButton(
sessionState: _authgear.sessionState,
targetState: SessionState.authenticated,
label: "Change Password",
onPressed: _unconfigured || _loading
? null
: () {
_onPressChangePassword(context);
},
),
SessionStateButton(
sessionState: _authgear.sessionState,
targetState: SessionState.authenticated,
Expand Down Expand Up @@ -808,6 +819,25 @@ class _MyAppState extends State<MyApp> {
}
}

Future<void> _onPressChangePassword(BuildContext context) async {
try {
setState(() {
_loading = true;
});
await _authgear.changePassword(
redirectURI: settingsActionRedirectURI,
colorScheme: _getColorScheme(context),
wechatRedirectURI: wechatRedirectURI,
);
} catch (e) {
onError(context, e);
} finally {
setState(() {
_loading = false;
});
}
}

Future<void> _onPressShowAuthTime(BuildContext context) async {
showDialog(
context: context,
Expand Down
12 changes: 4 additions & 8 deletions ios/Classes/SwiftAuthgearPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public class SwiftAuthgearPlugin: NSObject, FlutterPlugin, ASWebAuthenticationPr
return
}

result(FlutterError.unreachable)
result(FlutterError.cancel)
return
}

Expand Down Expand Up @@ -237,7 +237,7 @@ public class SwiftAuthgearPlugin: NSObject, FlutterPlugin, ASWebAuthenticationPr
return
}

result(FlutterError.unreachable)
result(FlutterError.cancel)
return
}
controller.navigationBarBackgroundColor = navigationBarBackgroundColor
Expand All @@ -263,13 +263,13 @@ public class SwiftAuthgearPlugin: NSObject, FlutterPlugin, ASWebAuthenticationPr
return
}

result(FlutterError.unreachable)
result(FlutterError.cancel)
return
}
if #available(iOS 12, *) {
let session = ASWebAuthenticationSession(
url: url,
callbackURLScheme: "nocallback",
callbackURLScheme: "authgearsdk",
completionHandler: completionHandler
)
if #available(iOS 13, *) {
Expand Down Expand Up @@ -895,10 +895,6 @@ fileprivate extension NSError {
}

fileprivate extension FlutterError {
static var unreachable: FlutterError {
return FlutterError(code: "UNREACHABLE", message: "unreachable", details: nil)
}

static var cancel: FlutterError {
return FlutterError(code: "CANCEL", message: "cancel", details: nil)
}
Expand Down
15 changes: 11 additions & 4 deletions lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'type.dart';
class OIDCAuthenticationRequest {
final String clientID;
final String redirectURI;
final String responseType;
final ResponseType responseType;
final bool isSsoEnabled;
final List<String> scope;
final String? codeChallenge;
Expand All @@ -21,6 +21,7 @@ class OIDCAuthenticationRequest {
final int? maxAge;
final AuthenticationPage? page;
final String? wechatRedirectURI;
final SettingsAction? settingsAction;

OIDCAuthenticationRequest({
required this.clientID,
Expand All @@ -38,11 +39,12 @@ class OIDCAuthenticationRequest {
this.maxAge,
this.page,
this.wechatRedirectURI,
this.settingsAction,
});

Map<String, String> toQueryParameters() {
final q = {
"response_type": responseType,
"response_type": responseType.value,
"client_id": clientID,
"redirect_uri": redirectURI,
"scope": scope.join(" "),
Expand Down Expand Up @@ -115,12 +117,17 @@ class OIDCAuthenticationRequest {
q["x_wechat_redirect_uri"] = wechatRedirectURI;
}

final settingsAction = this.settingsAction;
if (settingsAction != null) {
q["x_settings_action"] = settingsAction.value;
}

return q;
}
}

class OIDCTokenRequest {
final String grantType;
final GrantType grantType;
final String clientID;
final String? code;
final String? redirectURI;
Expand All @@ -145,7 +152,7 @@ class OIDCTokenRequest {
Map<String, String> toQueryParameters() {
final q = {
"client_id": clientID,
"grant_type": grantType,
"grant_type": grantType.value,
};

final code = this.code;
Expand Down
148 changes: 141 additions & 7 deletions lib/src/container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class AuthenticateOptions {
return OIDCAuthenticationRequest(
clientID: clientID,
redirectURI: redirectURI,
responseType: "code",
responseType: ResponseType.code,
scope: [
"openid",
"offline_access",
Expand Down Expand Up @@ -113,7 +113,7 @@ class ReauthenticateOptions {
final oidcRequest = OIDCAuthenticationRequest(
clientID: clientID,
redirectURI: redirectURI,
responseType: "code",
responseType: ResponseType.code,
scope: [
"openid",
"https://authgear.com/scopes/full-access",
Expand All @@ -130,6 +130,43 @@ class ReauthenticateOptions {
}
}

class SettingsActionOptions {
final SettingsAction settingAction;
final String redirectURI;
final List<String>? uiLocales;
final ColorScheme? colorScheme;
final String? wechatRedirectURI;

SettingsActionOptions({
required this.settingAction,
required this.redirectURI,
this.uiLocales,
this.colorScheme,
this.wechatRedirectURI,
});

OIDCAuthenticationRequest toRequest(String clientID, String idTokenHint, String loginHint, CodeVerifier verifier) {
return OIDCAuthenticationRequest(
clientID: clientID,
redirectURI: redirectURI,
responseType: ResponseType.settingsAction,
scope: [
"openid",
"offline_access",
"https://authgear.com/scopes/full-access",
],
isSsoEnabled: false,
codeChallenge: verifier.codeChallenge,
uiLocales: uiLocales,
colorScheme: colorScheme,
idTokenHint: idTokenHint,
loginHint: loginHint,
wechatRedirectURI: wechatRedirectURI,
settingsAction: settingAction,
);
}
}

// It seems that dart's convention of iOS' delegate is individual property of write-only function
// See https://api.dart.dev/stable/2.16.2/dart-io/HttpClient/authenticate.html
class Authgear implements AuthgearHttpClientDelegate {
Expand Down Expand Up @@ -370,7 +407,7 @@ class Authgear implements AuthgearHttpClientDelegate {
final oidcRequest = OIDCAuthenticationRequest(
clientID: clientID,
redirectURI: redirectURI,
responseType: "none",
responseType: ResponseType.none,
scope: [
"openid",
"offline_access",
Expand Down Expand Up @@ -449,13 +486,93 @@ class Authgear implements AuthgearHttpClientDelegate {
wechatRedirectURI: wechatRedirectURI);
}

Future<InternalAuthenticateRequest> internalCreateSettingsActionRequest(
String clientID,
String idTokenHint,
String loginHint,
SettingsActionOptions options
) async {
final codeVerifier = CodeVerifier(_rng);
final oidcRequest = options.toRequest(clientID, idTokenHint, loginHint, codeVerifier);
final url = await internalBuildAuthorizationURL(oidcRequest);

return InternalAuthenticateRequest(
url: url,
redirectURI: oidcRequest.redirectURI,
verifier: codeVerifier,
);
}

Future<void> _openSettingsAction({
required SettingsAction action,
required String redirectURI,
List<String>? uiLocales,
ColorScheme? colorScheme,
String? wechatRedirectURI,
}) async {
final idTokenHint = this.idTokenHint;
final refreshToken = _refreshToken;
if (refreshToken == null || idTokenHint == null) {
throw Exception("authenticated user required");
}

final appSessionTokenResp = await _getAppSessionToken(refreshToken);
final loginHint =
Uri.parse("https://authgear.com/login_hint").replace(queryParameters: {
"type": "app_session_token",
"app_session_token": appSessionTokenResp.appSessionToken,
}).toString();

final options = SettingsActionOptions(
settingAction: action,
redirectURI: redirectURI,
uiLocales: uiLocales,
colorScheme: colorScheme,
wechatRedirectURI: wechatRedirectURI,
);

final request =
await internalCreateSettingsActionRequest(clientID, idTokenHint, loginHint, options);

if (wechatRedirectURI != null) {
await native.registerWechatRedirectURI(
onWechatRedirectURI: _onWechatRedirectURI,
wechatRedirectURI: wechatRedirectURI);
}

final resultURL = await _uiImplementation.openAuthorizationURL(
url: request.url.toString(),
redirectURI: redirectURI,
shareCookiesWithDeviceBrowser: isSsoEnabled,
);
return await _finishSettingsAction(
url: Uri.parse(resultURL),
redirectURI: redirectURI,
codeVerifier: request.verifier);
}

Future<void> changePassword({
required String redirectURI,
List<String>? uiLocales,
ColorScheme? colorScheme,
String? wechatRedirectURI
}) {
return _openSettingsAction(
action: SettingsAction.changePassword,
redirectURI: redirectURI,
uiLocales: uiLocales,
colorScheme: colorScheme,
wechatRedirectURI: wechatRedirectURI,
);
}

Future<void> refreshIDToken() async {
if (shouldRefreshAccessToken) {
await refreshAccessToken();
}

final tokenRequest = OIDCTokenRequest(
grantType: "urn:authgear:params:oauth:grant-type:id-token",
grantType: GrantType.idToken,
clientID: clientID,
accessToken: accessToken,
);
Expand Down Expand Up @@ -520,7 +637,7 @@ class Authgear implements AuthgearHttpClientDelegate {

final xDeviceInfo = await _getXDeviceInfo();
final tokenRequest = OIDCTokenRequest(
grantType: "refresh_token",
grantType: GrantType.refreshToken,
clientID: clientID,
refreshToken: refreshToken,
xDeviceInfo: xDeviceInfo,
Expand Down Expand Up @@ -669,7 +786,7 @@ class Authgear implements AuthgearHttpClientDelegate {
final oidcRequest = OIDCAuthenticationRequest(
clientID: clientID,
redirectURI: redirectURI,
responseType: "code",
responseType: ResponseType.code,
scope: [
"openid",
"offline_access",
Expand Down Expand Up @@ -801,6 +918,7 @@ class Authgear implements AuthgearHttpClientDelegate {
required String redirectURI,
required CodeVerifier codeVerifier,
required String xDeviceInfo,
GrantType grantType = GrantType.authorizationCode,
}) async {
final queryParameters = url.queryParameters;
final error = queryParameters["error"];
Expand All @@ -825,7 +943,7 @@ class Authgear implements AuthgearHttpClientDelegate {
}

final tokenRequest = OIDCTokenRequest(
grantType: "authorization_code",
grantType: grantType,
clientID: clientID,
code: code,
redirectURI: redirectURI,
Expand Down Expand Up @@ -855,6 +973,22 @@ class Authgear implements AuthgearHttpClientDelegate {
return userInfo;
}

Future<void> _finishSettingsAction({
required Uri url,
required String redirectURI,
required CodeVerifier codeVerifier,
}) async {
final xDeviceInfo = await _getXDeviceInfo();
await _exchangeCode(
url: url,
redirectURI: redirectURI,
codeVerifier: codeVerifier,
xDeviceInfo: xDeviceInfo,
grantType: GrantType.settingsAction,
);
await disableBiometric();
}

Future<UserInfo> _finishReauthentication({
required Uri url,
required String redirectURI,
Expand Down
Loading

0 comments on commit 41761e9

Please sign in to comment.