Skip to content

Commit

Permalink
Use x_settings_action for change password #161
Browse files Browse the repository at this point in the history
  • Loading branch information
IniZio committed Feb 28, 2024
1 parent 865bd97 commit 5a1bef7
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 23 deletions.
12 changes: 12 additions & 0 deletions Sources/APIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ enum GrantType: String {
case biometric = "urn:authgear:params:oauth:grant-type:biometric-request"
case idToken = "urn:authgear:params:oauth:grant-type:id-token"
case app2app = "urn:authgear:params:oauth:grant-type:app2app-request"
case settingsAction = "urn:authgear:params:oauth:grant-type:settings-action"
}

enum ResponseType: String {
case code
case settingsAction = "urn:authgear:params:oauth:response-type:settings-action"
case none
}

struct APIResponse<T: Decodable>: Decodable {
Expand Down Expand Up @@ -36,6 +43,7 @@ struct OIDCAuthenticationRequest {
let maxAge: Int?
let wechatRedirectURI: String?
let page: AuthenticationPage?
let settingsAction: SettingsAction?

func toQueryItems(clientID: String, verifier: CodeVerifier?) -> [URLQueryItem] {
var queryItems = [
Expand Down Expand Up @@ -108,6 +116,10 @@ struct OIDCAuthenticationRequest {
queryItems.append(URLQueryItem(name: "x_page", value: page.rawValue))
}

if let settingsAction = self.settingsAction {
queryItems.append(URLQueryItem(name: "x_settings_action", value: settingsAction.rawValue))
}

if self.isSSOEnabled == false {
// For backward compatibility
// If the developer updates the SDK but not the server
Expand Down
183 changes: 164 additions & 19 deletions Sources/Authgear.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ struct AuthenticateOptions {
idTokenHint: nil,
maxAge: nil,
wechatRedirectURI: self.wechatRedirectURI,
page: self.page
page: self.page,
settingsAction: nil
)
}
}
Expand Down Expand Up @@ -79,7 +80,8 @@ struct ReauthenticateOptions {
idTokenHint: idTokenHint,
maxAge: self.maxAge ?? 0,
wechatRedirectURI: self.wechatRedirectURI,
page: nil
page: nil,
settingsAction: nil
)
}
}
Expand Down Expand Up @@ -203,10 +205,13 @@ public enum AuthenticationPage: String {

public enum SettingsPage: String {
case settings = "/settings"
case changePassword = "/settings/change_password"
case identity = "/settings/identities"
}

public enum SettingsAction: String {
case changePassword = "change_password"
}

public enum SessionStateChangeReason: String {
case noToken = "NO_TOKEN"
case foundToken = "FOUND_TOKEN"
Expand Down Expand Up @@ -243,7 +248,7 @@ public class Authgear {
private static let ExpireInPercentage = 0.9

static let CodeChallengeMethod = "S256"
static let SDKRedirectURI = "authgearsdk://host/path"
static let SDKRedirectURI = "nocallback://host/path"

let name: String
let clientId: String
Expand Down Expand Up @@ -976,6 +981,10 @@ public class Authgear {
redirectURI: String,
uiLocales: [String]? = nil,
colorScheme: ColorScheme? = nil,
settingsAction: SettingsAction? = nil,
idTokenHint: String? = nil,
responseType: ResponseType = ResponseType.none,
verifier: CodeVerifier? = nil,
wechatRedirectURI: String? = nil,
handler: URLCompletionHandler?
) {
Expand All @@ -1001,7 +1010,7 @@ public class Authgear {

let endpoint = try self.buildAuthorizationURL(request: OIDCAuthenticationRequest(
redirectURI: redirectURI,
responseType: "none",
responseType: responseType.rawValue,
scope: ["openid", "offline_access", "https://authgear.com/scopes/full-access"],
isSSOEnabled: self.isSSOEnabled,
state: nil,
Expand All @@ -1010,11 +1019,12 @@ public class Authgear {
loginHint: loginHint,
uiLocales: uiLocales,
colorScheme: colorScheme,
idTokenHint: nil,
idTokenHint: idTokenHint,
maxAge: nil,
wechatRedirectURI: wechatRedirectURI,
page: nil
), verifier: nil)
page: nil,
settingsAction: settingsAction
), verifier: verifier)

handler?(.success(endpoint))
} catch {
Expand All @@ -1027,7 +1037,6 @@ public class Authgear {
path: String,
uiLocales: [String]? = nil,
colorScheme: ColorScheme? = nil,
closeOnSuccess: Bool? = false,
handler: URLCompletionHandler?
) {
let handler = handler.map { h in self.withMainQueueHandler(h) }
Expand All @@ -1048,9 +1057,6 @@ public class Authgear {
if let colorScheme = colorScheme {
queryItems.append(URLQueryItem(name: "x_color_scheme", value: colorScheme.rawValue))
}
if closeOnSuccess == true {
queryItems.append(URLQueryItem(name: "redirect_uri", value: Authgear.SDKRedirectURI))
}
urlComponents.queryItems = queryItems
let redirectURI = urlComponents.url!
self.generateURL(redirectURI: redirectURI.absoluteString) { generatedResult in
Expand All @@ -1071,16 +1077,14 @@ public class Authgear {
uiLocales: [String]? = nil,
colorScheme: ColorScheme? = nil,
wechatRedirectURI: String? = nil,
handler: VoidCompletionHandler? = nil,
closeOnSuccess: Bool? = false
handler: VoidCompletionHandler? = nil
) {
let handler = handler.map { h in withMainQueueHandler(h) }

self.generateAuthgearURL(
path: path,
uiLocales: uiLocales,
colorScheme: colorScheme,
closeOnSuccess: closeOnSuccess
colorScheme: colorScheme
) { [weak self] result in
guard let self = self else { return }

Expand Down Expand Up @@ -1126,15 +1130,156 @@ public class Authgear {
page: SettingsPage,
uiLocales: [String]? = nil,
colorScheme: ColorScheme? = nil,
wechatRedirectURI: String? = nil,
closeOnSuccess: Bool? = false
wechatRedirectURI: String? = nil
) {
openURL(
path: page.rawValue,
uiLocales: uiLocales,
colorScheme: colorScheme,
wechatRedirectURI: wechatRedirectURI
)
}

private func openSettingsAction(
action: SettingsAction,
uiLocales: [String]? = nil,
colorScheme: ColorScheme? = nil,
wechatRedirectURI: String? = nil,
redirectURI: String,
handler: VoidCompletionHandler? = nil
) {
let verifier = CodeVerifier()
let this = self

self.refreshIDToken(handler: { result in
switch result {
case .success:
self.generateURL(
redirectURI: redirectURI,
uiLocales: uiLocales,
colorScheme: colorScheme,
settingsAction: action,
idTokenHint: self.idTokenHint,
responseType: ResponseType.settingsAction,
verifier: verifier
) { generatedResult in
switch generatedResult {
case let .failure(err):
handler?(.failure(err))
case let .success(url):
// For opening setting page, sdk will not know when user end
// the setting page.
// So we cannot unregister the wechat uri in this case
// It is fine to not unresgister it, as everytime we open a
// new authorize section (authorize or setting page)
// registerCurrentWeChatRedirectURI will be called and overwrite
// previous registered wechatRedirectURI
self.registerCurrentWechatRedirectURI(uri: wechatRedirectURI)

self.uiImplementation.openAuthorizationURL(
url: url,
redirectURI: URL(string: Authgear.SDKRedirectURI)!,
// prefersEphemeralWebBrowserSession is true so that
// the alert dialog is never prompted and
// the app session token cookie is forgotten when the webview is closed.
shareCookiesWithDeviceBrowser: true
) { result in
self.unregisterCurrentWechatRedirectURI()
switch result {
case let .success(url):
this.finishSettingsAction(url: url, redirectURI: redirectURI, verifier: verifier) { result in
switch (result) {
case .success:
handler?(result)
case let .failure(error):
handler?(.failure(wrapError(error: error)))
}
}
case let .failure(error):
handler?(.failure(wrapError(error: error)))
}
}
}
}
case let .failure(error):
handler?(.failure(wrapError(error: error)))
}
})
}

func finishSettingsAction(
url: URL,
redirectURI: String,
verifier: CodeVerifier,
handler: VoidCompletionHandler
) {
let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)!
let params = urlComponents.queryParams

if let errorParams = params["error"] {
if errorParams == "cancel" {
return handler(
.failure(AuthgearError.cancel)
)
}
return handler(
.failure(AuthgearError.oauthError(
OAuthError(
error: errorParams,
errorDescription: params["error_description"],
errorUri: params["error_uri"]
)
))
)
}

guard let code = params["code"] else {
return handler(
.failure(AuthgearError.oauthError(
OAuthError(
error: "invalid_request",
errorDescription: "Missing parameter: code",
errorUri: nil
)
))
)
}

do {
_ = try apiClient.syncRequestOIDCToken(
grantType: GrantType.settingsAction,
clientId: clientId,
deviceInfo: getDeviceInfo(),
redirectURI: redirectURI,
code: code,
codeVerifier: verifier.value,
codeChallenge: nil,
codeChallengeMethod: nil,
refreshToken: nil,
jwt: nil,
accessToken: nil,
xApp2AppDeviceKeyJwt: nil
)
handler(.success(()))
} catch {
return handler(.failure(wrapError(error: error)))
}
}

public func changePassword(
uiLocales: [String]? = nil,
colorScheme: ColorScheme? = nil,
wechatRedirectURI: String? = nil,
redirectURI: String,
handler: VoidCompletionHandler? = nil
) {
openSettingsAction(
action: .changePassword,
uiLocales: uiLocales,
colorScheme: colorScheme,
wechatRedirectURI: wechatRedirectURI,
closeOnSuccess: closeOnSuccess
redirectURI: redirectURI,
handler: handler
)
}

Expand Down
16 changes: 12 additions & 4 deletions example/ios_example/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import SwiftUI

class App: ObservableObject {
static let redirectURI = "com.authgear.example://host/path"
static let changePasswordRedirectURI = "com.authgear.example://host/after-changing-password"

static let app2appRedirectURI = "https://authgear-demo.pandawork.com/app2app/redirect"
static let app2appAuthorizeEndpoint = "https://authgear-demo.pandawork.com/app2app/authorize"
static let wechatUniversalLink = "https://authgear-demo.pandawork.com/wechat/"
Expand Down Expand Up @@ -234,12 +236,18 @@ class App: ObservableObject {
}

func changePassword() {
container?.open(
page: .changePassword,
container?.changePassword(
colorScheme: self.colorScheme,
wechatRedirectURI: App.wechatRedirectURI,
closeOnSuccess: true
)
redirectURI: App.changePasswordRedirectURI
) { result in
switch result {
case .success:
self.successAlertMessage = "Changed password successfully"
case let .failure(error):
self.setError(error)
}
}
}

func promoteAnonymousUser() {
Expand Down

0 comments on commit 5a1bef7

Please sign in to comment.