In iOS application is required to sign a certificate request, which had previously been obtained. When I try to run a query catch this error:
kCFURLErrorUserCancelledAuthentication -1012.
The documentation says:
kCFURLErrorUserCancelledAuthentication The connection failed because
the user cancelled required authentication.
Implemented as follows:
- (void)startConnection {
NSString *serverURL = #"host.ru/method";
MKNetworkEngine *engine = [[MKNetworkEngine alloc] initWithHostName:serverURL customHeaderFields:nil];
MKNetworkOperation *op = [engine operationWithPath:nil params:nil httpMethod:#"GET" ssl:YES];
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"client" ofType:#"p12"];
[op setShouldContinueWithInvalidCertificate:YES];
op.clientCertificate = thePath;
op.clientCertificatePassword = #"1234qwerty";
[op addCompletionHandler:^(MKNetworkOperation *operation) {
NSLog(#"[operation responseData]-->>%#", [operation responseString]);
}errorHandler:^(MKNetworkOperation *errorOp, NSError* err) {
NSLog(#"MKNetwork request error : %#", [err localizedDescription]);
}];
[engine enqueueOperation:op];
}
What am I doing wrong?
P.S.
Certificate, which try to sign the request has been received in advance. It tested separately in the browser, it's okay.
An application for android to the same server requests are normally the same scheme.
This can happen when your connection sends a request for an authentication challenge.
A possible cause is that the site's certificate is invalid/untrusted and you have opted not to accept invalid certificates.
Related
I'm developing an iOS app that needs to read User data from MS Azure Active Directory.
I have successfully followed some examples on iOS app from the MS Azure documentation and successfully brought up their authentication page and have the user signed in. What I get back is some user data in the form of a ADUserInformation object.
Here's is the code I have:
NSString *authority = #"https://login.microsoftonline.com/a5960f61-0bf9-4bf6-96cd-98c61d30XXXX/federationmetadata/2007-06/federationmetadata.xml";
NSString *resourceId = #"74cd2559-0389-4871-9904-bc767d71XXXX"; // (server)
NSString *clientId = #"c8a956a7-84b7-4050-875c-896aab6bXXXX"; //ios-client (us)
NSURL *redirectUri = [[NSURL alloc]initWithString:#"https://XXXXevents.azurewebsites.net/.auth/login/done"];
ADAuthenticationError *error;
ADAuthenticationContext * authContext = [ADAuthenticationContext authenticationContextWithAuthority:authority error:&error];
//authContext.parentController = parent;
[ADAuthenticationSettings sharedInstance].enableFullScreen = YES;
[authContext acquireTokenWithResource:resourceId
clientId:clientId
redirectUri:redirectUri
completionBlock:^(ADAuthenticationResult *result) {
if (result.status != AD_SUCCEEDED) {
NSLog(#"%#", result);
return;
}
else {
//save all of this information into core data
NSDictionary * payload = #{#"access_token" : result.tokenCacheItem.accessToken};
NSLog(#"%#", payload);
//#"aad"
//#"windowsazureactivedirectory"
[[QSActivityService defaultService].client loginWithProvider: #"aad"
token: payload
completion: ^(MSUser * _Nullable user, NSError * _Nullable error) {
NSLog(#"loginWithProvider-------");
if(!error) {
NSLog(#"YAY! %s - user: %# ", __FUNCTION__, user.userId);
ADUserInformation * temp = result.tokenCacheItem.userInformation;
[[CoreDataStack defaultStack] updateUserDetailFamilyName:temp.allClaims[#"family_name"]
version:temp.allClaims[#"ver"]
email:temp.allClaims[#"email"]
nbf:temp.allClaims[#"nbf"]
exp:temp.allClaims[#"exp"]
givenName:temp.allClaims[#"given_name"]
idp:temp.allClaims[#"idp"]
ipaddr:temp.allClaims[#"ipaddr"]
iss:temp.allClaims[#"iss"]
oid:temp.allClaims[#"oid"]
typ:temp.allClaims[#"typ"]
sub:temp.allClaims[#"sub"]
amr:temp.allClaims[#"amr"]
aud:temp.allClaims[#"aud"]
alg:temp.allClaims[#"alg"]
iat:temp.allClaims[#"iat"]
tid:temp.allClaims[#"tid"]
name:temp.allClaims[#"name"]
uniqueName:temp.allClaims[#"unique_name"]];
//other code, no problems here
MS Graph API
However, I would like access profile images, and all the other data. I have read that MS Graph API provides it, but I'm not sure how and where I would put the token.
Do I use the token from result.tokenCacheItem.accessToken? If so, in the header? or body?
Or do I simply hit up graph.windows.com twice. First time to get the Authentication Token, and second time for the data?
I have read a lot of documentation and none of them works as I keep getting the Token Missing or Malformed error message.
My Graph API code looks like this:
-(void)getUsersUsingAccessToken:(NSDictionary*)token completion:(void (^) (void))completion {
NSString * tenant = #"a5960f61-0bf9-4bf6-96cd-98c61d306f12";
NSString * accessToken = token[#"access_token"];
NSString * urlString = [NSString stringWithFormat: #"https://graph.windows.net/%#/tenantDetails?api-version=1.6", tenant];
NSString * httpVerb = #"POST";
//build an info object and convert to json
NSDictionary * bodyFormDict
= [NSDictionary dictionaryWithObjectsAndKeys:
#"client_credentials", #"grant_type",
#"https://graph.windows.net", #"resource",
#"c8a956a7-84b7-4050-875c-896aab6xxxx", #"client_id",
#"XLlZl69aUKiQTo4dpeiprItm+LYbDtpt6e9dn0bxxxx", #"client_secret",
nil];
NSError *error = nil;
//1st step
NSData * jsonInputData = [NSJSONSerialization dataWithJSONObject:bodyFormDict
options:NSJSONWritingPrettyPrinted
error:&error];
//2nd step
NSString * httpBodyString = [[NSString alloc]
initWithData:jsonInputData
encoding:NSUTF8StringEncoding];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.allowsCellularAccess = YES;
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
request.HTTPMethod = httpVerb;
[request setValue: #"application/json; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setValue: accessToken forHTTPHeaderField:#"Authorization: Bearer"];
[request setHTTPBody:[httpBodyString dataUsingEncoding:NSUTF8StringEncoding]];
//asynchronous
NSURLSessionDataTask * getDataTask = [self.session dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
//other code
}
If someone can provide working code sample using objective c to successfully retrieve data from the MS Graph API, it would be a great help.
Thanks for your time!
I believe the problem you are having is that the http header field isn't set correctly. Try this -
NSString *authValue = [NSString stringWithFormat:#"Bearer %#", accessToken];
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
I found my answers in MS MSDN's starter projects and code samples
https://msdn.microsoft.com/en-us/office/office365/howto/starter-projects-and-code-samples
The specific project that helped me is this:
https://github.com/OfficeDev/O365-iOS-Microsoft-Graph-Profile
As you are going through that sample keep in mind:
Replace INSERT-AUTHORITY-HERE - the name of the tenant in which you provisioned your application. The format should be https://login.windows.net/(YourAzureUserName).onmicrosoft.com
I have found that
https://login.microsoftonline.com/YourAzureAccountID/federationmetadata/2007-06/federationmetadata.xml
also works
Replace INSERT-RESOURCE-ID-HERE - the ID for your mobile app backend. This is the Web API service app ID. NOT the native client iOS app.
Replace INSERT-CLIENT-ID-HERE - the client ID you copied from your iOS NATIVE client application. NOT the Web API service app.
Replace INSERT-REDIRECT-URI-HERE - your site’s /.auth/login/done endpoint, using the HTTPS scheme. This value should be similar to
#"https://XXXXXXXXXX.azurewebsites.net/.auth/login/done"
IF you have trouble importing the ADAL framework...
http://shanghaiseagull.com/index.php/2016/05/11/import-another-project-framework-into-your-project/
library can be found here: https://github.com/AzureAD/azure-activedirectory-library-for-objc
Hope it helps someone starting out...and please let me know if I can be of further help.
I'm using test users with the IOS Facebook SDK. I create test users and retrieve one userID. Then I create a graphPath string that resembles the "/me" path: /{user-id}
NSString* userId = [[self.testUserTokens objectAtIndex:0] userID];
NSString* graphPath = [NSString stringWithFormat:#"/%#", userId];
Then I make the graph request.
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:graphPath parameters:#{#"fields": #"name, id"}];
FBSDKGraphRequestConnection *connection = [[FBSDKGraphRequestConnection alloc] init];
[connection addRequest:request
completionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
NSLog(#"data = %#", result);
if (error)
NSLog(#"error = %#", error);
//TODO: process me information
}];
[connection start];
This returns a 400 "Bad Request" error. I don't understand why at all, I thought I was following the correct format from the Facebook documentation. Does anyone know what I'm doing wrong? I figure it's just a small mistake I can't notice.
Edit: After using the Facebook Graph Explorer, it appears that a /{user-id} request works correctly, using my own Facebook User ID for instance. But it does not work with the IDs of my test users, which I can retrieve either in the Roles panel of Facebook Developers Dashboard or in my app using the method above. For my own ID, I notice that it is different whether I'm retrieving it from the Graph Explorer or from my app, which is logical considering Facebook explains IDs differ based on the app being used, I believe.
Doing the request above using my own personal ID, retrieved using [FBSDKAccessToken currentAccessToken].userId works perfectly fine. So the issue seems to stem from the test user IDs.
Facebook Developer support has resolved this issue for me after I submitted it as a bug.
Turns out it was required to pass a user access token, which I assume is required when wanting to access direct {user-id} data instead of "me" data.
The code thus looks like this:
[testManager requestTestAccountTokensWithArraysOfPermissions: #[[NSSet setWithObjects:#"email", #"user_friends", nil], [NSSet setWithObjects:#"email", #"user_friends", nil]] createIfNotFound:NO completionHandler:^(NSArray* tokens, NSError* error)
{
self.testUserTokens = tokens;
NSLog(#"token1 = %#", [[self.testUserTokens objectAtIndex:0] tokenString]);
NSString* userId = [[self.testUserTokens objectAtIndex:0] userID];
NSString *accessToken = [[self.testUserTokens objectAtIndex:0] tokenString];
NSString* graphPath = [NSString stringWithFormat:#"/%#", userId];
NSLog(#"graphPath = %#", graphPath);
[[[FBSDKGraphRequest alloc] initWithGraphPath:graphPath parameters:#{#"fields": #"name, id, email"} tokenString:accessToken version:nil HTTPMethod:#"GET"] startWithCompletionHandler:^(FBSDKGraphRequestConnection* connection, id result, NSError* error)
{
NSLog(#"data = %#", result);
if (error)
NSLog(#"error = %#", error);
}];
I am trying my hands on new fabric twitter kit for iOS.
After signing in we can ask user to allow access for the email id and if it allows then returns email of the logged in user but it giving me error.
Email (null), Error: Error Domain=NSURLErrorDomain Code=-1001
"The request timed out." UserInfo=0x7fdd314f1c30
{NSUnderlyingError=0x7fdd314d9aa0 "The request timed out.",
NSErrorFailingURLStringKey=https://api.twitter.com/1.1/account/verify_cre
dentials.json?skip_status=true&include_email=true,
NSErrorFailingURLKey=https://api.twitter.com/1.1/account/verify_credentia
ls.json?skip_status=true&include_email=true,
NSLocalizedDescription=The request timed out.}
And here is my code that i've tried from their doc.
if ([[Twitter sharedInstance] session]) {
TWTRShareEmailViewController* shareEmailViewController =
[[TWTRShareEmailViewController alloc]
initWithCompletion:^(NSString* email, NSError* error) {
NSLog(#"Email %#, Error: %#", email, error);
}];
[self presentViewController:shareEmailViewController
animated:YES
completion:nil];
} else {
// TODO: Handle user not signed in (e.g.
// attempt to log in or show an alert)
}
Is anything wrong in my code? Please help me.
I can post my status and media to twitter but can't get email Id.
Can anyone please help me in this problem? I'm also new to development.
To get user email address, your application should be whitelisted. Here is the link. Go to use this form. You can either send mail to sdk-feedback#twitter.com with some details about your App like Consumer key, App Store link of an App, Link to privacy policy, Metadata, Instructions on how to log into our App etc..They will respond within 2-3 working days.
Here is the story how I got whitelisted by conversation with Twitter support team:
Send mail to sdk-feedback#twitter.com with some details about your App like Consumer key, App Store link of an App, Link to privacy policy, Metadata, Instructions on how to log into our App. Mention in mail that you want to access user email adress inside your App.
They will review your App and reply to you withing 2-3 business days.
Once they say that your App is whitelisted, update your App's settings in Twitter Developer portal. Sign in to apps.twitter.com and:
On the 'Settings' tab, add a terms of service and privacy policy URL
On the 'Permissions' tab, change your token's scope to request email. This option will only been seen, once your App gets whitelisted.
Put your hands on code
Use of Twitter framework:
Get user email address
-(void)requestUserEmail
{
if ([[Twitter sharedInstance] session]) {
TWTRShareEmailViewController *shareEmailViewController =
[[TWTRShareEmailViewController alloc]
initWithCompletion:^(NSString *email, NSError *error) {
NSLog(#"Email %# | Error: %#", email, error);
}];
[self presentViewController:shareEmailViewController
animated:YES
completion:nil];
} else {
// Handle user not signed in (e.g. attempt to log in or show an alert)
}
}
Get user profile
-(void)usersShow:(NSString *)userID
{
NSString *statusesShowEndpoint = #"https://api.twitter.com/1.1/users/show.json";
NSDictionary *params = #{#"user_id": userID};
NSError *clientError;
NSURLRequest *request = [[[Twitter sharedInstance] APIClient]
URLRequestWithMethod:#"GET"
URL:statusesShowEndpoint
parameters:params
error:&clientError];
if (request) {
[[[Twitter sharedInstance] APIClient]
sendTwitterRequest:request
completion:^(NSURLResponse *response,
NSData *data,
NSError *connectionError) {
if (data) {
// handle the response data e.g.
NSError *jsonError;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:data
options:0
error:&jsonError];
NSLog(#"%#",[json description]);
}
else {
NSLog(#"Error code: %ld | Error description: %#", (long)[connectionError code], [connectionError localizedDescription]);
}
}];
}
else {
NSLog(#"Error: %#", clientError);
}
}
Hope it helps !!!
I'm trying to create an iOS app that uses OAuth2 authentication using the native iOS NSURLSession URL loading classes. I gain an access token fine using the directions here:
http://www.freesound.org/docs/api/authentication.html
I subsequently launch the application and run a search query
https://www.freesound.org/apiv2/search/text/?query=snare
The request header fields looks like this (note my access token is not expired and I have confirmed it is the same as I received from performing the steps above)
{
"Authorization: Bearer" = MY_ACCESS_TOKEN;
}
This fails with:
{"detail": "Authentication credentials were not provided."}
The response headers look like this:
{
Allow = "GET, HEAD, OPTIONS";
Connection = "keep-alive";
"Content-Type" = "application/json";
Date = "Sat, 31 Jan 2015 13:56:32 GMT";
Server = "nginx/1.2.1";
"Transfer-Encoding" = Identity;
Vary = "Accept, Cookie";
"Www-Authenticate" = "Bearer realm=\"api\"";
}
The funny thing is that this does not always happen. If I repeat this entire process a number of times, deleting the app in between, it will eventually work. Once it works, it will continue to work while I'm developing. Sometimes then when I come back to it, say the next day, it stops working and I need to repeat this deleting and re-installing routine to get it back working again!
There's an authentication challenge delegate method on NSURLSession that will get called if implemented. It's a 'server trust' challenge. Could this be something to do with it? Would you even expect an authentication challenge of this nature? There's nothing mentioned about it in the docs alluded to above.
Any help would be much appreciated.
EDIT
This is how the search text ("snare") GET call is made.
I basically pass in an NSMutableURLRequest with the URL set to the above (https://www.freesound.org/apiv2/search/text/?query=snare). useAccessToken is set to YES.
- (void)makeRequest:(NSMutableURLRequest *)request useAccessToken:(BOOL)useAccessToken completion:(CompletionBlock)completion {
NSAssert(completion, #"No completion block.");
if (useAccessToken) {
NSString *accessToken = [[ODMFreesoundTokenCache sharedCache] accessToken];
NSAssert(accessToken.length, #"No access token.");
[request addValue:accessToken forHTTPHeaderField:#"Authorization: Bearer"];
}
NSLog(#"Making request: %# \n\nWith access token: %#", request, [[ODMFreesoundTokenCache sharedCache] accessToken]);
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSInteger code = [(NSHTTPURLResponse *)response statusCode];
if (code == 200) {
if (!error) {
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"json: %#", json);
completion(json, error);
}
else {
completion(nil, error);
}
}
else {
NSString *reason = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSError *error = [NSError errorWithDomain:#"Request Error" code:code userInfo: reason ? #{NSLocalizedDescriptionKey : reason} : nil];
NSLog(#"error: %#", error);
completion(nil, error);
}
}];
[task resume];
}
The 2 flows for authentication described in the doc are not "safe" for a device. Using API keys would require the secret to be stored in the device.
The OAuth2 flow they support (authorization_code) requires a server to server call to exchange a code for the actual token (This step: http://www.freesound.org/docs/api/authentication.html#step-3). This call requires another credential (the client_secret that you probably should not store in the device either.
You need a server in between that negotiates this for you. Or a server that translates the code flow into token one. (Illustrated here: https://auth0.com/docs/protocols#5).
I'm dealing the authenticate issue with Tumblr account using [NSURLConnection sendAsynchronousRequest:queue:completionHandler:] to send the authenticate request, but here I meet a tough problem:
Whenever I send the request at the first time, everything goes perfectly, but when the first authentication is done and then resend the request second time, there comes "NSURLErrorDomain error -1012".
The authenticate page is loaded in a webview so that the authentication should be done in my app without a browser. But it is interesting that if the process runs in a browser there comes no error, errors only happen when using webview.
It was weird that the authentication goes with the same code, but only the first authentication can be done, only if I reinstall the app can I authenticate it again, and after this the problem comes again.
I did everything I can chase to solve the issue, I clean the cache and cookie in webview, step the authentication process to see parameters, set the cachePolicy of the request but nothing helps.
I also found that on ios6 the process goes without any error. But on ios7 I get the -1012.
code -1012 tells me that the user cancelled the authentication, but the process goes automatically and I do not cancel it.
I'm wondering if the problem comes from the NSURLConnection.
- (void)authenticate:(NSString *)URLScheme WithViewController:(UIViewController *)con callback:(TMAuthenticationCallback)callback {
self.threeLeggedOAuthTokenSecret = nil;
self.hostViewController = con;
self.callback = callback;
[self emptyCookieJar];
NSString *tokenRequestURLString = [NSString stringWithFormat:#"http://www.tumblr.com/oauth/request_token?oauth_callback=%#", TMURLEncode([NSString stringWithFormat:#"%#://tumblr-authorize", URLScheme])];
NSLog(#"%#", tokenRequestURLString);
NSMutableURLRequest *request = mutableRequestWithURLString(tokenRequestURLString);
NSLog(#"%#", request);
[[self class] signRequest:request withParameters:nil consumerKey:self.OAuthConsumerKey
consumerSecret:self.OAuthConsumerSecret token:nil tokenSecret:nil];
[self openOAuthViewController];
NSURLConnectionCompletionHandler handler = ^(NSURLResponse *response, NSData *data, NSError *error) {
NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
if (error) {
if (callback) {
callback(nil, nil, error);
}
return;
}
NSLog(#"%d", statusCode);
if (statusCode == 200) {
self.threeLeggedOAuthCallback = callback;
NSDictionary *responseParameters = formEncodedDataToDictionary(data);
self.threeLeggedOAuthTokenSecret = responseParameters[#"oauth_token_secret"];
NSURL *authURL = [NSURL URLWithString:
[NSString stringWithFormat:#"http://www.tumblr.com/oauth/authorize?oauth_token=%#",
responseParameters[#"oauth_token"]]];
[self initOAuthViewControllerWithURL:authURL];
} else {
if (callback) {
callback(nil, nil, errorWithStatusCode(statusCode));
}
}
};
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:handler];
}
Code above, everything goes normally before [NSURLConnection sendAsynchronousRequest:queue:completionHandler:],and after this method I got the error in completionHandler.