Saving AccessToken to Keychain for Google Drive API - ios

I am using the Google-API-Client and the gtm-oauth2 libraries. When the logon successfully completes
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)authResult
error:(NSError *)error {
I am handed back an access token in authResult.authResult
However if I look at what is saved to my keychain either automatically or using
[GTMOAuth2ViewControllerTouch saveParamsToKeychainForName:kGoogleKeychainItemName authentication:authResult];
I do not see that the access token is saved once I retrieve it. Only the refresh token is saved
If I immediately or any other time do
GTMOAuth2Authentication *auth =
[GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kGoogleKeychainItemName
clientID:kGoogleClientID
clientSecret:kGoogleClientSecret error:&error];
My access Token is nil
I assume that this is deliberate but I cannot see any documentation on this. Why does this happen and what is the thinking behind it.

There are some variants why data is not saving:
Possible the method (authForGoogleFromKeychainForName ) is not supported.
Possible if a scanner of finger print is existed in the phone, there should be additional feature in settings for using Keychain - we have some problems with the saving on the phone5 where fingerprint is existed.

The reason only the Refresh Token is used from the keychain is that this simplifies the OAuth2 process.
We do not have to verify that the Access token is still valid.
It makes the process easier to program and the cost is negligible.

Related

How to access id_token in MSGraph SDK on iOS?

I'm using MSGraph SDK iOS to login users with their Office365 accounts.
My end goal is to get:
refresh token
user details
id token (id_token as described in Azure Active Directory (AAD) endpoint documentation)
Using sample app I was able to login user, get a refresh token and user details (via [[[[MSGraphClient client] me] request] getWithCompletion:...]) but I can't find id_token anywhere.
Also, the Android version of the app I'm working on is using https://outlook.office.com/api/v2.0 as a base URL for SDK and I'm trying to change it also in iOS app from https://graph.microsoft.com/v1.0/.
I might be missing something obvious or important in docs or sample app, sorry if that's the case.
Question is: how to get id_token in MSGraphSDK on iOS?
UPD:
Here's code I'm using:
[NXOAuth2AuthenticationProvider setClientId:<clientId> scopes:#[#"https://graph.microsoft.com/Files.ReadWrite", #"https://graph.microsoft.com/Calendars.ReadWrite", #"openid"]];
[[NXOAuth2AuthenticationProvider sharedAuthProvider] loginWithViewController:nil completion:^(NSError *error) {
if (error) {
return;
}
NSArray *accounts = [[NXOAuth2AccountStore sharedStore] accountsWithAccountType:#"MSGraph"];
NXOAuth2Account *account = accounts.firstObject;
}];
NXOAuth2Account doesn't have any property that can be connected with id_token. The question is how to get id_token from NXOAuth2Client or MSGraphSDK frameworks?
An id_token is only returned if you're using the OpenID Connect flow. To enable this, you need to add openid to the list of scopes you're requesting and id_token+code as your response-type.
Optionally you can also request email and profile if you want to get a more "complete" id_token.
One additional item to keep in mind, the tokens are not provided by the SDK. They are obtained from Azure Active Directory via OAuth. The SDK simply takes the token you've previously obtained.
You can obtain a token using just about any library that supports OAuth 2.0 but I suspect the sample you're looking at is using NXOAuth2AuthenticationProvider. Something along the lines of:
[NXOAuth2AuthenticationProvider setClientId:<clientId>
scopes:#[#"https://graph.microsoft.com/Files.ReadWrite",
#"https://graph.microsoft.com/Calendars.ReadWrite"]];
In order to obtain the id_token using the above example, you add the openid scope:
[NXOAuth2AuthenticationProvider setClientId:<clientId>
scopes:#[#"https://graph.microsoft.com/Files.ReadWrite",
#"https://graph.microsoft.com/Calendars.ReadWrite",
#"openid"]];

How to use the box-ios-sdk with predefined access token and refresh token?

I am writing a framework that wraps functionalities of Box, for that I am using the box-ios-sdk. But all of their authentication methods uses their own flow. I have to input the appID and appSecret and then start the process to get an access token that is managed internally by the sdk.
The deal is that I already have my own OAuth2 Flow implemented, I already have the accessToken and the refresh token for the boxAPI. So how to use (or initialize somehow) the box-ios-sdk with the tokens that I already have?
Example:
[[BOXOAuth2Session alloc] initWithAccesstoken:(NSString *) refreshToken:(NSString *) expireDate:(NSDate *)];
or
[[BoxClient alloc] initWithAccesstoken:(NSString *) refreshToken:(NSString *) expireDate:(NSDate *)];
I`ve been digging into the documentation and the source code, but still have not found any viable way to do it.
Assuming you have completely taken over the oAuth flow, including the refreshing of access tokens at the necessary times, you might be able to use the "App Users" mode of the SDK.
Here is the documentation for that:
https://github.com/box/box-ios-sdk/blob/master/doc/AppUsers.md
In this mode, you basically just need to set an "BOXAPIAccessTokenDelegate" on the BOXContentClient that you're working with, and then implement the "fetchAccessTokenWithCompletion:" delegate method to provide your own access token. In this mode, you just need to ensure that your implementation of "fetchAccessTokenWithCompletion:" always returns a valid access token.

Error fetching OAuth2 access token - invalid_grant. BUT it is VALID

Attempting to authenticate using Google on iOS with backend server, code used to work with older google client libraries.
$client = new Google_Client();
$oauth2 = new Google_Service_Oauth2($client);
$client->setApplicationName('MyApp');
$client->setClientId($google_config["clientId"]);
$client->setClientSecret($google_config["clientSecret"]);
$client->setRedirectUri("");
$client->setDeveloperKey($google_config["developerKey"]));
Log::error("Google/authcode", $request->get('token'));
if (! isset($_SESSION['access_token'])) {
try {
$client->authenticate($request->get('token'));
} .... THROWS exception: Error fetching OAuth2 access token, message: 'invalid_grant:
Attempting to validate token against:
https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=
https://www.googleapis.com/oauth2/v2/tokeninfo?id_token=
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=
all work successfully shows that token is valid.
EDIT:
JUST FOUND OUT after adding verifyIdToken call, that:
Invalid issuer, https://accounts.google.com != accounts.google.com seems like this is why I am getting a failure. I am guessing that iOS sdk vs PHP is setting things up differently? Or perhaps this is not relevant and is assumed that it is not authorization of the server from iOS device. Since if I keep changing things on server side code i.e. chang OAUTH2_ISSUER to be https://accounts.google.com, I would get wrong recipient error, which makes sense since I am using tokenid obtained by iOS client to authorize server.
Older version of iOS app works fine as well. But token is completely different although validates just fine as well.
Any help is greatly appreciated.
I think I misunderstood documentation. In order to authorize server one needs to pass googleUser.serverAuthCode from
- (void)signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)googleUser
withError:(NSError *)error { ....
If only validating backend server with authorized on iOS side user googleUser.authentication.accessToken to the server which in turn would need to be validated or parsed on the server to get email and other data.

iOS authentication for file upload?

I'm struggling to figure out how to use my Box authentication tokens to use the Box API. I've built the authentication flow into my app so that I can save away the relevant pieces (access token, refresh token, etc.) to the Keychain. The issue I'm having is that whenever I re-open the app, I can't seem to find an appropriate way to set up my BoxOAuth2Session or whatever to re-use the saved tokens to upload files to Box. Currently, I'm recreating the BoxOAuth2Session with my clientID and secret, and manually setting the accessToken, refreshToken, etc. values on that session. I create a BoxFilesResourceManager, attach this BoxOAuth2Session, and upload a file with uploadFileWithInputStream. This request always fails with a 401. The only way I have been able to upload files to Box is immediately following the login step using the [BoxSDK sharedSDK].filesManager. What is the expected workflow for re-creating the OAuth state to access the API?
A BoxOAuth2Session is bound to an SDK instance. When you access the [BoxSDK sharedSDK] singleton, you are using an instance of the SDK that is already wired up with its own BoxOAuth2Session and manager instances. In normal usage, we recommend using the sharedSDK singleton, so you should manipulate the BoxOAuth2Session attached to this SDK.
One way to do this is to attempt to load a refresh token from the keychain and set the refreshToken property on the OAuth2Session.
[BoxSDK sharedSDK].OAuth2Session.clientID = #"YOUR_CLIENT_ID";
[BoxSDK sharedSDK].OAuth2Session.clientSecret = #"YOUR_CLIENT_SECRET";
// set up stored OAuth2 refresh token
self.keychain = [[KeychainItemWrapper alloc] initWithIdentifier:REFRESH_TOKEN_KEY accessGroup:nil];
id storedRefreshToken = [self.keychain objectForKey:(__bridge id)kSecValueData];
if (storedRefreshToken)
{
[BoxSDK sharedSDK].OAuth2Session.refreshToken = storedRefreshToken;
}
The SDK will automatically refresh the OAuth2 session and acquire a new access token and refresh token on the next API call, so long as the refresh token has not been revoked and is not expired. You may wish to manually trigger a heartbeat call to force a refresh.
We've put together a sample app that demonstrates how to store and load refresh tokens using the keychain.
As a side note, we do not recommend storing the access token on the device since this token is a bearer token; losing this token could allow Mallory to impersonate your app's users.

mgtwitterengine and oauth 401 error: Boggled

OK... so here is my code:
twitterEngine = [[MGTwitterEngine alloc] initWithDelegate:self];
[twitterEngine setConsumerKey:CONSUMER_KEY secret:CONSUMER_SECRET];
accessToken = [twitterEngine getXAuthAccessTokenForUsername:profile.twitterUserId password:profile.twitterPassword];
NSLog(#"Access token: %#", accessToken);
the console shows the access token returned just fine (so it seems to work)
eg. Access token: C8A24515-0F11-4B5A-8813-XXXXXXXXXXXXXX
but instead of accessTokenReceived method being called next on my delegate, it calls requestFailed with a 401. How can I be getting a 401 unauthorized and getting an access token back from the method call?????
xAuth, the process for exchanging a login and password for an access token, is a privilege for applications that verifiably meet Twitter's criteria: desktop or mobile applications that are otherwise unable to provide the entire three-legged OAuth flow. Out-of-band OAuth and custom URI schemes are still preferred over xAuth.
If you've exhausted other OAuth implementations and want to use xAuth, you can contact Twitter through api#twitter.com from an email address directly associated with the account owning the application. Include full details about your application, its user base, links to screenshots of it in action, and a detailed description on why this form of authorization is appropriate for your application. Inquires for xAuth are considered on a case-by-case basis and will not be granted to all applicants.
Implementors of xAuth must not store logins and passwords within their applications -- this is not a drop-in replacement for basic auth or a way to avoid implementing OAuth authentication.
Found the issue... for anyone else that has this problem... Getting your app approved for OAuth is only part of the process. Although it looks like you are done and the twitter page gives you your key and secret... there is one not-quite-so-easy-to-find next step. You must send an email to api#twitter.com and ask them to actually enable it.
That was fun figuring out. :)

Resources