Twitter integrated in Iphone App - iphone

I used MGTwitterEngine
which try to login then request fail error comes
Error Detail
connectionIdentifier 6260FAFD-AE4E-4F05-AE67-FFA1DA18578F
error description The operation couldn’t be completed. (HTTP error 401.)
error user info
({
body = "\n\n Client application is not permitted to use xAuth.\n /oauth/access_token\n\n";
response = "";
})
Any Idea
-Amit Battan

Basic authentication is no longer supported by Twitter. You can use Twitter-oAuth-iPhone. It added oAuth with MGTwitterEngine. There is a demo code project included too. However, you will require SDK >= 3.2 to compile the latest version.
The problem with xAuth that you need to request to the Twitter API team providing detail of the application. In case of oAuth that is not required. You will need to create an application in Twitter Dev and then you need to use the keys in the project. That's all.

MGTwitterEngine is significantly outdated at this point, as it doesn't use xAuth/oAuth. See this tutorial on using MGTwitterEngine with xAuth:
Switching from Basic to xAuth with mgtwitterengine on iPhone

The OAuth do not need the permission from Twitter. But XAuth is required.
I'm not using MGTwitterEngine, with the official document of Twitter, I scratched a client with C, to calculated the nonce and signature, then use the tool cURL, then I can get the oauth_token and token_secret.
Additional message about cURL is that you must have created your App in Twitter, you can use the OAuth tool in your App(not required to be the current App your're working on) setting. If the App in twitter is not your current App, you should generate the cURL command line first. Set the Request Settings to POST and the Request URI to http://api.twitter.com/oauth/request_token, leave other setting as default, then click the button on the page bottom, you then see the cURL command, it seems like that:
curl --request 'POST' 'http://api.twitter.com/oauth/request_token' --header 'Authorization: OAuth oauth_consumer_key="your_consumer_key", oauth_nonce="your_random_string", oauth_signature="your_signature", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1329273306", oauth_token="451145411-AxGXG8xNdW1Eymwx85hs3sc1OE3NYsXe578AUtPe", oauth_version="1.0"' --verbose
replace the oauth_consumer_key , oauth_timestamp and oauth_signature with yours, delete the oauth_token and its value, copy the command to your cmd or shell and execute it. Another important trick is that, you must explicitly mark the oauth_callback in Authorization, and set the value as oob:
auth_callback="oob"
If your consumer_key and oauth_signature is correct, this steps will not lead to the Client application is not permitted to use xAuth error.

#taskinoor
there the code I am using ...
I have create application on twitter already
Controller .h file
#import <UIKit/UIKit.h>
#import "MGTwitterEngine.h"
#class OAToken;
#interface TwitterTestViewController : UIViewController <MGTwitterEngineDelegate> {
MGTwitterEngine *twitterEngine;
OAToken *token;
}
#end
Controller .m file
#import "TwitterTestViewController.h"
#implementation TwitterTestViewController
- (void)awakeFromNib{
// Put your Twitter username and password here:
NSString *username = #"amit_bursys";
NSString *password = #"mypassword";
NSString *consumerKey = #"my_key";
NSString *consumerSecret = #"my_seret";
if (! username || ! password || !consumerKey || !consumerSecret) {
NSLog(#"You forgot to specify your username/password/key/secret in AppController.m, things might not work!");
NSLog(#"And if things are mysteriously working without the username/password, it's because NSURLConnection is using a session cookie from another connection.");
}
twitterEngine = [[MGTwitterEngine alloc] initWithDelegate:self];
[twitterEngine setUsesSecureConnection:NO];
[twitterEngine setConsumerKey:consumerKey secret:consumerSecret];
[twitterEngine setUsername:username];
[twitterEngine getXAuthAccessTokenForUsername:username password:password];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
}
- (void)dealloc {
[super dealloc];
}
#pragma mark MGTwitterEngineDelegate methods
- (void)requestSucceeded:(NSString *)connectionIdentifier{
NSLog(#"Request succeeded for connectionIdentifier = %#", connectionIdentifier);
}
- (void)requestFailed:(NSString *)connectionIdentifier withError:(NSError *)error{
NSLog(#"Request failed for connectionIdentifier = %#, error = %# (%#)",
connectionIdentifier,
[error localizedDescription],
[error userInfo]);
}
- (void)statusesReceived:(NSArray *)statuses forRequest:(NSString *)connectionIdentifier{
NSLog(#"Got statuses for %#:\r%#", connectionIdentifier, statuses);
}
- (void)directMessagesReceived:(NSArray *)messages forRequest:(NSString *)connectionIdentifier{
NSLog(#"Got direct messages for %#:\r%#", connectionIdentifier, messages);
}
- (void)userInfoReceived:(NSArray *)userInfo forRequest:(NSString *)connectionIdentifier
{
NSLog(#"Got user info for %#:\r%#", connectionIdentifier, userInfo);
}
#end
and Code log is
2010-12-21 11:14:31.533 TwitterTest[734:207] Request failed for connectionIdentifier = D28BD476-7315-4510-B51A-968E516D169D, error = The operation couldn’t be completed. (HTTP error 401.) ({
body = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n <request>/oauth/access_token</request>\n <error>Client application is not permitted to use xAuth.</error>\n</hash>\n";
response = "<NSHTTPURLResponse: 0x6022470>";
})

Related

GTM OAuth2 retrieving access_token via Keychain not successful

I'm following Google's examples on how to authorize an app to access one or more APIs.
The problem is that when i authorise successfully i get the access_token, but after this i can't get it from the keychain it is stored into. I read somewhere that iPhone Simulator doesn't work with Keychain, is it because of that, and if it is can you tell me some other way to store my access token?
Here is my code:
static NSString *const kKeychainItemName = #"OAuthGoogleReader";
GTMOAuth2Authentication *auth;
auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName
clientID:kClientID
clientSecret:kClientSecret];
BOOL isSignedIn = [auth canAuthorize];
if (isSignedIn) {
NSLog(#"Signed");
self.window.rootViewController = self.viewController;
auth.accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:#"accessToken"];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.google.com/reader/api/0/subscription/list?access_token=%#", [auth accessToken]]]];
GTMHTTPFetcher* myFetcher = [GTMHTTPFetcher fetcherWithRequest:request];
// optional upload body data
//[myFetcher setPostData:[postString dataUsingEncoding:NSUTF8StringEncoding]];
[myFetcher setAuthorizer:auth];
[myFetcher beginFetchWithDelegate:self
didFinishSelector:#selector(myFetcher:finishedWithData:error:)];
// - (void)myFetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)retrievedData error:(NSError *)error;
}else{
NSString *scope = #"https://www.google.com/reader/api/";
GTMOAuth2ViewControllerTouch *viewController;
viewController = [[GTMOAuth2ViewControllerTouch alloc] initWithScope:scope
clientID:kClientID
clientSecret:kClientSecret
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
self.window.rootViewController = viewController;
}
I get error:
2012-08-22 16:54:47.253 greader[20833:c07] Signed
2012-08-22 16:54:47.705 greader[20833:c07] Cannot authorize request with scheme http (<NSMutableURLRequest http://www.google.com/reader/api/0/subscription/list?access_token=(null)>)
as you can see access_token is just nil.
Also some simple examples on how to use this library would be great.
Thank you!
The gtm-oauth2 library handles storing and retrieving the access token and other auth values on the keychain. The app should not need to use the access token string directly, nor should the app put the authorization tokens into NSUserDefaults, as that is insufficiently secure.
gtm-auth2 also by default will refuse to attach an access token to a URL with an http: scheme. OAuth 2 is secure only when used with https: scheme URLs.

Self login in Salesforce API in iPhone?

I am using Salesforce api and i want to login automatically(hard code user name and password). i am using REST API and here is the login code which shows login form:
- (void)login {
SFOAuthCredentials *credentials = [[[SFOAuthCredentials alloc] initWithIdentifier:remoteAccessConsumerKey] autorelease];
credentials.protocol = #"https";
credentials.domain = OAuthLoginDomain;
credentials.redirectUri = OAuthRedirectURI;
self.coordinator = [[[SFOAuthCoordinator alloc] initWithCredentials:credentials] autorelease];
self.coordinator.delegate = self;
NSLog(#"%#",self.coordinator);
// remove this line if we want to cache the key, and use refresh flow
//effectively, we are saying, purge the old login and re-authenticate each time
[self.coordinator revokeAuthentication];
//now let's authenticate
[self.coordinator authenticate];
}
What i want, to automatically login (not ask username or password) so where i insert user name and password?
The Salesforce toolkit for iOS page shows an example of it using ZKSForce which returns a token that can be used by the REST API calls, see the document here
The REST API typically uses an OAuth token which saves the user from having to enter their username and password into a third party system such as yours. They enter the login once and the system refreshes the token to stay logged in (this is an oversimplified explanation). Either use OAuth which is the correct way with mobile and the REST API or use the session ID from an old style SOAP login request.
A quick Google and I found this - https://github.com/superfell/zkSforce
Haven't tried it yet though!
Include zkforce in your project
Add These files in your project
#import "ZKSforce.h"
#import "FDCServerSwitchboard.h"
#import "ZKLoginResult.h"
Add this code in .m
NSString *username =
NSString *password =
NSString *token = #"amnwcg24Uu5IenCvAJM5HgRq";
NSString *passwordToken = [NSString stringWithFormat:#"%#%#", password, token];
[[FDCServerSwitchboard switchboard] loginWithUsername:username password:passwordToken target:self selector:#selector(loginResult:error:)];
See result
- (void)loginResult:(ZKLoginResult *)result error:(NSError *)error
{
if (result && !error)
{
NSLog(#"Hey, we logged in!");
//[self fetchAccounts];
}
else if (error)
{
NSLog(#"An error occurred while trying to login.");
}
}

How to Authenticate a Microsoft SharePoint site using REST API from an iOS application

I came to know that we could use REST apis to get data from SharePoint sites. Also SharePoint supports REST from 2010 onwards. I got the API for listing the data and its details from SharePoint. It is "ListData.svc". Is there any other APIs similar to that using which we could authenticate our site. I tried it browser(listdata.svc) and before that I was logged in. If I am logged out and do the "siteUrl/_vti_bin/ListData.svc", I am not able to get the result, request getting timed out or sometimes it shows Webpage is not available. If anyone know how to SharePoint stuffs in iPhone application, please share something on the same.
Read this: http://sharepointsemantics.com/2011/07/the-client-side-object-model-help-with-headless-authentication-in-sharepoint-online/ Make sure you read the linked articles written by Chris Johnson, following the information there should solve your authentication woes.
Sidenote, you pretty much HAVE to use forms authentication on the SharePoint end.
Here is the way I have done this for NTLM authentication to SharePoint 2010 over http. It works and returns JSON dictionary from any call to the listdata.svc (e.g. call to URL yourdomain/_vti_bin/listdata.svc/YourList):
Grab AFNetworking and follow the instructions to get it into your XCode application.
When you have AFNetworking compiling in your project you need to subclass AFHTTPClient class of AFNetworking framework. E.g. add a new class to your iOS XCode project and choose AFHTTPClient as it's object type.
Once you have subclassed you get something like the following:
YourHTTPClient.h
#import "AFHTTPClient.h"
#import "AFJSONRequestOperation.h"
#import "AFNetworkActivityIndicatorManager.h"
typedef void(^OLClientSuccess) (AFJSONRequestOperation *operation, id responseObject);
typedef void(^OLClientFailure) (AFJSONRequestOperation *operation, NSError *error);
#interface OLHTTPClient : AFHTTPClient
{
NSString *strBASEURL;
NSString *strUser;
NSString *strPassword;
}
#property (nonatomic, retain) NSString *strUser;
#property (nonatomic, retain) NSString *strPassword;
#property (nonatomic, retain) NSString *strBASEURL;
+(id) sharedClient;
- (void)setUsername:(NSString *)username andPassword:(NSString *)password;
-(void) getStuff:(OLClientSuccess) success failure:(OLClientFailure) failure;
#end
The in your YourHTTPClient.m file you could have the code below but in this .m file is where you will implement your custom method calls to get your list data from SharePoint. See below:
Code fragment for authentication from YourHTTPClient.m:
- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSURLRequest *request = [self requestWithMethod:#"GET" path:path parameters:parameters];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[operation setAuthenticationChallengeBlock:^(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge) {
NSURLCredential *newCredential = [NSURLCredential credentialWithUser:self.User password:self.password persistence:NSURLCredentialPersistenceForSession];
[challenge.sender useCredential:newCredential forAuthenticationChallenge:challenge];
}];
[self enqueueHTTPRequestOperation:operation];
}
#end
I have only tried the above with a SharePoint 2010 environment configured to authenticate using NTLM. It may need reconfiguration if you need to authenticate using Kerberos but I suspect it is possible also using AFNetworking.
Look at this project, it supports SharePoint 2013 RestAPI.Its working for me, and very sure it will work for you also.
https://github.com/jimmywim/SPRestAPI
By default the SPRESTQuery provides the response in XMl, If you want response in json you will have to write this line in executeQuery Method.
[apiRequest setValue:#"application/json;odata=verbose" forHTTPHeaderField:#"Accept"];

Using tweet search with Twitter+OAuth on iPhone

Originally I needed the ability to use the search API with twitter. I did this using Matt Gemmell's great MGTwitterEngine. That code was very very simple and looked something like this:
- (void)viewDidLoad {
[super viewDidLoad];
tweetArrays = nil;
tweetNameArray = nil;
NSString *username = #"<username>";
NSString *password = #"<password>";
NSString *consumerKey = #"<consumerKey>";
NSString *consumerSecret = #"<consumerSecret>";
// Most API calls require a name and password to be set...
if (! username || ! password || !consumerKey || !consumerSecret) {
NSLog(#"You forgot to specify your username/password/key/secret in AppController.m, things might not work!");
NSLog(#"And if things are mysteriously working without the username/password, it's because NSURLConnection is using a session cookie from another connection.");
}
// Create a TwitterEngine and set our login details.
twitterEngine = [[MGTwitterEngine alloc] initWithDelegate:self];
[twitterEngine setUsesSecureConnection:NO];
[twitterEngine setConsumerKey:consumerKey secret:consumerSecret];
// This has been undepreciated for the purposes of dealing with Lists.
// At present the list API calls require you to specify a user that owns the list.
[twitterEngine setUsername:username];
[twitterEngine getSearchResultsForQuery:#"#HelloWorld" sinceID:0 startingAtPage:1 count:100];
}
This would end up calling the function:
- (void)searchResultsReceived:(NSArray *)searchResults forRequest:(NSString *)connectionIdentifier
And then I could do what I wanted with the searchResults. This required me to include the yajl library.
I then wanted to expand my code to allow users to tweet. I downloaded Ben Gottlieb's great code Twitter-OAuth-iPhone
So there's only one problem. The getSearchResultsForQuery returns a requestFailed with the following error:
Error Domain=HTTP Code=400 "The operation couldn’t be completed. (HTTP error 400.)"
To call this code I simply took the demo project in Twitter-OAuth-iPhone and added a call to getSearchResultsForQuery as seen here:
- (void) viewDidAppear: (BOOL)animated {
if (_engine) return;
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate: self];
_engine.consumerKey = kOAuthConsumerKey;
_engine.consumerSecret = kOAuthConsumerSecret;
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine: _engine delegate: self];
if (controller)
[self presentModalViewController: controller animated: YES];
else {
[_engine getSearchResultsForQuery:#"HelloWorld"];
// [_engine sendUpdate: [NSString stringWithFormat: #"Already Updated. %#", [NSDate date]]];
}
}
This as stated above returns a 400 error. Every other twitter API call I add here does work such as:
- (NSString *)getRepliesStartingAtPage:(int)pageNum;
Am I doing anything wrong? Or does getSearchResultsForQuery no longer work? The two code bases seem to use different versions of MGTwitterEngine, could that be causing the problem?
Thanks!
The problem is that you have to instantiate the twitter engine as an instance of SA_OAuthTwitterEngine, instead of MGTwitterEngine. When you call getSearchResultsForQuery, it uses a call to _sendRequestWithMethod to actually send the search request. SA_OAuthTwitterEngine overrides the MGTwitterEngine's implementation, but its version of that function doesn't work with getSearchResultsForQuery.
So, you need to go to getSearchResultsForQuery and make it use the MGTwitterEngine's version of the function.

Integrating iPhone Application with Shibboleth

Has anyone integrated an iPhone application with a Shibboleth Identity Provider? Googling did not come up with anything so I am asking the gurus directly.
If it has not been previously dones, is it feasible to do so?
The answer to both is "Yes."
I'm a Java guy, so being asked two weeks ago to:
Learn Objective-C
Write an native iPhone App
Authenticated programmatically with Shibboleth
Download an display Shibboleth protected datafile
...Was a little daunting. Compound that with the absence of any forum posts to help out has prompted me to share my experience.
Here's an overview followed by some hopefully very helpful sample code. Please vote for my answer if this helps! It worth a few weeks of my time :)
For an application on the iPhone to download Shibbolized resources, the following needs to happen:
Use the URL API's in Cocoa to submit the HTTP request for the resource in question.
Implement a delegate class for the request to:
Respond to the SP re-direct to the IdP (automatic courtesy of Cocoa)
Respond to server certificate trust challenges
Respond to user credential challenges
Respond to errors (if needed)
Receive IdP's "binding template" for the authenticated user, an HTML form which re-directs the user back to the SP with two parameters
Programmatically HTTP POST the two parameters from the IdP back to the SP.
Cookies are automatically stored and forwards courtesy of Cocoa again
Implement a second URL Request delegate to receive the originally request data.
Here are some useful references from Apple and Shibboleth:
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html
https://spaces.internet2.edu/display/SHIB2/IdPSPLocalTestInstall
And hopefully I can include all the source for a quick demonstration.
ApplicationDelegate.h
----------
#import <UIKit/UIKit.h>
#import "ConsoleViewController.h"
/*
The application delegate will hold references to the application's UIWindow and a ConsoleViewController.
The console does all of the interesting Shibboleth activities.
*/
#interface ApplicationDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
ConsoleViewController *consoleViewController;
}
#end
ApplicationDelegate.m
----------
#import "ApplicationDelegate.h"
#import "ConsoleViewController.h"
/*
The implementation for the ApplicationDelegate initializes the console view controller and assembles everything.
The console does all of the interesting Shibboleth activities.
*/
#implementation ApplicationDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Initialize the console.
consoleViewController = [[ConsoleViewController alloc] init];
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window setBackgroundColor:[UIColor lightGrayColor]];
[window addSubview:[consoleViewController view]];
[window makeKeyAndVisible];
}
- (void)dealloc {
[window release];
[ConsoleViewController release];
[super dealloc];
}
#end
ConsoleController.h
----------
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/*
The ConsoleViewController's interface declares references to the network data used in negotiating with Shibboleth
and a UITextView used to display the final result or errors.
*/
#interface ConsoleViewController : UIViewController {
NSMutableData *responseData;
NSString *responseString;
UITextView *console;
}
#end
ConsoleController.m
----------
#import "ApplicationDelegate.h"
#import "ConsoleViewController.h"
/*
This delegate is used when making the second HTTP request with Shibboleth. If you're just getting here, start
by reading the comments for ConsoleViewController below.
All we need to do now is receive the response from the SP and display it.
If all goes well, this should be the secured page originally requested.
*/
#interface AuthenticationRedirectDelegate : NSObject {
NSMutableData *authResponseData;
NSString *authResponseString;
UITextView *console;
}
#property (nonatomic retain) UITextView *console;
#end
/*
Refer to the comments for the interface above.
*/
#implementation AuthenticationRedirectDelegate
#synthesize console;
-(id)init {
authResponseData = [[NSMutableData alloc] retain];
return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[authResponseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[authResponseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[console setText:[error localizedDescription]];
}
/*
Once the data is received from Shibboleth's SP, display it.
*/
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
authResponseString = [[NSString alloc] initWithData:authResponseData encoding:NSUTF8StringEncoding];
[console setText:authResponseString];
[connection release];
}
#end
/*
The implementation of the ConsoleViewController, and AuthenticationRedirectDelegate above, contain the real logic of
this Shibboleth exercise. The ConsoleViewController performs the following:
1. Prepare the initial HTTP request to a Shibboleth protected resource.
2. Act as the delegate whilst Cocoa's URL Loading API receives the HTTP Response.
NOTE: We instruct Cocoa in advance to take care of the SP redirecting to the IdP, accepting the server certificate,
and submitting the user credentials
3. Once the HTTP Response is finished loading, parse the <form action, RelayState and SAMLResponse from the IdP's
response
4. Call a utility method to prepare a second HTTP POST Request to the <form action/SP with the IdP's parameters
NOTE: We do not need to transfer over any of Shibboleth's cookies, since Cocoa is doing this automatically
5. Use a new instance of AuthenticationRedirectDelegate to receive the POST's response, which should be the secured
page originally requested.
6. Display the final content in the UITextView known as console.
*/
#implementation ConsoleViewController
/*
A handy utility method for extracting a substring marked by two provided token strings.
Used in parsing the HTML form returned by the IdP after the first HTTP Request.
*/
+(id)substringFromString:(NSString *)source BetweenOpenToken:(NSString *)openToken AndCloseToken:(NSString *)closeToken {
NSUInteger l = [source length];
NSUInteger openTokenLen = [openToken length];
NSUInteger openTokenLoc = ([source rangeOfString:openToken]).location;
NSUInteger valueLoc = openTokenLoc + openTokenLen;
NSRange searchRange = NSMakeRange(valueLoc, l - valueLoc);
NSUInteger closeTokenLoc = ([source rangeOfString:closeToken options:NSCaseInsensitiveSearch range:searchRange]).location;
searchRange = NSMakeRange(valueLoc, closeTokenLoc - valueLoc);
NSString *result = [source substringWithRange:searchRange];
return result;
}
/*
This function takes the three properties returned by the IdP after the first HTTP request and
HTTP POSTs them to the SP as specified by the IdP in the "url" parameter.
*/
-(void)authReturnTo:(NSURL *)url WithRelay:(NSString *)relayState AndSAML:(NSString *)samlResponse {
// Here we assemble the HTTP POST body as usual.
NSString *preBody = [[NSString alloc] initWithString:#"RelayState="];
preBody = [preBody stringByAppendingString:relayState];
preBody = [preBody stringByAppendingString:#"&"];
preBody = [preBody stringByAppendingString:#"SAMLResponse="];
preBody = [preBody stringByAppendingString:samlResponse];
/* The SAMLResponse parameter contains characters (+) that the SP expects to be URL encoded.
Here we simply manually URL encode those characters. You may wish to harden this with proper
URL encoding for production use.
*/
NSString *httpBody = [preBody stringByReplacingOccurrencesOfString:#"+" withString:#"%2B"];
NSData *httpBodyData = [httpBody dataUsingEncoding:NSUTF8StringEncoding];
NSString *httpContentLength = [NSString stringWithFormat:#"%d", [httpBodyData length]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:12.0];
[request setHTTPMethod:#"POST"];
[request setValue:httpContentLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:httpBodyData];
// Submit the HTTP POST using the second delegate class to receive the response
AuthenticationRedirectDelegate *delegate = [[AuthenticationRedirectDelegate alloc] init];
delegate.console=console;
[[NSURLConnection alloc] initWithRequest:request delegate:delegate];
}
/*
When this UIViewController finishes loading, automatically prepare and send a request to the Shibboleth SP Web Server
for a secured resource.
*/
- (void)viewDidLoad {
[super viewDidLoad];
console = [[UITextView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[[self view] addSubview:console];
responseData = [[NSMutableData data] retain];
// TODO: Enter your own URL for a Shibboleth secured resource.
NSURL *url = [NSURL URLWithString:#"<URL>"];
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:12.0];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
/* Control flows to the delegate methods below */
}
/*
Refer to Apple's docs on the URL Loading System for details.
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html
*/
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
/*
Refer to Apple's docs on the URL Loading System for details.
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html
*/
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
/*
This implementation in the delegate let's Cocoa trust my SP Web Server's self-signed certificate.
TODO: You will want to harden this for production use.
Refer to Apple's docs on the URL Loading System for details.
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html
*/
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] || [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic];
}
/*
This implementation for the delegate does two things:
1. Respond to challenges for my server's self-signed certificate
2. Respond to the IdP's challenge for the username and password.
TODO: Enter your own username and password here.
Refer to Apple's docs on the URL Loading System for details.
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html
*/
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
// TODO: Enter the correct username and password below.
/*
WARNING: Using an incorrect user name and password will result in your application being re-challenged
by the IdP. Cocoa will return to this function in a never-ending loop. This can result in the message
"NSPosixErrorDomain Too many open files". You'll need to perform additional coding to handle this.
*/
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic])
[challenge.sender useCredential:[NSURLCredential credentialWithUser:#"<USERNAME>" password:#"<PASSWORD>" persistence:NSURLCredentialPersistenceNone] forAuthenticationChallenge:challenge];
else
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
/*
You may wish to add more code here to log errors.
Refer to Apple's docs on the URL Loading System for details.
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html
*/
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[console setText:[error localizedDescription]];
}
/*
Once Cocoa has received a (hopefully) authenticated response from the IdP, we parse out the relevant pieces and prepare to
HTTP POST them back to the SP as specified by the IdP in the <form action attribute.
Refer to Apple's docs on the URL Loading System for details.
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html
*/
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
if([responseString rangeOfString:#"SAMLResponse"].length < 1)
{
[console setText:[#"Unexpected response:\n]n" stringByAppendingString:responseString]];
return;
}
NSString *relayState = [ConsoleViewController substringFromString:responseString BetweenOpenToken:#"RelayState\" value=\"" AndCloseToken:#"\"/>"];
NSString *SAMLResponse = [ConsoleViewController substringFromString:responseString BetweenOpenToken:#"SAMLResponse\" value=\"" AndCloseToken:#"\"/>"];
NSString *formAction = [ConsoleViewController substringFromString:responseString BetweenOpenToken:#"<form action=\"" AndCloseToken:#"\""];
NSURL *formActionURL = [[NSURL alloc] initWithString:formAction];
[self authReturnTo:formActionURL WithRelay:relayState AndSAML:SAMLResponse];
}
#end
I managed to do just that, but it took me some time to understand every step of the process and to reproduce it perfectly. If I have time, I might write a detailed tutorial, because I didn't find any help for a lot of problems I got. The thing is, it also depends on the website you want to connect to, so yours maybe does not follow the same path as mine (its process is the same as the one described here).
To see every request fired by my browser (Chrome) to connect, I used the developer tools Network panel, with 'Preserve log' checked.
A few hints :
1°) You need to get "_idp_authn_lc_key..." cookie. There's a request that set it for you, find it.
2°) You need the login ticket (LT-...). You'll probably find it in the body of the page that asks you your credentials.
3°) You need a service ticket (ST-...). Again, you will find it in the page that the previous request returned.
4°) You need SAMLResponse. Again, you will find it in the page that the previous request returned.
5°) Finally, you can log in by sending back SAMLResponse to the service provider. You should take care of the encoding, here. I had a few '+' or '=' that I needed to change to '%2B' and '%3D'. You will be given a "_idp_session" cookie, that will allow you to reconnect without all this mess.
If someone tries to do the same, I'd be happy to help ! Just send me a message.
I successfully implemented using EC's solution as a starting point. The only other thing I'd add is that you really have to pay attention to keeping only one request going at a time. In our implementation the authentication process would get confused between multiple asynchronous requests running concurrently. Using NSOperation to throttle the queue seemed to work great for me.