Skip to content

Commit

Permalink
Merge pull request #278 from mohssenfathi/pac
Browse files Browse the repository at this point in the history
Implementing PAR
  • Loading branch information
mohssenfathi authored Apr 18, 2023
2 parents 6660233 + e4b9d4d commit c8a8058
Show file tree
Hide file tree
Showing 25 changed files with 559 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ class AuthenticationURLUtility {
static let scopesKey = "scope"
static let sdkKey = "sdk"
static let sdkVersionKey = "sdk_version"
static let requestUriKey = "request_uri"

static let sdkValue = "ios"

static func buildQueryParameters(_ scopes: [UberScope]) -> [URLQueryItem] {
static func buildQueryParameters(scopes: [UberScope], requestUri: String?) -> [URLQueryItem] {
var queryItems = [URLQueryItem]()

queryItems.append(URLQueryItem(name: appNameKey, value: Configuration.shared.appDisplayName))
Expand All @@ -44,6 +45,9 @@ class AuthenticationURLUtility {
queryItems.append(URLQueryItem(name: scopesKey, value: scopes.toUberScopeString()))
queryItems.append(URLQueryItem(name: sdkKey, value: sdkValue))
queryItems.append(URLQueryItem(name: sdkVersionKey, value: Configuration.shared.sdkVersion))
if let requestUri {
queryItems.append(URLQueryItem(name: requestUriKey, value: requestUri))
}

return queryItems
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class AuthenticationProvider {

let productFlowPriority: [UberAuthenticationProductFlow]
let scopes: [UberScope]
let requestUri: String?

/// Returns an AuthenticationProvider.
///
Expand All @@ -38,8 +39,9 @@ class AuthenticationProvider {
/// - productFlowPriority: The product flows against which to authenticate, in the order of which Uber products you'd like to use to authenticate the user.
///
/// For example, you may want to SSO with the UberEats app, but if the app does not exist on the user's device, then try to authenticate with the Uber Rides app instead. In this example you'd call this parameter with [ eats, rides ].
init(scopes: [UberScope], productFlowPriority: [UberAuthenticationProductFlow]) {
init(scopes: [UberScope], productFlowPriority: [UberAuthenticationProductFlow], requestUri: String?) {
self.scopes = scopes
self.requestUri = requestUri
self.productFlowPriority = productFlowPriority
}

Expand All @@ -54,16 +56,16 @@ class AuthenticationProvider {
switch loginType {
case .authorizationCode:
// Rides and Eats temporarily share the same authorization code flow
return AuthorizationCodeGrantAuthenticator(scopes: scopes)
return AuthorizationCodeGrantAuthenticator(scopes: scopes, requestUri: requestUri)
case .implicit:
// Rides and Eats temporarily share the same implicit grant code flow
return ImplicitGrantAuthenticator(scopes: scopes)
return ImplicitGrantAuthenticator(scopes: scopes, requestUri: requestUri)
case .native:
switch authProduct.uberProductType {
case .rides:
return RidesNativeAuthenticator(scopes: scopes)
return RidesNativeAuthenticator(scopes: scopes, requestUri: requestUri)
case .eats:
return EatsNativeAuthenticator(scopes: scopes)
return EatsNativeAuthenticator(scopes: scopes, requestUri: requestUri)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ import UIKit
@objc public var state: String?

@objc override var authorizationURL: URL {
return OAuth.authorizationCodeLogin(clientID: Configuration.shared.clientID, redirect: Configuration.shared.getCallbackURI(for: .authorizationCode), scopes: scopes, state: state).url
return OAuth.authorizationCodeLogin(
clientID: Configuration.shared.clientID,
redirect: Configuration.shared.getCallbackURI(for: .authorizationCode),
scopes: scopes,
state: state,
requestUri: requestUri
).url
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ import UIKit
/// Scopes to request during login
@objc public var scopes: [UberScope]

@objc public init(scopes: [UberScope]) {
@objc public var requestUri: String?

@objc public init(scopes: [UberScope],
requestUri: String? = nil) {
self.scopes = scopes
self.requestUri = requestUri
super.init()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ import Foundation

- returns: An initialized AuthenticationDeeplink
*/
@objc public init(scopes: [UberScope]) {
let queryItems = AuthenticationURLUtility.buildQueryParameters(scopes)
@objc public init(scopes: [UberScope], requestUri: String?) {
let queryItems = AuthenticationURLUtility.buildQueryParameters(scopes: scopes, requestUri: requestUri)
let scheme = "eatsauth"
let domain = "connect"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ import Foundation

- returns: true if a redirect was handled, false otherwise.
*/
@objc public override init(scopes: [UberScope]) {
deeplink = EatsAuthenticationDeeplink(scopes: scopes)
super.init(scopes: scopes)
@objc public override init(scopes: [UberScope], requestUri: String?) {
deeplink = EatsAuthenticationDeeplink(scopes: scopes, requestUri: requestUri)
super.init(scopes: scopes, requestUri: requestUri)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
*/
@objc(UBSDKImplicitGrantAuthenticator) public class ImplicitGrantAuthenticator: BaseAuthenticator {
@objc override var authorizationURL: URL {
return OAuth.implicitLogin(clientID: Configuration.shared.clientID, scopes: self.scopes, redirect: Configuration.shared.getCallbackURI(for: .implicit)).url
return OAuth.implicitLogin(
clientID: Configuration.shared.clientID,
scopes: scopes,
redirect: Configuration.shared.getCallbackURI(for: .implicit),
requestUri: requestUri
).url
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ import Foundation

- returns: An initialized AuthenticationDeeplink
*/
@objc public init(scopes: [UberScope]) {
let queryItems = AuthenticationURLUtility.buildQueryParameters(scopes)
@objc public init(scopes: [UberScope], requestUri: String? = nil) {
let queryItems = AuthenticationURLUtility.buildQueryParameters(scopes: scopes, requestUri: requestUri)
let scheme = "uberauth"
let domain = "connect"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ import Foundation

- returns: true if a redirect was handled, false otherwise.
*/
@objc public override init(scopes: [UberScope]) {
deeplink = RidesAuthenticationDeeplink(scopes: scopes)
super.init(scopes: scopes)
@objc public override init(scopes: [UberScope], requestUri: String?) {
deeplink = RidesAuthenticationDeeplink(scopes: scopes, requestUri: requestUri)
super.init(scopes: scopes, requestUri: requestUri)
}
}
19 changes: 17 additions & 2 deletions source/UberCore/Authentication/LoginButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import UIKit
@objc func loginButton(_ button: LoginButton, didLogoutWithSuccess success: Bool)

/**
THe Login Button completed a login
The Login Button completed a login

- parameter button: The LoginButton involved
- parameter accessToken: The access token that
Expand All @@ -52,6 +52,14 @@ import UIKit
@objc func loginButton(_ button: LoginButton, didCompleteLoginWithToken accessToken: AccessToken?, error: NSError?)
}

/**
* Protocol to provide content for login
*/
@objc(UBSDKLoginButtonDataSource) public protocol LoginButtonDataSource {

@objc func prefillValues(_ button: LoginButton) -> Prefill?
}

/// Button to handle logging in to Uber
@objc(UBSDKLoginButton) public class LoginButton: UberButton {

Expand All @@ -62,6 +70,8 @@ import UIKit
/// The LoginButtonDelegate for this button
@objc public weak var delegate: LoginButtonDelegate?

@objc public weak var dataSource: LoginButtonDataSource?

/// The LoginManager to use for log in
@objc public var loginManager: LoginManager {
didSet {
Expand Down Expand Up @@ -206,7 +216,12 @@ import UIKit
delegate?.loginButton(self, didLogoutWithSuccess: success)
refreshContent()
case .signedOut:
loginManager.login(requestedScopes: scopes, presentingViewController: presentingViewController, completion: loginCompletion)
loginManager.login(
requestedScopes: scopes,
presentingViewController: presentingViewController,
prefillValues: dataSource?.prefillValues(self),
completion: loginCompletion
)
}
}

Expand Down
73 changes: 69 additions & 4 deletions source/UberCore/Authentication/LoginManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import SafariServices
var loggingIn: Bool = false
var willEnterForegroundCalled: Bool = false
private var postCompletionHandler: AuthenticationCompletionHandler?
private let urlSession = URLSession(configuration: .default)

/**
Create instance of login manager to authenticate user and retreive access token.
Expand Down Expand Up @@ -187,17 +188,44 @@ import SafariServices

- parameter scopes: scopes being requested.
- parameter presentingViewController: The presenting view controller present the login view controller over.
- parameter prefillValues: Optional values to pre-populate the signin form with.
- parameter completion: The LoginManagerRequestTokenHandler completion handler for login success/failure.
*/
@objc public func login(requestedScopes scopes: [UberScope], presentingViewController: UIViewController? = nil, completion: AuthenticationCompletionHandler? = nil) {
@objc public func login(requestedScopes scopes: [UberScope], presentingViewController: UIViewController? = nil, prefillValues: Prefill? = nil, completion: AuthenticationCompletionHandler? = nil) {
self.postCompletionHandler = completion
UberAppDelegate.shared.loginManager = self

let authProvider = AuthenticationProvider(scopes: scopes, productFlowPriority: productFlowPriority)

loggingIn = true
willEnterForegroundCalled = false
executeLogin(presentingViewController: presentingViewController, authenticationProvider: authProvider)

let executeLogin: (String?) -> Void = { [weak self] requestUri in
guard let self = self else {
return
}
let authProvider = AuthenticationProvider(scopes: scopes, productFlowPriority: self.productFlowPriority, requestUri: requestUri)
self.executeLogin(presentingViewController: presentingViewController, authenticationProvider: authProvider)
}

let responseType: OAuth.ResponseType? = {
switch loginType {
case .authorizationCode:
return .code
case .implicit:
return .token
case .native:
return nil
}
}()

if let prefillValues = prefillValues,
let responseType = responseType {
executeParRequest(prefillValues: prefillValues,
responseType: responseType) { requestUri in
executeLogin(requestUri)
}
} else {
executeLogin(nil)
}
}

/**
Expand Down Expand Up @@ -279,6 +307,42 @@ import SafariServices

// Mark: Private Interface

private func executeParRequest(prefillValues: Prefill,
responseType: OAuth.ResponseType,
_ completion: @escaping (String?) -> Void) {

let loginHint = prefillValues.dictValue
guard !loginHint.isEmpty else {
completion(nil)
return
}

let request = Request(
session: urlSession,
endpoint: OAuth.par(
clientID: Configuration.shared.clientID,
loginHint: loginHint,
responseType: responseType
)
)

request?.prepare()
request?.execute { response in
let requestUri: String? = {
guard let data = response.data,
response.error == nil,
let par = try? JSONDecoder.uberDecoder.decode(Par.self, from: data) else {
return nil
}
return par.requestUri
}()

DispatchQueue.main.async {
completion(requestUri)
}
}
}

private func executeLogin(presentingViewController: UIViewController?, authenticationProvider: AuthenticationProvider) {
if let authenticator = authenticationProvider.authenticators(for: loginType).first, authenticator.authorizationURL.scheme == "https" {
executeWebLogin(presentingViewController: presentingViewController, authenticator: authenticator)
Expand Down Expand Up @@ -413,4 +477,5 @@ import SafariServices

postCompletionHandler?(accessToken, error)
}

}
3 changes: 2 additions & 1 deletion source/UberCore/Authentication/LoginManagingProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@

- parameter scopes: scopes being requested.
- parameter presentingViewController: The presenting view controller present the login view controller over.
- parameter prefillValues: Optional values to pre-populate the signin form with.
- parameter completion: The LoginManagerRequestTokenHandler completion handler for login success/failure.
*/
@objc func login(requestedScopes scopes: [UberScope], presentingViewController: UIViewController?, completion: ((_ accessToken: AccessToken?, _ error: NSError?) -> Void)?)
@objc func login(requestedScopes scopes: [UberScope], presentingViewController: UIViewController?, prefillValues: Prefill?, completion: ((_ accessToken: AccessToken?, _ error: NSError?) -> Void)?)

/**
Called via the RidesAppDelegate when the application is opened via a URL. Responsible
Expand Down
Loading

0 comments on commit c8a8058

Please sign in to comment.