I've got and tried a number of samples using ADALiOS. ADALiOS keeps changing its implementation as you know. Because I am a beginner of swift, I don't have any idea how to make samples from Internet work.
I tried a version of adal 3.0 (pre release).
as you can see, the below code was copied from one of samples downloaded. Every sample has got a complier errors such as 'missing argument label-policy...' or 'cannot convert ADAuthenticationResult...' in authContext.acquireToken() or acquireTokenwithResource() method regardless of its adal version. anyone can help me?
the error message is about completionBlock:....
thanks
authContext.acquireToken(withScopes: [Any](), additionalScopes: [Any](), clientId: clientId, redirectUri: redirectURL, identifier: id!, promptBehavior: prompt, extraQueryParameters: "", completionBlock: <#T##ADAuthenticationCallback!##ADAuthenticationCallback!##(ADAuthenticationResult?) -> Void#>){
if result.status.value != AD_SUCCEEDED.value {
// Failed, return error description
completionHandler(false, result.error.description)
}
else {
// Succeeded, return the acess token
var token = result.accessToken
// Initialize the dependency resolver with the logged on context.
// The dependency resolver is passed to the Outlook library.
self.dependencyResolver = ADALDependencyResolver(context: authContext, resourceId: self.outlookResource, clientId: self.clientId, redirectUri: self.redirectURL)
completionHandler(true, token)
}
}
I just got this working using adal 3.0 (pre release). Looks like ADAL API has changed when looking at the examples. I couldn't find acquireTokenwithResource() so I used:
acquireToken(withScopes: additionalScopes: clientId: redirectUri: identifier:, promptBehavior:)
I think the biggest issue I had was not registering the native app here: https://apps.dev.microsoft.com
After I did that and used clientId and redirectURI from apps.dev.microsoft, and also include the correct scope, I was able to authenticate and get back an accessToken.
My acquireToken Looks like this:
func acquireAuthToken(completion: ((AuthenticationResult) -> Void)?) {
let identity = ADUserIdentifier(id: "Default email address", type: ADUserIdentifierType(rawValue:1))
self.context.acquireToken(withScopes: ["User.Read"], additionalScopes: nil, clientId: AppData.sharedInstance?.clientId, redirectUri: URL(string: (AppData.sharedInstance?.redirectUriString)!), identifier: identity, promptBehavior: AD_PROMPT_AUTO) {
(result) in
if let handler = completion {
if result!.status == AD_SUCCEEDED {
self.accessToken = result!.token
handler(AuthenticationResult.Success)
}
else {
handler(AuthenticationResult.Failure(result!.error))
}
}
}
}
The context is just ADAuthenticationContext(authority: ppData.sharedInstance?.authority, .Authority, error: &error)
HTH
Related
I'm using a service that provides an OAuth2.0 authentication. This are the steps i need:
Open a URL with user Id as params
User approves my app (which is correctyle registered).
The user is redirected to a RedirectUri, with access token in the hash.
The third point is my main problem.
I've implemented the OAuth with microsoft libraries and everything works fine. But I cant use them here so I'm trying https://github.com/OAuthSwift/OAuthSwift this one.
This is my code:
private func authenticationService() {
// create an instance and retain it
let oauthswift = OAuth2Swift(
consumerKey: "xx",
consumerSecret: "xxx",
authorizeUrl: "//myurl + userId",
responseType: "token"
)
oauthswift.authorizeURLHandler = OAuthSwiftOpenURLExternally.sharedInstance
let handle = oauthswift.authorize(
withCallbackURL: "???",
scope: "", state:"") { result in
switch result {
case .success(let (credential, response, parameters)):
print(credential.oauthToken)
// Do your request
case .failure(let error):
print(error.localizedDescription)
}
}
}
This open correctly my Safari but then I'm redirected to the URI with access token in the hash and nothing happened.
The main problem here is that I've a redirect uri so I guess the callback URL is not called? And this is not opening a sheet but it is redirecting to Safari. And I dont like this approach.
How can I perform OAuth2.0 in swift with the steps above? How can I get the access token from an url? What is the best library and how can I get the most of it?
Update:
This is my code for stackExchange:
let request: OAuth2Request = .init(authUrl: "https://stackexchange.com/oauth/dialog?client_id=<MYCLIENTID>&scope=private_info&redirect_uri=https://stackexchange.com/oauth/login_success",
tokenUrl: "https://stackoverflow.com/oauth/access_token/json",
clientId: "<MYCLIENTID>",
redirectUri: "https://stackexchange.com/oauth/login_success",
clientSecret: "",
scopes: [])
The OAuth domain in stack apps is => stackexchange.com
So i've added in my URL Types the following: redirect-uri://<stackexchange.com> (even without <>)
But everytimes I approve my app i'm stacked in the "Authorizing application" which contains my token and i'm not redirected.
If you are targeting iOS 13 you can use the new AuthenticationServices library provided by Apple.
It will work on both macOS and iOS.
Maybe this would help other developers, I create a simple and small swift package to handle OAuth2 in Swift, you can check the demo project it works very well 👍
https://github.com/hadiidbouk/SimpleOAuth2
Edit:
You are passing the wrong URLs, they should be like this
let request: OAuth2Request = .init(authUrl: "https://stackoverflow.com/oauth",
tokenUrl: "https://stackoverflow.com/oauth/access_token/json",
clientId: "<<your client id>>",
redirectUri: "redirect-uri://stackexchange.com",
clientSecret: "<<your client secret>>",
scopes: ["private_info"])
In the below code:
private func authenticationService() {
// create an instance and retain it
let oauthswift = OAuth2Swift(...)
you don't retain oauthswift (nor handle). They will be deallocated as soon as you finish executing the authenticationService function.
You need to store references to oauthswift and handle outside the function (at the class level).
let oauthswift: OAuth2Swift
let handle: ...
init() {
oauthswift = OAuth2Swift(
consumerKey: "xx",
consumerSecret: "xxx",
authorizeUrl: "//myurl + userId",
responseType: "token"
)
...
}
private func authenticationService() {
handle = oauthswift.authorize(...)
}
My app needs to get a basecamp3 login. Hence I used the OAuth2Swift library. But unfortunately, I am unable to receive the token from basecamp even the user has authorized the app.
Below is the screenshot
I have used the following code
func createAuthRequest(){
// create an instance and retain it
let oauthswift = OAuth2Swift(
consumerKey: clientID,
consumerSecret: clientSecret,
authorizeUrl: authURL,
responseType: "token"
)
//oauthswift.authorizeURLHandler = self
oauthswift.authorizeURLHandler = SafariURLHandler(viewController: self, oauthSwift: oauthswift)
let handle = oauthswift.authorize(
withCallbackURL: URL(string: redirectURL)!,
scope: "profile", state:"") { result in //This block of code never executed
switch result {
case .success(let (credential, response, parameters)):
print(credential.oauthToken)
// Do your request
case .failure(let error):
print(error.localizedDescription)
}
}
}
The code inside withCallbackURL never executed even the user has authorized the app.
Any help regarding this is appreciated.
I found the solution the problem was I was using wrong authentication & token URL.
Following URL need to be used. I missed to add web_server in auth/token url and unfortunately Basecamp haven't mentioned the same in their documets.
let authURL = "https://launchpad.37signals.com/authorization/new?type=web_server"
let tokenURL = "https://launchpad.37signals.com/authorization/token?type=web_server"
and redirectURL = com.abc.abc:/oauth2Callback (The same redircturl need to be updated for app under basecamp developer console where com.abc.abc is bundle id of the app)
I am facing problem with "https://api.linkedin.com/v2/me" api of LinkedIn. Code was working good when I was using v1 api. I have updated my code for version 2 api for linkedIn authentication and When I tried to get profile with api "https://api.linkedin.com/v2/me" I am getting error Request failed: forbidden (403). I don't know how to resolve it.
Here is my code:
let linkedinHelper = LinkedinSwiftHelper(configuration: LinkedinSwiftConfiguration(clientId: Constant.Key.kLinkedInClientId, clientSecret: Constant.Key.kLinkedInClientSecret, state: Constant.Key.kLinkedInState, permissions: ["r_basicprofile", "r_emailaddress"], redirectUrl: Constant.Key.kLinkedInRedirectURL),nativeAppChecker: WebLoginOnly())
linkedinHelper.authorizeSuccess({ (token) in
print(token)
let url = "https://api.linkedin.com/v2/me"
linkedinHelper.requestURL(url, requestType: LinkedinSwiftRequestGet, success: { (response) -> Void in
print(response)
}) {(error) -> Void in
print(error.localizedDescription)
//handle the error
}
I've set URL scheme in info.plist as well.
You have to pass key in oauth2_access_token
Example:
https://api.linkedin.com/v2/me?oauth2_access_token={linkedin_key}
Edit:-
Also in permission need to set "r_liteprofile" instead of "r_basicprofile". To change permission worked for me.
I am developing an iOS app using django rest framework for apis. But currently I am not be able to getting ahead when calling apis with authentication credentials.
I succeeded in calling the api using Postman and curl by setting Header as Authentication Bearer <token>.. but I continuously failed at calling it from iOS app. I am using Moya for calling api. And I don't know what I should do next.
What I tried: (when calling Moya)
let token = "abcde12345sometoken"
let plugin = AccessTokenPlugin(tokenClosure: token)
let provider = MoyaProvider<AccountAPI>(plugins : [plugin])
provider.request(.getAccountProfile(oauth_id: oauth_id, provider: "facebook")) { (result) in
// doing something with result
}
and configured API as:
extension AccountAPI : TargetType, AccessTokenAuthorizable {
// codes conforming variables to TargetType protocol
public var authorizationType: AuthorizationType {
switch self {
case .getFacebookAccountToken:
return .none
default:
return .bearer
}
}
public var headers: [String: String]? {
switch self {
case .getFacebookAccountToken, .getEmailAccountToken: // post requests
return ["Content-type":"application/x-www-form-urlencoded"]
default:
return ["Content-type":"application/json"]
}
}
}
Is there anything I should consider when using Moya for authentication or maybe with Info.plist and so on?
Or the document says this approach is for JWT token, and maybe my method is not for JWT and something else..? Give me some advice!
For my case, I use
Moya 12.0.1
MultiTarget
example:
plugins = [AccessTokenPlugin(tokenClosure: {
let token = ...
return token
})]
MoyaProvider<MultiTarget>(
plugins: plugins
)
.request(MultiTarget(myAPI)) {
...
}
But it never calls tokenClosure
Solution
you need to add this extension
extension MultiTarget: AccessTokenAuthorizable {
public var authorizationType: AuthorizationType {
guard let target = target as? AccessTokenAuthorizable else { return .none }
return target.authorizationType
}
}
source: https://github.com/Moya/Moya/blob/master/Sources/Moya/Plugins/AccessTokenPlugin.swift#L62
After a few hours of trying this and that.. I found out that it was the api endpoint redirects itself based on the content-language.. so the header that I set is dead when being redirected. So either setting the i18n url in advance or setting the content-language header would solve my problem.
I have a simple iOS Swift app loosely based on the AppAuth-iOS example (https://github.com/openid/AppAuth-iOS) as well as Okta OAuth sample (https://github.com/oktadeveloper/okta-openidconnect-appauth-ios). I am not using Service Discovery nor authomatic token aquisition (i.e. not using authStateByPresentingAuthorizationRequest).
My sample works against Azure AD but does not work against Okta. I am able to log in and am authenticated and redirected back to my mobile app (AppDelegate.application()) but then the flow does not return to my OIDAuthorizationService.present() completion block.
Here is some code:
#IBAction func signInButton(_ sender: Any) {
// select idp
switch selectedIdentityProvider! {
case "Azure AD":
selectedAuthConfig = AzureAdAuthConfig()
case "Okta":
selectedAuthConfig = OktaAuthConfig();
default:
return
}
appAuthAuthorize(authConfig: selectedAuthConfig!)
}
func appAuthAuthorize(authConfig: AuthConfig) {
let serviceConfiguration = OIDServiceConfiguration(
authorizationEndpoint: NSURL(string: authConfig.authEndPoint)! as URL,
tokenEndpoint: NSURL(string: authConfig.tokenEndPoint)! as URL)
let request = OIDAuthorizationRequest(configuration: serviceConfiguration, clientId: authConfig.clientId, scopes: authConfig.scope, redirectURL: NSURL(string: authConfig.redirectUri)! as URL, responseType: OIDResponseTypeCode, additionalParameters: nil)
doAppAuthAuthorization(authRequest: request)
}
func doAppAuthAuthorization(authRequest: OIDAuthorizationRequest) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.currentAuthorizationFlow = OIDAuthorizationService.present(authRequest, presenting: self, callback: {
(authorizationResponse, error) in
if (authorizationResponse != nil) {
self.authState = OIDAuthState(authorizationResponse: authorizationResponse!)
self.logMessage(message: "Got authorization tokens. Access token: \(String(describing: self.authState?.lastAuthorizationResponse.authorizationCode))")
self.doTokenRequest()
} else {
self.authState = nil
self.logMessage(message: "Authorization error: \(String(describing: error?.localizedDescription))")
}
})
}
I could rewrite the code to use authStateByPresentingAuthorizationRequest() to see if it works but am a bit leery as this code works against Azure AD. Any suggestions?
Update 1
I forgot to mention that I have a working Android/Java example going against the same Okta definitions and working like a charm.
Update 2
I did rewrite the code to use authStateByPresentingAuthorizationRequest() against Okta and am getting the same result (i.e. getting stuck after redirect back to my app). I tested this against Azure AD and it works Ok.
Resolved. I guess the problem was that the redirect URL defined in Okta was mixed case. Android AppAuth implementation does not mind but iOS AppAuth implementation does. Changed redirect URL in Okta to lower case only, changed redirect Uri paramter passed in to lower case only and bing, all works great. Thanks #jmelberg for pointing me in this direction - by debugging resumeAuthorizationFlow(with: url) I was able to see the exact behaviour and why the call returned a False.