mailcore2 imap Error Domain=MCOErrorDomain Code=5 - ios

I in turn to the making relevant mailcore2 problems, based on past errors, and all of the share, or didn't find the solution to this problem below, so the problem I have encountered redistribution, also hope to get more help, thank you!
Mailcore2 using the code below:
self.imapSession = [[MCOIMAPSession alloc] init];
self.imapSession.hostname = hostname;
self.imapSession.port = 993;
self.imapSession.username = username;
self.imapSession.password = password;
if (oauth2Token != nil) {
self.imapSession.OAuth2Token = oauth2Token;
self.imapSession.authType = MCOAuthTypeXOAuth2;
}
self.imapSession.connectionType = MCOConnectionTypeTLS;
MasterViewController * __weak weakSelf = self;
self.imapSession.connectionLogger = ^(void * connectionID, MCOConnectionLogType type, NSData * data) {
#synchronized(weakSelf) {
if (type != MCOConnectionLogTypeSentPrivate) {
NSLog(#"event logged:%p %i withData: %#", connectionID, type, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
}
};
// Reset the inbox
self.messages = nil;
self.totalNumberOfInboxMessages = -1;
self.isLoading = NO;
self.messagePreviews = [NSMutableDictionary dictionary];
[self.tableView reloadData];
NSLog(#"checking account");
self.imapCheckOp = [self.imapSession checkAccountOperation];
[self.imapCheckOp start:^(NSError *error) {
MasterViewController *strongSelf = weakSelf;
NSLog(#"finished checking account.");
if (error == nil) {
[strongSelf loadLastNMessages:NUMBER_OF_MESSAGES_TO_LOAD];
} else {
NSLog(#"error loading account: %#", error);
}
strongSelf.imapCheckOp = nil;
}];
When I use iOS8.1 equipment, error message as follows, the console output:
checking account
2015-02-15 10:58:36.712 MailCoreTest[11971:2988194] event logged:0x166a4d50 0 withData: * OK [CAPABILITY IMAP4 IMAP4rev1 IDLE XAPPLEPUSHSERVICE ID UIDPLUS AUTH=LOGIN NAMESPACE] QQMail IMAP4Server ready
2015-02-15 10:58:36.821 MailCoreTest[11971:2988194] event logged:0x166a4d50 0 withData: (null)
2015-02-15 10:58:36.824 MailCoreTest[11971:2988128] finished checking account.
2015-02-15 10:58:36.825 MailCoreTest[11971:2988128] error loading account: Error Domain=MCOErrorDomain Code=5 "Unable to authenticate with the current session's credentials." UserInfo=0x16596ec0 {NSLocalizedDescription=Unable to authenticate with the current session's credentials.}
But when I use iOS8.0 devices, console to print the results are as follows:
checking account
2015-02-15 11:22:53.249 MailCoreTest[7853:1742121] finished checking account.
2015-02-15 11:22:53.249 MailCoreTest[7853:1742121] error loading account: Error Domain=MCOErrorDomain Code=1 "A stable connection to the server could not be established." UserInfo=0x17e50ab0 {NSLocalizedDescription=A stable connection to the server could not be established.}
Please help me, I contact with Objective - C time is short, but I must do my best to be aggressive, still hope to give a detailed solution, sincere thank you once again, hard work!

Yes its an authentication issue, I had same issue with my app but it was working fine before.
After I got this error, I checked my gmail account and there was an email from Google with subject "Suspicious Sign in prevented" and This sign in was of CA, USA. Actually My Application was under review so it was rejected for this reason.
Then I found that gmail is disabling unauthorised IPs.
You have to enable the 2 step verification for the gmail account to resolve this and generate an app specific password. Use that password in your app and It'll work fine.
Follow below google link to generate application specific password.
https://support.google.com/mail/answer/1173270?hl=en

Related

API Gateway generated SDK for iOS (Objective-C) with Cognito User Pool for authorized users

I have deployed an API using AWS API Gateway and I am trying to access it through an iOS device. Some endpoints support unauthorized users and I have no trouble accessing them but others don't and I can't manage to query them. I use the new Cognito User Pool feature for authentication which works fine when using a web application (or postman).
First, even though some of the endpoints are protected (like in the following picture of the console), when I deploy the API and generate the SDK for iOS (Objective-C), I can read in the README file: "All endpoints do not require authorization."
Then, when I run the following authentication code from AWS documentation, everything seems to work just fine:
AWSServiceConfiguration *serviceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionEUWest1 credentialsProvider:nil];
AWSCognitoIdentityUserPoolConfiguration *userPoolConfiguration = [[AWSCognitoIdentityUserPoolConfiguration alloc] initWithClientId:ClientId clientSecret:nil poolId:UserPoolId];
[AWSCognitoIdentityUserPool registerCognitoIdentityUserPoolWithConfiguration:serviceConfiguration userPoolConfiguration:userPoolConfiguration forKey:#"UserPool"];
AWSCognitoIdentityUserPool *pool = [AWSCognitoIdentityUserPool CognitoIdentityUserPoolForKey:#"UserPool"];
AWSCognitoIdentityUser *user = [pool getUser];
[[user getSession:username password:password validationData:nil] continueWithBlock:^id(AWSTask<AWSCognitoIdentityUserSession *> *task) {
if (task.error) {
NSLog(#"Could not get user session. Error: %#", task.error);
} else if (task.exception) {
NSLog(#"Could not get user session. Exception: %#", task.exception);
} else {
NSLog(#"Successfully retrieved user session data");
AWSCognitoIdentityUserSession *session = (AWSCognitoIdentityUserSession *) task.result;
NSMutableString *poolId = [[NSMutableString alloc] initWithString:#"cognito-idp.eu-west-1.amazonaws.com/"];
[poolId appendString:UserPoolId];
NSString *tokenStr = [session.idToken tokenString];
NSDictionary *tokens = [[NSDictionary alloc] initWithObjectsAndKeys:tokenStr, poolId, nil];
CognitoPoolIdentityProvider *idProvider = [[CognitoPoolIdentityProvider alloc] init];
[idProvider addTokens:tokens];
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionEUWest1 identityPoolId:IdentityPoolId identityProviderManager:idProvider];
[credentialsProvider clearKeychain];
[credentialsProvider clearCredentials];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionEUWest1
credentialsProvider:credentialsProvider];
[[credentialsProvider getIdentityId] continueWithBlock:^id _Nullable(AWSTask<NSString *> * _Nonnull task) {
if (task.error) {
NSLog(#"Could not get identity id: %#", task.error);
} else if (task.exception) {
NSLog(#"Could not get identity id: %#", task.exception);
} else {
NSLog(#"Identity id: %#", task.result);
}
return nil;
}];
}
return nil;
}];
I implemented CognitoPoolIdentityProvider as specified in this post.
#interface CognitoPoolIdentityProvider ()
#property (strong, nonatomic) NSDictionary *tokens;
#end
#implementation CognitoPoolIdentityProvider
- (AWSTask<NSDictionary *> *)logins {
return [AWSTask taskWithResult:self.tokens];
}
- (void)addTokens:(NSDictionary *)tokens {
self.tokens = tokens;
}
#end
I manage to get a proper token (tested using postman) and a user id:
2016-12-27 12:43:35.760 AskHub[26625:10037234] AWSiOSSDK v2.4.11 [Debug] AWSURLResponseSerialization.m line:63 | -[AWSJSONResponseSerializer responseObjectForResponse:originalRequest:currentRequest:data:error:] | Response body:
{"IdentityId":"eu-west-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"}
2016-12-27 12:43:35.766 AskHub[26625:10037234] Identity id: eu-west-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
However, when I run the following code to hit a protected endpoint, CloudFront considers that I am not authenticated.
APIHealthClient *apiClient = [APIHealthClient defaultClient];
APIAskHubRequest *initReq = [[APIAskHubRequest alloc] init];
initReq.message = #"FIN";
NSLog(#"Sending initial request");
[[apiClient webhooksAskhubPost:initReq] continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(#"Could not send initial request: %#", task.error);
} else if (task.exception) {
NSLog(#"Could not send initial request: %#", task.exception);
} else {
NSLog(#"Successfully sent initial request");
}
return nil;
}];
Reponse:
2016-12-27 12:56:25.247 AskHub[26784:10046562] Could not send initial request: Error Domain=com.amazonaws.AWSAPIGatewayErrorDomain Code=1 "(null)" UserInfo={HTTPBody={
message = Unauthorized;
}, HTTPHeaderFields={type = immutable dict, count = 9,
entries =>
3 : Via = {contents = "X.X XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.cloudfront.net (CloudFront)"}
4 : x-amzn-ErrorType = {contents = "UnauthorizedException"}
5 : Content-Type = {contents = "application/json"}
6 : Content-Length = {contents = "27"}
7 : Connection = {contents = "keep-alive"}
8 : x-amzn-RequestId = {contents = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"}
9 : Date = {contents = "Tue, 27 Dec 2016 11:56:25 GMT"}
10 : X-Cache = {contents = "Error from cloudfront"}
11 : X-Amz-Cf-Id = {contents = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}
}
}
Am I missing something here? Does the automatically generated SDK support User Pool authentication?
Based upon the code I see, you are clearing credentials but never getting credentials. On quick review it looks like you are setting up the service configuration and authentication correctly, but you never call credentials.
If that was the case, even though your application would authenticate, the credentials provider would never go get the logins dictionary.
(You can verify this in the AWS Console, if you look at the identityId you are trying to use to access the resource, you can look at the "logins" count. If it is 0, you are not logged in.)
In order to be authenticated you need to have the credentials provider call your identityProviderManager "logins" method.
That call happens when you do "getCredentialsForIdentity" which is "credentials" in the SDK.
Typically the sequence is GetID followed by GetCredentialsForId. In the IOS SDK, the GetId happens with "getSession" (so you should have that), and the GetCredentials happens with "credentials"
(Note that there are different SDKs with different naming in each (Mobile Hub vs IOS SDK for example) but all that matters is that your credentials provider gets a login dictionary. With verbose logging you should actually see the "logins" dictionary logged, which will prove to you that a logins dictionary is being provided to the credentials provider).
I figured out that there is a confusion here between two types of authentication modes: using a credentials provider or just adding the JWT token to the "Authorization" header parameter (or your custom parameter name if you specified one when creating your authorizer).
The first method did not work for me, even though it is the only one that I have found in the documentation when it comes to using the iOS SDK. The second method works pretty fine, but to be able to use it without hacking into the SDK, don't forget to add the "Authorization" header when you specify your API in API Gateway.

Google Drive on iOS and "invalid_grant"

I have an iOS app that uses Google Drive to sync some files. The app was working fine up until a couple of months ago (it uses iCloud by default for syncing so it wasn't until recently that I learned the Google Drive since was no longer working).
The error I'm getting is:
An error occurred: Error Domain=com.google.HTTPStatus Code=400 "(null)" UserInfo={json={
error = "invalid_grant";
}, data=<7b0a2020 22657272 6f722220 3a202269 6e76616c 69645f67 72616e74 220a7d>}
The OAuth part sets to be OK and I think I'm authorized to access the user's Google Drive. The error happens when I try to list the files at the top level to see if I need to create a special directory for my app.
- (void)createGoogleDocumentsDirIfNeeded:(id<GoogleServiceDelegate>)delegate
{
NSString *parentID = #"root";
GTLQueryDrive *query = [GTLQueryDrive queryForFilesList];
query.q = [NSString stringWithFormat:#"'%#' in parents and title = '%#' and trashed != true", parentID, APP_NAME];
[googleService executeQuery:query completionHandler:^(GTLServiceTicket *ticket,
GTLDriveFileList *files,
NSError *error)
{
if (error == nil)
{
if ( files.items.count == 0)
{
NSLog(#"Creating google documents dir: %#", APP_NAME);
[self createGoogleDocumentsDir:delegate];
}
else
{
GTLDriveFile *file = [files objectAtIndexedSubscript:0];
self.googleDocumentsID = file.identifier;
NSLog(#"Directory exists: %#; fileID = %#", APP_NAME, googleDocumentsID);
[delegate googleDocumentsDirCreated:YES];
}
}
else
{
NSLog(#"An error occurred: %#", error);
}
}];
}
The error is displayed in the last NSLog().
I'm pretty convinced that something has change on Google's side that is causing this. I've reverted to code to a point where I know it worked before and it is still failing.
Possibly I've made some change in our Google Developer's Console that is causing this, but I can't see what.
It is also the case that the Google API Dashboard still shows some successful accesses, so it seems this is not failing for everyone.
Any thoughts as to what could be going on? The "invalid_grant" error seems pretty generic and web searches show it happens in lots of situations. Any help would be appreciated.
As you say, there could be a variety of explanations.
Some to check are:-
has the user withdrawn his permission
are you using an expired refresh token
is the phone's clock accurate
Read https://developers.google.com/identity/protocols/OAuth2UserAgent
The URL used when authenticating a user is https://accounts.google.com/o/oauth2/v2/auth. This endpoint is accessible over SSL, and HTTP connections are refused.
Only for httpS connects for now

cloudKit: CKSubscription error "This request requires an authenticated account""

I'm testing my app with no authenticated iCloud accounts but I'm getting this error when to subscribe the device for notifications:
subscription error<CKError 0x1700581e0: "Not Authenticated" (9/1002); "This request requires an authenticated account"; Retry after 3.0 seconds>
This is Ok but my question is how can check if the device is login to iCloud before try to run the CKSubscription code?
I'll really appreciate your help.
I ended up here searching for "cloudkit This request requires an authenticated account" on google. The problem for me was that I wasn't signed in to iCloud inside the simulator. I had kind of assumed I would be running under my development Apple ID automatically...
You can use the accountStatusWithCompletionHandler method on the container. If you want to use subscriptions, then it should return a status with a .hashValue of 1
container = CKContainer.defaultContainer()
container.accountStatusWithCompletionHandler({status, error in
if (error != nil) { NSLog("Error = \(error.description)")}
NSLog("Account status = \(status.hashValue) (0=CouldNotDetermine/1=Available/2=Restricted/3=NoAccount)")
})
This is the Objective-C version:
CKContainer *container = [CKContainer defaultContainer];
[container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError *error)
{
if (((accountStatus == 3) || (accountStatus == 2)) && (!error))
{
NSLog(#" no error but status %ld",accountStatus);
// typedef NS_ENUM(NSInteger, CKAccountStatus) {
// /* An error occurred when getting the account status, consult the corresponding NSError */
// CKAccountStatusCouldNotDetermine = 0,
// /* The iCloud account credentials are available for this application */
// CKAccountStatusAvailable = 1,
// /* Parental Controls / Device Management has denied access to iCloud account credentials */
// CKAccountStatusRestricted = 2,
// /* No iCloud account is logged in on this device */
// CKAccountStatusNoAccount = 3,
//
// }
}
if (error)
{
NSLog(#" accountStatus error %#",error);
}
} ];
in swift 2 you compare status to
case CouldNotDetermine
case Available
case Restricted
case NoAccount

Google OAuth Login Error: Invalid credentials

I have an iPad application which allows users to login to their Gmail account(s) using OAuth2. Thus far, the login process and email fetching is successful. However, when the app is closed and then re-opened after a (long) period of time, an error is produced "invalid credentials,' even though previous logins with the same credentials were successful.
Login Flow:
1) User logs in to gmail using OAuth 2.
2) User email address and oAuthToken provided by the GTMOAuth2Authentication object are saved to core data for future logins.
3) IMAP Session is created using saved email address and OAuthToken.
Here is the relevant code
Google Login
- (void)gmailOAuthLogin
{
NSDictionary *googleSettings = [[EmailServicesInfo emailServicesInfoDict] objectForKey:Gmail];
GTMOAuth2ViewControllerTouch *googleSignInController =
[[GTMOAuth2ViewControllerTouch alloc] initWithScope:GmailScope clientID:GmailAppClientID clientSecret:GmailClientSecret keychainItemName:KeychainItemName completionHandler:^(GTMOAuth2ViewControllerTouch *googleSignInController, GTMOAuth2Authentication *auth, NSError *error){
if (error != nil) {
//handle error
} else {
[[ModelManager sharedInstance] authenticateWithEmailAddress:[auth userEmail]
oAuthToken:[auth accessToken] imapHostname:[googleSettings objectForKey:IMAPHostName] imapPort:[[googleSettings objectForKey:IMAPPort]integerValue] smtpHostname:[googleSettings objectForKey:SMTPHostName] smtpPort:[[googleSettings objectForKey:SMTPPort]integerValue] type:EmailProtocolTypeImapAndSmtpGMail success:^(Account *account) {
//create IMAP session using above arguments
} failure:^(NSError *error) {
//handle error
}];
}
}];
[self presentGoogleSignInController:googleSignInController];
}
Create IMAP Session Using MailCore2
- (void)authenticateWithEmailAddress:(NSString *)emailAddress password:(NSString *)password oAuthToken:(NSString *)oAuthToken imapHostname:(NSString *)imapHostname imapPort:(NSInteger)imapPort smtpHostname:(NSString *)smtpHostname smtpPort:(NSInteger)smtpPort success:(void (^)())success failure:(void (^)(NSError *))failure
{
self.imapSession = [[MCOIMAPSession alloc] init];
self.imapSession.hostname = imapHostname;
self.imapSession.port = imapPort;
self.imapSession.username = emailAddress;
self.imapSession.connectionType = MCOConnectionTypeTLS;
self.imapSession.password = nil;
self.imapSession.OAuth2Token = oAuthToken;
self.imapSession.authType = nil != oAuthToken ? MCOAuthTypeXOAuth2 :
self.imapSession.authType;
[self.imapSession setConnectionLogger:^(void * connectionID, MCOConnectionLogType type,
NSData * data){
NSLog(#"MCOIMAPSession: [%i] %#", type, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
self.smtpSession = [[MCOSMTPSession alloc] init];
self.smtpSession.hostname = smtpHostname;
self.smtpSession.port = smtpPort;
self.smtpSession.username = emailAddress;
self.smtpSession.connectionType = MCOConnectionTypeTLS;
self.smtpSession.password = nil;
self.smtpSession.OAuth2Token = oAuthToken;
self.smtpSession.authType = nil != oAuthToken ? MCOAuthTypeXOAuth2 :
self.smtpSession.authType;
[self.smtpSession setConnectionLogger:^(void * connectionID, MCOConnectionLogType type, NSData * data){
NSLog(#"MCOSMTPSession: [%i] %#", type, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
[[self.imapSession checkAccountOperation] start:^(NSError *error) {
if (nil == error) {
success();
} else {
failure(error); //FAILS WITH INVALID CREDENTIALS ERROR
}
}];
}
Once again, the above code works fine, unless the application has not been used in some time. I was not sure if I needed to refresh the OAuthToken or not, so I tried doing the following on launch of the application:
GTMOAuth2Authentication *auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:KeychainItemName clientID:GmailAppClientID clientSecret:GmailClientSecret];
BOOL canAuthorize = [auth canAuthorize]; //returns YES
NSDictionary *googleSettings = [[EmailServicesInfo emailServicesInfoDict] objectForKey:Gmail];
[[ModelManager sharedDefaultInstance] authenticateWithEmailAddress:[auth userEmail] oAuthToken:[auth refreshToken] imapHostname:[googleSettings objectForKey:IMAPHostName] imapPort:[[googleSettings objectForKey:IMAPPort]integerValue] smtpHostname:[googleSettings objectForKey:SMTPHostName] smtpPort:[[googleSettings objectForKey:SMTPPort]integerValue] type:EmailProtocolTypeImapAndSmtpGMail success:^(Account *account) {
//create IMAP session
} failure:^(NSError *error) {
NSLog(#"failure %#", error);
}];
But I still get the same error. I have no idea why the OAuth token stops working or how to resolve this. Since the user is able to save multiple accounts, I am wondering if I need to save the refresh token for each account in core data and use that if the access token stops working?
(Disclaimer - I don't know iOS or the gtm-oauth2 libraries, but I do know Google's OAuth implementation.)
Conceptually you do need to persist the refresh token for the user. The refresh token is a long-lived credential which is used (along with your client secret) to get a short-lived access token that is used for actual API calls.
If you anticipate making multiple calls in a short period of time then your app will normally actually persist both the refresh token and access token (currently access tokens will last 1 hour).
That all said, it looks like the gtm-oauth2 library should be taking care of persisting these already (looks like authForGoogleFromKeychainForName does this).
What I think you need help with is getting an up-to-date access token that you can use to initiate your IMAP session.
The gtm-oauth2 library does contain an authorizeRequest method. It takes information about an HTTP request you intend to make and adds the appropriate authorization headers. It looks like this code will examine the state of the access token, and refresh it if necessary.
While I know you aren't able to make an HTTP request (you need to speak IMAP), my suggestion is to use this method with a dummy NSMutableURLRequest - and then, once it's finished, don't actually send the HTTP request, instead examine the headers it added and pull the access token from there.
See:
https://code.google.com/p/gtm-oauth2/wiki/Introduction#Using_the_Authentication_Tokens
Hope that helps - I don't have an iOS environment to test it on.

Error when trying to retrieve basic user information

I have successfully included the Facebook Login in my IOS app however i seem to be having some difficulty getting some of the users basic information such as name, email etc....
My current code looks like this:
// Ask for the required permissions
self.loginView.readPermissions = #[#"basic_info",
#"user_location",
#"user_birthday",
#"user_likes"];
// Fetch user data
[FBRequestConnection
startForMeWithCompletionHandler:^(FBRequestConnection *connection,
id<FBGraphUser> user,
NSError *error) {
if (!error) {
// Display the user info
tempLabel.text = user.name;
}
}];
However there is always an error (where the if statement checks for !error, its always equal to false).
Can someone help me in trying to get this please?
Thanks,
Jake
EDIT:
2013-08-04 15:52:03.457 Ludis[37821:c07] {
"com.facebook.sdk:HTTPStatusCode" = 400;
"com.facebook.sdk:ParsedJSONResponseKey" = {
body = {
error = {
code = 2500;
message = "An active access token must be used to query information about the current user.";
type = OAuthException;
};
};
code = 400;
};
}
2013-08-04 15:52:03.458 Ludis[37821:c07] The operation couldn’t be completed. (com.facebook.sdk error 5.)
You haven't obtained the user's permission to perform the action you're attempting to perform. First call
openActiveSessionWithReadPermissions:allowLoginUI:completionHandler:
on FBSession. Here's some more information about how to log in to Facebook from iOS

Resources