I'm trying to use Google's OAuth2 and YouTube APIs. The OAuth returns GTMOAuth2Authentication object that you then use to make requests to services like YouTube. My login works fine, and when I manually pass the authentication object, I can make requests.
However, I should also be able to access the authentication object via keychain, and I receive a valid object, but if I try to use it to make requests, I cannot. I keep getting the following error: "The operation couldn’t be completed. (com.google.GTMHTTPFetcher error -1.)" I'd appreciate it if someone could point out my mistake. I'm testing on a real iPhone 5s.
Authentication Code:
GTMOAuth2ViewControllerTouch * viewController = [[GTMOAuth2ViewControllerTouch alloc]
initWithScope:scope
clientID:kGoogleClientID
clientSecret:kGoogleClientSecret
keychainItemName:kGoogleKeychainItemName
delegate:self
finishedSelector:#selector(viewController:
finishedWithAuth:
error:)];
[self.navigationController pushViewController:viewController animated:YES];
Authentication Completion Handler:
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error {
NSLog(#"%s", __PRETTY_FUNCTION__);
if (error != nil) {
NSLog(#"%s %#", __PRETTY_FUNCTION__, error.localizedDescription);
return;
}
MediaGETWrapper *getWrapper = [MediaGETWrapper sharedWrapper];
getWrapper.googleAuth = auth; // passing auth directly without keychain
[getWrapper youTubeSubscriptionsWithSuccess:nil failure:nil];
YouTube Client Initialization:
self.youTube = [GTLServiceYouTube new];
GTMOAuth2Authentication *auth = [GTMOAuth2Authentication new];
[GTMOAuth2ViewControllerTouch
authorizeFromKeychainForName:kGoogleKeychainItemName
authentication:auth
error:nil];
self.youTube.authorizer = auth;
Request:
- (void)youTubeSubscriptionsWithSuccess:(void(^)(NSArray *subscriptions))success
failure:(void(^)(NSError *error))error {
NSLog(#"%s", __PRETTY_FUNCTION__);
// self.youTube.authorizer = self.googleAuth; // If uncommented, works!
GTLQueryYouTube *query = [GTLQueryYouTube queryForSubscriptionsListWithPart:#"snippet"];
query.mine = YES;
[self.youTube
executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLYouTubeChannelListResponse *channelList,
NSError *error) {
if (error != nil) {
NSLog(#"%s %#", __PRETTY_FUNCTION__, error.localizedDescription); // fails here
return;
}
for (GTLYouTubeSubscription *channel in channelList) {
NSLog(#"%#", channel.snippet.title);
}
}];
}
I couldn't find a direct fix, but you can do this:
Get the access token from a GTMOAuth2Authentication object via:
auth.accessToken
Then set the access token wherever you want to make requests.
To refresh the token, use this method:
[auth
authorizeRequest:nil // just to refresh
completionHandler:^(NSError *error) {
// your code here
}];
Related
I'm a newbie on mobile dev. I'm trying to authenticate to Amazon Cognito.
I first login to Credentials Provider using a username, pin, platform and deviceToken using custom services model - I then get identityId, endPoint and token back. I'm told that I need to swap the token I got back and refresh my credentials in order for me to be authenticated to AWS Cognito and S3. But all the process is confusing and have a lot of examples that are different.
I've created a SignInProvider, extending AWSSignInProvider to access the - (void) login: (void (^) (id result, NSError *error)) completionHanlder; I have my token, endpoint and identityId inside my login method..what do I do with the completion handler and whats next after.
#implementation SignInProvider
+(instanceType) sharedInstance{}
- (NSString) identityProviderName{}
- (AWSTask<NSString*>*) token{}
- (BOOL) isLoggedIn{}
- (NSSting*) userName{}
- (void) reloadSession{}
- (void) login: (void (^) (id result, NSError *error)) completionHanlder{
authRequest = [IMPCLDMobileAuthenticationRequest new];
[authRequest setToken:#"930fc1b56d8ca19a84500f9a79af71b65f60331f0242ce4395cdf41186443692"];
[authRequest setPassword:#"pin"];
[authRequest setUsername:#"example#email.co.za"];
[authRequest setPlatform:#"ios"];
serviceClient = [IMPCLDImpressionInternalMicroserviceClient defaultClient];
[[serviceClient mobileAuthenticationPost:authRequest] continueWithBlock:^id(AWSTask *loginTask)
{
//what to do here with my loginTask results (token, endpoint, identityId)
}
return nil;
}
To swap/save token in AWS you need to do below in your continueWithBlock
[[serviceClient mobileAuthenticationPost:authRequest] continueWithBlock:^id(AWSTask *loginTask)
{
AWSSNSCreateEndpointResponse *response = loginTask.result;
AWSSNSSubscribeInput *subscribeRequest = [AWSSNSSubscribeInput new];
subscribeRequest.endpoint = response.endpointArn;
subscribeRequest.protocols = #"application";
subscribeRequest.topicArn = YOUR_TOPIC_ARN;
return [sns subscribe:subscribeRequest];
}] continueWithBlock:^id(AWSTask *task) {
if (task.cancelled) {
NSLog(#"Task cancelled");
}
else if (task.error) {
NSLog(#"Error occurred: [%#]", task.error);
}
else {
NSLog(#"Success");
}
return nil;
}];
When I try to login to YouTube and upload a video, it gets uploaded without any issue. If I upload a video after 2-3 hours, I will get an error saying,
Error: Error Domain=com.google.GTLJSONRPCErrorDomain Code=401 "The operation couldn’t be completed. (Invalid Credentials)" UserInfo=0x14585d90 {error=Invalid Credentials, GTLStructuredError=GTLErrorObject 0x14d85ba0: {message:"Invalid Credentials" code:401 data:[1]}, NSLocalizedFailureReason=(Invalid Credentials)}
Here is the code which does Youtube login,
GIDSignIn *googleSignIn = [GDSharedInstance googleSDKApplicationSharedInstance];
googleSignIn.delegate = self;
googleSignIn.scopes = [NSArray arrayWithObject:#"https://www.googleapis.com/auth/youtube"];
[googleSignIn signIn];
signin delegate
- (void)signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user withError:(NSError *)error
{
// Auth is converted to use it for uploading a video
GTMOAuth2Authentication *youTubeAuth = [[GTMOAuth2Authentication alloc] init];
youTubeAuth.clientID = kClientID;
youTubeAuth.clientSecret = #"xxx";
youTubeAuth.userEmail = googleUser.profile.email;
youTubeAuth.userID = googleUser.userID;
youTubeAuth.accessToken = googleUser.authentication.accessToken;
youTubeAuth.refreshToken = googleUser.authentication.refreshToken;
youTubeAuth.expirationDate = googleUser.authentication.accessTokenExpirationDate;
self.youTubeService.authorizer = youTubeAuth;
}
Upload code,
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:[NSURL URLWithString:path] error:&error];
if (fileHandle) {
NSString *mimeType = [self MIMETypeForFilename:filename
defaultMIMEType:#"video/mov"];
GTLUploadParameters *uploadParameters =
[GTLUploadParameters uploadParametersWithFileHandle:fileHandle
MIMEType:mimeType];
uploadParameters.uploadLocationURL = locationURL;
GTLQueryYouTube *query = [GTLQueryYouTube queryForVideosInsertWithObject:video
part:#"snippet,status,recordingDetails"
uploadParameters:uploadParameters];
GTLServiceYouTube *service = self.youTubeService;
self.uploadFileTicket = [service executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLYouTubeVideo *uploadedVideo,
NSError *error)
{
// here I will get 401 error
}];
}
The only problem is with the GTLServiceYouTube. GIDSignIn seems to handle the refresh tokens, so that the user is always logged in after the first login. But the GTLOAuth2Authentication only works on the first login and is broken after one hour.
The token needs to be refreshed.
Use this piece of code :-
- (void)applicationWillEnterForeground:(UIApplication *)application {
[[GIDSignIn sharedInstance] signInSilently]
}
The problem here is that your auth token is expiring. You will have to use your refresh token to get a new, valid auth token after your old token expires.
If you are using an older version of the Google Plus iOS SDK, You can use GTMOAuth2Authentication to force a refresh of the auth token with the authorizeRequest: method.
From GTMOAuth2Authentication.h
// The request argument may be nil to just force a refresh of the access token,
// if needed.
- (void)authorizeRequest:(NSMutableURLRequest *)request
completionHandler:(void (^)(NSError *error))handler;
Implementation:
// In your sign in method
[[GPPSignIn sharedInstance] setKeychainName:#"googleAuth"];
// ...
// Retrieving auth and refreshing token
GTMOAuth2Authentication *auth;
auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:#"googleAuth"
clientID:#"kYourGoogleClientId"
clientSecret:#"kYourGoogleClientSecret"];
NSLog(#"old auth: %#", auth);
[auth authorizeRequest:nil completionHandler:^(NSError *error) {
if (error) {
// no auth data or refresh failed
NSLog(#"Error: %#", error);
} else {
// Auth token refresh successful
NSLog(#"new auth: %#", auth);
}
}];
I have had facebook sharing working fine in my ios app for a year and have upgraded (aka totally rewritten) to use the latest api (4.7.x) and now sharing doesnt work at all. I check that I have publish_actions permission (which I do prior to this method being called, I have 'expicitly shared' checked in open graph settings, action types, capabilities. I am validating the content (I dont get an error) and have a delegate, none of its methods get called.
-(void)shareWithFacebook:(NSString *)message
{
if ([[FBSDKAccessToken currentAccessToken] hasGranted:#"publish_actions"])
{
NIDINFO(#"Facebook sharing has publish_actions permission");
}
else
{
FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init];
[loginManager logInWithPublishPermissions:#[#"publish_actions"]
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error)
{
NIDERROR(#"Facebook sharing getting publish_actions permission failed: %#", error);
}
];
}
NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: #{
#"og:type": #"article",
#"og:title": #"Bloc",
#"og:description": message,
#"og:url": #"http://getonbloc.com/download"
}];
FBSDKShareOpenGraphObject *object = [FBSDKShareOpenGraphObject objectWithProperties:properties];
// Create the action
FBSDKShareOpenGraphAction *action = [FBSDKShareOpenGraphAction actionWithType:#"mynamespace:Share" object:object key:#"article"];
[action setString:#"true" forKey:#"fb:explicitly_shared"];
// Create the content
FBSDKShareOpenGraphContent *content = [[FBSDKShareOpenGraphContent alloc] init];
content.action = action;
content.previewPropertyName = #"article";
// Share the content
FBSDKShareAPI *shareAPI = [[FBSDKShareAPI alloc] init];
shareAPI.shareContent = content;
shareAPI.delegate = self;
NSError *error;
if([shareAPI validateWithError:&error] == NO)
{
NIDERROR(#"Facebook sharing content failed: %#", error);
}
[shareAPI share];
}
#pragma mark - FBSDKSharingDelegate
- (void) sharer:(id<FBSDKSharing>)sharer didCompleteWithResults:(NSDictionary *)results
{
NIDINFO(#"Facebook sharing completed: %#", results);
}
- (void) sharer:(id<FBSDKSharing>)sharer didFailWithError:(NSError *)error
{
NIDERROR(#"Facebook sharing failed: %#", error);
}
- (void) sharerDidCancel:(id<FBSDKSharing>)sharer
{
NIDINFO(#"Facebook sharing cancelled.");
}
I have login working and can get photos fine. I don't get any feedback at all from the facebook api, nothing gets posted. Am I doing something particularly stupid here?
Just a possibility, but I find that Facebook integration has become inconvenient because I find that every time I check the current token for granted permission through hasGranted:, it almost always fail even though I gained permission a few minutes ago, or from a previous app launch.
It seems that in your code, if no permission is granted, you try to login and get the permission again. But when that block returns, regardless whether the actual permission is granted or not, you throw an error. Instead, you should continue with sharing if it is successful.
I am creating an iOS application in which I am integrating Office-365-SDK-for-iOS for import contacts from outlook365.I am successfully able to authenticate with Microsoft Azure. But when I am fetching user and user's contacts then following error is coming-
Error Domain=Error in the Request Code=401 "The operation couldn’t be completed. (Error in the Request error 401.)
Here is my code for authentication and get client
//Acquire access and refresh tokens from Azure AD for the user.
-(void)acquireAuthTokenWithResourceId:(NSString *)resourceId completionHandler:(void (^)(BOOL authenticated))completionBlock
{
ADAuthenticationError *error;
self.authContext = [ADAuthenticationContext authenticationContextWithAuthority:OutlookAuthority error:&error];
[self.authContext acquireTokenWithResource:OutlookRsourceId
clientId:OutlookClientId
redirectUri:[NSURL URLWithString:OutlookRedirectUrl]
completionBlock:^(ADAuthenticationResult *result)
{
if (AD_SUCCEEDED != result.status)
{
completionBlock(NO);
}
else
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:result.tokenCacheStoreItem.userInformation.userId
forKey:#"LogInUser"];
[userDefaults synchronize];
self.dependencyResolver = [[ADALDependencyResolver alloc] initWithContext:self.authContext
resourceId:OutlookRsourceId
clientId:OutlookClientId
redirectUri:[NSURL URLWithString:OutlookRedirectUrl]];
completionBlock(YES);
}
}];
}
- (void) getClient:(void (^) (MSOutlookServicesClient *))callback
{
OutlookAuthManager* authenticationController = [OutlookAuthManager sharedInstance];
[authenticationController acquireAuthTokenWithResourceId:OutlookRsourceId completionHandler:^(BOOL authenticated)
{
if (authenticated)
{
callback([[MSOutlookServicesClient alloc] initWithUrl:#"https://outlook.office365.com/api/v1.0" dependencyResolver:[authenticationController dependencyResolver]]);
}
else
{
NSLog(#"Error in authentication");
}
}];
}
And following I am getting user-
[[OutlookAuthManager sharedInstance] getClient:^(MSOutlookServicesClient *client)
{
NSURLSessionTask* task = [[client getMe] readWithCallback:^(MSOutlookServicesUser *user, NSError *error)
{
if(error == nil)
{
dispatch_async(dispatch_get_main_queue(),
^{
NSLog(#"------>%#",user.DisplayName);
NSLog(#"------>%#",user.Alias);
NSLog(#"------>%#",user.Id);
NSLog(#"------>%#",user.MailboxGuid);
});
}
else
{
[client.resolver.logger logMessage:error.description withLevel:LOG_LEVEL_ERROR];
}
}];
[task resume];
}];
But That error is coming here.
Please help me
Thanks
Start by validating your access token. Since it's a 401 it's likely a problem there.
I am using "STTwitter" for getting token from below URL and request body
https://dev.twitter.com/docs/api/1.1/post/oauth2/token
request body code with URL is below
- (void)verifyCredentialsWithSuccessBlock:(void(^)(NSString *username))successBlock errorBlock:(void(^)(NSError *error))errorBlock {
[self postResource:#"oauth2/token"
baseURLString:#"https://api.twitter.com"
parameters:#{ #"grant_type" : #"client_credentials" }
useBasicAuth:YES
uploadProgressBlock:nil
downloadProgressBlock:nil
successBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) {
NSString *tokenType = [json valueForKey:#"token_type"];
if([tokenType isEqualToString:#"bearer"] == NO) {
NSError *error = [NSError errorWithDomain:NSStringFromClass([self class]) code:STTwitterAppOnlyCannotFindBearerTokenInResponse userInfo:#{NSLocalizedDescriptionKey : #"Cannot find bearer token in server response"}];
errorBlock(error);
return;
}
self.bearerToken = [json valueForKey:#"access_token"];
successBlock(_bearerToken);
} errorBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) {
errorBlock(error);
NSLog(#"ERROR %#",[error description]);
}];
}
For Calling above method i am doing below code
STTwitterAppOnly *twitter1 = [[STTwitterAppOnly alloc] init];
[twitter1 verifyCredentialsWithSuccessBlock:^(NSString *bearerToken) {
NSLog(#"sd");
[self.twitter getUserTimelineWithScreenName:#"SEREEN_NAME"
successBlock:^(NSArray *statuses) {
NSLog(#"ERROR ::: %#",[statuses description]);
// ...
} errorBlock:^(NSError *error) {
// ...
}];
} errorBlock:^(NSError *error) {
// ...
}];
I got below Error when perform above code...
**Error Domain=STHTTPRequest Code=99 "Unable to verify your credentials"**
Can you suggest me what I missed in my code?
My concern is that, I want to read twitter feed without login in Twitter.Only using "Consumer Key" and "Secret Key" with Twitter API V1.1.
Thanks
if you have ConsumerKey and ConsumerSecret you can try it
STTwitterAPIWrapper *twitterAPI = [[STTwitterAPIWrapper twitterAPIApplicationOnlyWithConsumerKey:#"Your Consumer Key" consumerSecret:#"Your Consumer Secret"] autorelease];
[twitterAPI verifyCredentialsWithSuccessBlock:^(NSString *username) {
[twitterAPI getUserTimelineWithScreenName:#"your ScreenName" count:25 successBlock:^(NSArray *statuses) {
NSLog(#"Success:%#", statuses);
} errorBlock:^(NSError *error){
NSLog(#"Error : %#",error.description);
}];
} errorBlock:^(NSError *error) {
NSLog(#"Error : %#",error.description);
}];
Verify that you have enter Consumer key and Consumer Secret in both plist and header prefix...
The code you posted has several issues. First, you're making a request from the verify method error block instead of the success block. Second, you're using the postResource method directly, instead of the higher level one to do what you want.
If all that you want is a bearer token for your consumer tokens, you can just copy / paste the sample code from STTwitter README in the App Only Authentication section.