How to limit Google Drive App Permissions at login - ios

I am using the standard iOS Google Drive SDK login mechanism with scope kGTLAuthScopeDrive (or "https://www.googleapis.com/auth/drive"). During login, after providing the user name and password, the view window shows:
This app would like to:
(1) Know who you are on Google
(2) View your email address
(3) View and manage the files and documents in your Google Drive
Some other text.
Cancel Button Accept Button
The two buttons fall off the bottom of an iPhone 5 screen and need to be scrolled up to be seen and tapped. I don't need to know "who you are on Google" or "your email address". Is there a scope I can use that only manages Google Drive files so that the Accept button will appear on the initial view without needing to scroll up? Or, is there some other way to auto-scroll to show the buttons?

Unfortunately there is not a scope for what you are looking for. If you look at the Drive 'About' request, you'll see why it results in the first 2 notes. There is not a scope that allows you to manage files and not see this information.
"kind": "drive#user",
"displayName": string,
"picture": {
"url": string
},
"isAuthenticatedUser": boolean,
"permissionId": string,
"emailAddress": string

Commenting out line 142 in GoogleAPI file "GTMOAuth2SignIn.m" results in the desired UI effect. Unfortunately, the login fails and a long error message is displayed. Perhaps someone with more knowledge of the GoogleAPI signin process can make it work. Line 142 and the full procedure are as follows:
//scope = [GTMOAuth2Authentication scopeWithStrings:scope, emailScope, nil];
- (void)addScopeForGoogleUserInfo {
GTMOAuth2Authentication *auth = self.authentication;
if (self.shouldFetchGoogleUserEmail) {
NSString *const emailScope = #"https://www.googleapis.com/auth/userinfo.email";
NSString *scope = auth.scope;
if ([scope rangeOfString:emailScope].location == NSNotFound) {
//scope = [GTMOAuth2Authentication scopeWithStrings:scope, emailScope, nil];
auth.scope = scope;
}
}
if (self.shouldFetchGoogleUserProfile) {
NSString *const profileScope = #"https://www.googleapis.com/auth/userinfo.profile";
NSString *scope = auth.scope;
if ([scope rangeOfString:profileScope].location == NSNotFound) {
scope = [GTMOAuth2Authentication scopeWithStrings:scope, profileScope, nil];
auth.scope = scope;
}
}
}

Related

How to open salesforce login screen within the app

I am using salesforce oauth connected app setup. When I launch the app it redirects me to login in the safari in app browser. How to control this? Do I need to use any salesforce library like zksforce.
I followed these steps to create a project: https://developer.salesforce.com/docs/atlas.en-us.mobile_sdk.meta/mobile_sdk/ios_new_force_project.htm
Please suggest me which is the right way to display login screen in a native app.
There is no way to open login screen native -as far as I know :)-
You should set properties first as :
[SalesforceSDKManager sharedManager].connectedAppId = RemoteAccessConsumerKey;
[SalesforceSDKManager sharedManager].connectedAppCallbackUri = OAuthRedirectURI;
[SalesforceSDKManager sharedManager].authScopes = #[ #"web", #"api" ];
you can set callback properties if necessary :
[SalesforceSDKManager sharedManager].postLaunchAction = ^(SFSDKLaunchAction launchActionList) {
};
[SalesforceSDKManager sharedManager].launchErrorAction = ^(NSError *error, SFSDKLaunchAction launchActionList) {
};
And Then you should call :
[[SalesforceSDKManager sharedManager] launch];

Upload Youtube video with "fixed" token in Google APIs Client Library for Objective-C for REST

I'm trying to create an app that upload video on a specified channel, without prompt a login page. I'll try to explain better what i need.
I'm using Google APIs Client Library for Objective-C for REST, with this library i can use a "standard" upload flow :
user record a video -> he press an upload button -> Safari open the login google page -> user login in his own account and give permission to the app -> Safari redirect back to the ios app -> the upload process begin -> the video will be uploaded on the personal user channel.
Instead this is the desired workflow of my ios app:
user record a video -> he press an upload button -> the video will be uploaded in the app's youtube channel.
The only help i had find is this article ,it explain a way to obtain a refreshable app token to upload video in an app channel. It is exactly what i need. Anyway this is a web example, it uploads videos that are in a server. My videos are in the phone, so i think that i have to modify the flow of this article in this way:
obtain token the first time by login as channel owner -> create a token.txt and save it in my server -> create a page called get_token.php that print the content of token.txt and refresh it if the token expire.
With this flow in my app i need this other flow:
user record a video -> press an upload button -> i made a call to get_token.php and retrive the actual token -> i made a call by library with the token retrived to upload the video on the app's youtube channel.
Here i have found some problems, this is my authentication methods :
#pragma mark - Sign In
- (void)authNoCodeExchange {
[self verifyConfig];
NSURL *issuer = [NSURL URLWithString:kIssuer];
[self logMessage:#"Fetching configuration for issuer: %#", issuer];
// discovers endpoints
[OIDAuthorizationService discoverServiceConfigurationForIssuer:issuer
completion:^(OIDServiceConfiguration *_Nullable configuration, NSError *_Nullable error) {
if (!configuration) {
[self logMessage:#"Error retrieving discovery document: %#", [error localizedDescription]];
return;
}
[self logMessage:#"Got configuration: %#", configuration];
if (!kClientID) {
[self doClientRegistration:configuration
callback:^(OIDServiceConfiguration *configuration,
OIDRegistrationResponse *registrationResponse) {
[self doAuthWithoutCodeExchange:configuration
clientID:registrationResponse.clientID
clientSecret:registrationResponse.clientSecret];
}];
} else {
[self doAuthWithoutCodeExchangeCri:configuration clientID:kClientID clientSecret:nil];
}
}];
}
/////////////////
- (void)doAuthWithoutCodeExchangeCri:(OIDServiceConfiguration *)configuration
clientID:(NSString *)clientID
clientSecret:(NSString *)clientSecret {
NSURL *redirectURI = [NSURL URLWithString:kRedirectURI];
OIDTokenRequest *tokenExchangeRequest =
[_authState.lastAuthorizationResponse tokenExchangeRequest];
[OIDAuthorizationService performTokenRequest:tokenExchangeRequest
callback:^(OIDTokenResponse *_Nullable tokenResponse,
NSError *_Nullable error) {
if (!tokenResponse) {
[self logMessage:#"Token exchange error: %#", [error localizedDescription]];
} else {
[self logMessage:#"Received token response with accessToken: %#", tokenResponse.accessToken];
}
[_authState updateWithTokenResponse:tokenResponse error:error];
GTMAppAuthFetcherAuthorization *gtmAuthorization =
[[GTMAppAuthFetcherAuthorization alloc] initWithAuthState:authState];
// Sets the authorizer on the GTLRYouTubeService object so API calls will be authenticated.
self.youTubeService.authorizer = gtmAuthorization;
// Serializes authorization to keychain in GTMAppAuth format.
[GTMAppAuthFetcherAuthorization saveAuthorization:gtmAuthorization
toKeychainForName:kGTMAppAuthKeychainItemName];
[self uploadVideoFile];
}];
}
i have also something like:
NSString * RetrivedToken = #"ya29xxxxxxx3nJxxxxxxx6qqQ-FxxxxxxxdGH";
How i can modify those methods to accept my retrivedtoken instead the one they retrive from the standard auth workflow?
This isn't really an answer but its going to be to big for a comment.
So you want to create an app that will allow others to upload to your youtube channel. Normally i would say use a service account which would allow you to do this much easier. However the YouTube api does not support service accounts.
You are going to need to authenticate the app once save your refresh token then embed this refresh token in your app.
Why this is not a good idea with a mobile app.
Refresh tokens can expire so if it does expire or break then your going to have to authenticate the app again and embed the new refresh token into your app and release a new version to your users.
An alternative would be to set up a web service with the refresh token on that and have your app access the web service for upload. Then if the refresh token breaks you will only have to fix it on your web service.
I have done this before its messy but there is really no other way of doing it.
You can see from your code
if (!tokenResponse) {
[self logMessage:#"Token exchange error: %#", [error localizedDescription]];
} else {
[self logMessage:#"Received token response with accessToken: %#", tokenResponse.accessToken];
}
your are using OIDTokenResponse object tokenResponse in your further code.
First make an object of Class OIDTokenResponse like *oidTokenResponse and assign it your access token from your server like
oidTokenResponse.accessToken=YOUR_TOKEN_FROM_SERVER;
and then use
[_authState updateWithTokenResponse:oidTokenResponse error:error];
In other way you can also do that and you won't need to change your code much you can do it with one line of code but i'm not sure you are allow to change to access token here but you can try it.
Leave the other code and just add one more line in your else
if (!tokenResponse) {
[self logMessage:#"Token exchange error: %#", [error localizedDescription]];
} else {
[self logMessage:#"Received token response with accessToken: %#", tokenResponse.accessToken];
tokenResponse.accessToken=YOUR_TOKEN_FROM_SERVER;
}

Autofill Username and Password in UIWebView

I'm doing a very easy app for me. When I launch this app, it simply bring me to this web page https://social.tre.it/expert.
I would like to automatise login so is there a way to autofill username and password, check the "I accept condition" mark and push "login" button?
I tried this Autofill Username and Password UIWebView Swift but it doesn't work, I'm not the creator of the page so I don't know exactly how it's structured.
Would be cool even if iCloud keychains would fill the forms...but maybe I'm asking too much!
You can use JavaScript to do it.
If you inspect the page, you can pretty easily spot the IDs of the text input fields (expert_email and expert_password) and execute some JS code to fill them.
Implement the webViewDidFinishLoad method of the UIWebView delegate like so:
OBJECTIVE-C:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
//fill data
NSString *email = #"myemail#email.com";
NSString *password = #"mypassword";
NSString *fillDataJsCall = [NSString stringWithFormat:#"document.getElementById('expert_email').value = '%#';document.getElementById('expert_password').value = '%#';", email, password];
[webView stringByEvaluatingJavaScriptFromString:fillDataJsCall];
//check checkboxes
[webView stringByEvaluatingJavaScriptFromString:#"document.getElementById('expert_remember_me').checked = true; document.getElementById('expert_terms_of_service').checked = true;"];
//submit form
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void)
{
[webView stringByEvaluatingJavaScriptFromString:#"document.forms[\"new_expert\"].submit();"];
});
}
SWIFT:
func webViewDidFinishLoad(webView: UIWebView) {
// fill data
let savedUsername = "USERNAME"
let savedPassword = "PASSWORD"
let fillForm = String(format: "document.getElementById('expert_email').value = '\(savedUsername)';document.getElementById('expert_password').value = '\(savedPassword)';")
webView.stringByEvaluatingJavaScriptFromString(fillForm)
//check checkboxes
webView.stringByEvaluatingJavaScriptFromString("document.getElementById('expert_remember_me').checked = true; document.getElementById('expert_terms_of_service').checked = true;")
//submit form
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(1 * NSEC_PER_SEC)), dispatch_get_main_queue()){
webView.stringByEvaluatingJavaScriptFromString("document.forms[\"new_expert\"].submit();")
}
}
This should fill your info. However, this code can easily break if this website changes its structure in the future. This method of executing JS code is more reasonable when you're handling your own pages.
Edit:
I updated the code to demonstrate how to check the checkboxes and how to submit the form.
Some notes:
when the login fails the page is reloaded, so you're going to end up with an endless loop of calls to webViewDidFinishLoad since you're trying to submit over and over again, so you might want to add some logic to break in this case. That's why I put a delay before submitting in order to be able to see what's going on.
In addition to the info in the previous point - you're going to get calls to webViewDidFinishLoad anyway after login is successful (when redirected to the next page) so you might want to raise a flag once a different page loads (perform the login attempt only when on the login page).

iOS - programatically rejecting permission to contacts (after user has accepted)

Is there a way to programatically reject/disable/disallow/reject permissions to access the calendar?
I'm using the following code to ask for / determine permissions:
//request access
[[MyCalendarUtil sharedManager] requestAccess:^(BOOL granted, NSError *error) {
/* This code will run when uses has made his/her choice */
if(error)
{
// display error message here
_calendarLabel.text = #"Calendar OFF";
}
else
if(!granted)
{
// display access denied error message here
_calendarLabel.text = #"Calendar OFF";
}
else
{
// access granted
_calendarLabel.text = #"Calendar ON";
}
}];
Can I disable the permission later on upon a user pressing a button ... ?
No way, its impossible. Take it granted. I will give 1 Million dollars even if you find a private API. :)
Its a privacy concern.
All you can do is, programmatically launching your application's preferences in the Settings App, and again its users wish to disable or enable the access.

iOS Facebook Native Share Dialog : initial text

I'm using Facebook Native Share Dialog in my iOS app. I give the initial text, but when dialog pop up, it add the post URL to my initial text. How can I solve this problem? Here the code.
BOOL displayedNativeDialog =
[FBNativeDialogs
presentShareDialogModallyFrom:self
initialText:#"Say something about this..."
image:[UIImage imageWithData:imageData]
url:[NSURL URLWithString:activityUrl]
handler:^(FBNativeDialogResult result, NSError *error) {
if (error) {
} else {
if (result == FBNativeDialogResultSucceeded) {
} else {
}
}
}];
If you're sharing a URL (link share) and the image is inside that link, then just set the image parameter to nil. Then the post URL will not be added to the initial text. If you provide an image, it's as if you're sharing a photo and adding the link (and text) as photo caption info.
So based on what you're trying to do, select the option that works for you, i.e. is it a link share or a photo share.

Resources