Skip to content

Commit

Permalink
Close change password page on success authgear#183
Browse files Browse the repository at this point in the history
  • Loading branch information
IniZio committed Feb 27, 2024
1 parent c555fe7 commit c20c972
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 3 deletions.
3 changes: 3 additions & 0 deletions javasample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@
<data android:scheme="com.authgear.exampleapp.android"
android:host="host"
android:pathPrefix="/open_wechat_app"/>
<data android:scheme="com.authgear.exampleapp.android"
android:host="host"
android:pathPrefix="/after-changing-password"/>
</intent-filter>
</activity>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public class MainActivity extends AppCompatActivity {
private View mDisableBiometric;
private View mAuthenticateBiometric;
private View mOpenSettings;
private View mChangePassword;
private View mFetchUserInfo;
private View mShowAuthTime;
private View mLogout;
Expand Down Expand Up @@ -87,6 +88,7 @@ protected void onCreate(Bundle savedInstanceState) {
mDisableBiometric = findViewById(R.id.disableBiometric);
mAuthenticateBiometric = findViewById(R.id.authenticateBiometric);
mOpenSettings = findViewById(R.id.openSettings);
mChangePassword = findViewById(R.id.changePassword);
mFetchUserInfo = findViewById(R.id.fetchUserInfo);
mShowAuthTime = findViewById(R.id.showAuthTime);
mLogout = findViewById(R.id.logout);
Expand Down Expand Up @@ -149,6 +151,7 @@ protected void onCreate(Bundle savedInstanceState) {
mDisableBiometric.setOnClickListener(view -> viewModel.disableBiometric());
mAuthenticateBiometric.setOnClickListener(view -> viewModel.authenticateBiometric(this));
mOpenSettings.setOnClickListener(view -> viewModel.openSettings());
mChangePassword.setOnClickListener(view -> viewModel.openChangePassword());
mFetchUserInfo.setOnClickListener(view -> viewModel.fetchUserInfo());
mShowAuthTime.setOnClickListener(view -> viewModel.showAuthTime(this));
mLogout.setOnClickListener(view -> viewModel.logout());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public class MainApplication extends Application {
public static final String AUTHGEAR_APP2APP_REDIRECT_URI = "https://authgear-demo-android.pandawork.com/app2app/redirect";
public static final String AUTHGEAR_WECHAT_REDIRECT_URI = "com.authgear.exampleapp.android://host/open_wechat_app";
public static final String AUTHGEAR_WECHAT_APP_ID = "wxa2f631873c63add1";
public static final String AUTHGEAR_SETTINGS_ACTION_CHANGE_PASSWORD_REDIRECT_URI = "com.authgear.exampleapp.android://host/after-changing-password";
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.oursky.authgear.OnFetchUserInfoListener;
import com.oursky.authgear.OnHandleApp2AppAuthenticationRequestListener;
import com.oursky.authgear.OnLogoutListener;
import com.oursky.authgear.OnOpenSettingsActionListener;
import com.oursky.authgear.OnOpenURLListener;
import com.oursky.authgear.OnPromoteAnonymousUserListener;
import com.oursky.authgear.OnReauthenticateListener;
Expand All @@ -45,6 +46,7 @@
import com.oursky.authgear.SessionState;
import com.oursky.authgear.SessionStateChangeReason;
import com.oursky.authgear.SettingOptions;
import com.oursky.authgear.SettingsActionOptions;
import com.oursky.authgear.TransientTokenStorage;
import com.oursky.authgear.UIVariant;
import com.oursky.authgear.UserInfo;
Expand Down Expand Up @@ -660,6 +662,42 @@ public void onFailed(Throwable throwable) {
});
}

public void openChangePassword() {
mIsLoading.setValue(true);

mAuthgear.refreshIDToken(new OnRefreshIDTokenListener() {
@Override
public void onFinished() {
SettingsActionOptions options = new SettingsActionOptions(
MainApplication.AUTHGEAR_SETTINGS_ACTION_CHANGE_PASSWORD_REDIRECT_URI
);
options.setColorScheme(getColorScheme());
options.setWechatRedirectURI(MainApplication.AUTHGEAR_WECHAT_REDIRECT_URI);
mAuthgear.changePassword(options, new OnOpenSettingsActionListener() {
@Override
public void onFinished() {
mIsLoading.setValue(false);
Log.d(TAG, "changePassword finished");
}

@Override
public void onFailed(Throwable throwable) {
Log.d(TAG, throwable.toString());
mIsLoading.setValue(false);
setError(throwable);
}
});
}

@Override
public void onFailed(Throwable throwable) {
Log.d(TAG, throwable.toString());
mIsLoading.setValue(false);
setError(throwable);
}
});
}

public void promoteAnonymousUser() {
mIsLoading.setValue(true);
PromoteOptions options = new PromoteOptions(MainApplication.AUTHGEAR_REDIRECT_URI);
Expand Down
9 changes: 9 additions & 0 deletions javasample/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,15 @@
android:text="Open settings"
tools:ignore="HardcodedText" />

<Button
android:id="@+id/changePassword"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_gravity="center_horizontal"
android:text="Change password"
tools:ignore="HardcodedText" />

<Button
android:id="@+id/fetchUserInfo"
android:layout_width="wrap_content"
Expand Down
34 changes: 34 additions & 0 deletions sdk/src/main/java/com/oursky/authgear/Authgear.kt
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,40 @@ constructor(
}
}

/**
* Open change password page in webview
*
* @param options Setting options.
* @param listener The listener.
* @param handler The handler of the thread on which the listener is called.
*/
@MainThread
@JvmOverloads
public fun changePassword(
options: SettingsActionOptions,
listener: OnOpenSettingsActionListener? = null,
handler: Handler = Handler(Looper.getMainLooper())
) {
scope.launch {
try {
core.settingsAction(
SettingsAction.CHANGE_PASSWORD,
options
)
handler.post {
listener?.onFinished()
}
} catch (e: Throwable) {
e.printStackTrace()
handler.post {
listener?.onFailed(e)
}
} finally {
AuthgearCore.unregisteredWechatRedirectURI()
}
}
}

/**
* Promote the current anonymous user. Note that this must not be called before there is an
* anonymous user.
Expand Down
80 changes: 80 additions & 0 deletions sdk/src/main/java/com/oursky/authgear/AuthgearCore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,47 @@ internal class AuthgearCore(
return finishAuthorization(deepLink, codeVerifier)
}

@ExperimentalAuthgearApi
@Suppress("RedundantSuspendModifier")
suspend fun createSettingsActionRequest(
action: SettingsAction,
options: SettingsActionOptions,
verifier: Verifier = generateCodeVerifier()
): AuthenticationRequest {
requireIsInitialized()

val refreshToken = tokenStorage.getRefreshToken(name)
?: throw UnauthenticatedUserException()

val token: String
try {
token = oauthRepo.oauthAppSessionToken(refreshToken).appSessionToken
} catch (e: Exception) {
handleInvalidGrantError(e)
throw e
}

val loginHint = "https://authgear.com/login_hint?type=app_session_token&app_session_token=${
URLEncoder.encode(token, StandardCharsets.UTF_8.name())
}"

val idTokenHint = this.idToken ?: throw AuthgearException("Call refreshIDToken first")

val request = options.toRequest(action = action, idTokenHint = idTokenHint, loginHint = loginHint)
val authorizeUri = authorizeEndpoint(request, verifier)
return AuthenticationRequest(authorizeUri, request.redirectUri, verifier)
}

@Suppress("BlockingMethodInNonBlockingContext")
@OptIn(ExperimentalAuthgearApi::class)
suspend fun settingsAction(action: SettingsAction, options: SettingsActionOptions) {
requireIsInitialized()
val codeVerifier = this.setupVerifier()
val request = createSettingsActionRequest(action, options, codeVerifier)
val deepLink = openAuthorizeUrl(request.redirectUri, request.url)
return finishSettingsAction(deepLink, codeVerifier)
}

@ExperimentalAuthgearApi
@Suppress("RedundantSuspendModifier")
suspend fun createReauthenticateRequest(
Expand Down Expand Up @@ -795,6 +836,45 @@ internal class AuthgearCore(
return userInfo
}

fun finishSettingsAction(deepLink: String, verifier: Verifier? = null) {
val uri = Uri.parse(deepLink)
val redirectUri = "${uri.scheme}://${uri.authority}${uri.path}"
val state = uri.getQueryParameter("state")
val error = uri.getQueryParameter("error")
val errorDescription = uri.getQueryParameter("error_description")
var errorURI = uri.getQueryParameter("error_uri")
if (error != null) {
if (error == "cancel") {
throw CancelException()
}
throw OAuthException(
error = error,
errorDescription = errorDescription,
state = state,
errorURI = errorURI
)
}
val code = uri.getQueryParameter("code")
?: throw OAuthException(
error = "invalid_request",
errorDescription = "Missing parameter: code",
state = state,
errorURI = errorURI
)
val codeVerifier = verifier?.verifier ?: storage.getOidcCodeVerifier(name)
val tokenResponse = oauthRepo.oidcTokenRequest(
OidcTokenRequest(
grantType = GrantType.SETTINGS_ACTION,
clientId = clientId,
xDeviceInfo = getDeviceInfo(this.application).toBase64URLEncodedString(),
code = code,
redirectUri = redirectUri,
codeVerifier = codeVerifier ?: "",
)
)
disableBiometric()
}

fun finishReauthentication(deepLink: String, verifier: Verifier? = null): UserInfo {
val uri = Uri.parse(deepLink)
val redirectUri = "${uri.scheme}://${uri.authority}${uri.path}"
Expand Down
3 changes: 2 additions & 1 deletion sdk/src/main/java/com/oursky/authgear/GrantType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ enum class GrantType(val raw: String) {
ANONYMOUS("urn:authgear:params:oauth:grant-type:anonymous-request"),
BIOMETRIC("urn:authgear:params:oauth:grant-type:biometric-request"),
ID_TOKEN("urn:authgear:params:oauth:grant-type:id-token"),
APP2APP("urn:authgear:params:oauth:grant-type:app2app-request")
APP2APP("urn:authgear:params:oauth:grant-type:app2app-request"),
SETTINGS_ACTION("urn:authgear:params:oauth:grant-type:settings-action")
}

@Serializer(forClass = GrantType::class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.oursky.authgear

interface OnOpenSettingsActionListener {
fun onFinished()
fun onFailed(throwable: Throwable)
}
5 changes: 5 additions & 0 deletions sdk/src/main/java/com/oursky/authgear/SettingsAction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.oursky.authgear

enum class SettingsAction(val raw: String) {
CHANGE_PASSWORD("change_password")
}
34 changes: 34 additions & 0 deletions sdk/src/main/java/com/oursky/authgear/SettingsActionOptions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.oursky.authgear

import com.oursky.authgear.oauth.OidcAuthenticationRequest

data class SettingsActionOptions @JvmOverloads constructor(
var redirectURI: String,
/**
* WeChat redirect uri is needed when integrating WeChat login
* The wechatRedirectURI will be called when user click the login with WeChat button
*/
var wechatRedirectURI: String? = null,
/**
* Theme override
*/
var colorScheme: ColorScheme? = null,
var uiLocales: List<String>? = null,
)

internal fun SettingsActionOptions.toRequest(action: SettingsAction, idTokenHint: String, loginHint: String): OidcAuthenticationRequest {
return OidcAuthenticationRequest(
redirectUri = this.redirectURI,
responseType = "settings_action",
scope = listOf("openid", "https://authgear.com/scopes/full-access"),
isSsoEnabled = false,
prompt = null,
loginHint = loginHint,
idTokenHint = idTokenHint,
uiLocales = this.uiLocales,
colorScheme = this.colorScheme,
wechatRedirectURI = this.wechatRedirectURI,
page = null,
settingsAction = action.raw,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ internal data class OidcAuthenticationRequest constructor(
var colorScheme: ColorScheme? = null,
var idTokenHint: String? = null,
var wechatRedirectURI: String? = null,
var page: String? = null
var page: String? = null,
var settingsAction: String? = null
)

internal fun OidcAuthenticationRequest.toQuery(clientID: String, codeVerifier: AuthgearCore.Verifier?): Map<String, String> {
Expand Down Expand Up @@ -76,6 +77,10 @@ internal fun OidcAuthenticationRequest.toQuery(clientID: String, codeVerifier: A
query["x_page"] = it
}

this.settingsAction?.let {
query["x_settings_action"] = it
}

if (!this.isSsoEnabled) {
// For backward compatibility
// If the developer updates the SDK but not the server
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.oursky.authgear.oauth

import com.oursky.authgear.GrantType
import com.oursky.authgear.SettingsAction
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

Expand Down Expand Up @@ -28,5 +29,7 @@ internal data class OidcTokenRequest(
@SerialName("code_challenge")
val codeChallenge: String? = null,
@SerialName("code_challenge_method")
val codeChallengeMethod: String? = null
val codeChallengeMethod: String? = null,
@SerialName("x_settings_action")
val settingsAction: SettingsAction? = null
)

0 comments on commit c20c972

Please sign in to comment.