Lightweight OAuth 2.0 framework for Objective-C
iOAuth was created by Laura Skelton.
- Download iOAuth Demo project
- Drag the "iOAuth/OAuth" directory from the Finder into your project (Make sure "Copy items into destination group folder", "Create groups for any added folders", and your target are all selected.)
- Rename Configuration.example.plist to Configuration.plist and drag it from the Finder into your project (Again, make sure "Copy items into destination group folder", "Create groups for any added folders", and your target are all selected.)
- Register a custom URL Scheme for your application (Here is a guide- make sure to create a unique name for your app's URL scheme)
- Create an app with the API you're using, and use your custom URL scheme and the path "oauth" as the Redirect URI for your app (eg. yourcustomurlscheme://oauth)
- Edit Configuration.plist to add the credentials for your API (your Client ID, Client Secret, Redirect URI (yourcustomurlscheme://oauth), and the URLs for your API's Authentication and Access Token requests)
See the iOAuth Demo app for examples.
Usage Examples:
Set up a delegate to manage launching the OAuth authentication and handling its success and failure callbacks.
#import "AppDelegate.h"
#import "OAuthHandler.h"
@interface AppDelegate () <OAuthHandlerDelegate>
-(void)getMyName:(id)sender;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// simple version, for an API that does not use state or scope
//[[OAuthHandler sharedHandler] authenticateWithDelegate:self];
// complex version for an API that uses state or scope, eg. Vimeo
[[OAuthHandler sharedHandler] authenticateWithDelegate:self usesState:YES withScope:nil];
return YES;
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
// lsioauth://oauth?access_token=324235253442
NSLog(@"url recieved: %@", url);
if ([[url host] isEqualToString:@"oauth"]) {
// parse the authentication code query
[[OAuthHandler sharedHandler] authorizeFromExternalURL:url delegate:self];
}
return YES;
}
Be sure to handle the OAuthHandler Delegate callbacks for authentication success and failure.
#pragma mark - OAuthHandler Delegate
- (void)oauthHandlerDidAuthorize
{
// let application know that we can access the API now
NSLog(@"Authentication success!");
[self getMyName:nil];
}
- (void)oauthHandlerDidFailWithError:(NSString *)errorMessage
{
// Authentication failed
UIAlertView *alertView = [ [UIAlertView alloc] initWithTitle:@"Authorization Failed"
message:errorMessage
delegate:self
cancelButtonTitle:@"Dismiss"
otherButtonTitles:nil];
[alertView show];
}
@end
After the user is authenticated with your API, sign each of your requests with their OAuth access token.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:someURL];
[[OAuthHandler sharedHandler] signRequest:request withCallback:^(NSMutableURLRequest *signedRequest) {
[NSURLConnection sendAsynchronousRequest:signedRequest
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// handle the response here as usual
[self handleResponse:response withData:data andError:error];
}];
}];
For example, in the Vimeo demo app, we make an API call for the logged in user's name.
-(void)getMyName:(id)sender
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.vimeo.com/me"]];
// make sure to sign the request with the OAuth access token before calling the API!
[[OAuthHandler sharedHandler] signRequest:request withCallback:^(NSMutableURLRequest *signedRequest) {
[NSURLConnection sendAsynchronousRequest:signedRequest
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// handle the response here as usual
NSError *e;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&e];
NSLog(@"response: %@, data: %@", response, jsonDict);
UIAlertView *alertView = [ [UIAlertView alloc] initWithTitle:@"Logged In"
message:[jsonDict objectForKey:@"name"]
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alertView show];
}];
}];
}
First call to server to get a code
, which represents that user's login credentials with the API. This is a GET request. Send the user to this url to log in and authorize your app to use their account:
https://www.hackerschool.com/oauth/authorize?response_type=code&client_id=(my_client_id)&redirect_uri=(my_redirect_uri)
Tricks to keep in mind: the redirect_uri
should be the same redirect that your app registered with the API when you got your client_id
and client_secret
. This is the URL that will need to process the returned code
to request an access_token
from the API in just a minute. The things in parentheses should be replaced with your client_id
and your redirect_uri
.
If the user logs in and authorizes your app, the API will send a GET request to your redirect uri with a code
parameter with the Authorization Code for that user. You'll use the code
in the next step to request an access_token
for that user.
GET request the API returns:
(your-redirect-uri)?code=(some-authorization-code-for-this-user)
Second call to server to get access_token
, which you will use to sign each API request you make. Signing a request with a valid access_token
lets you access whatever the logged-in user has permission to access. This must be a POST request. Make a POST request to this URL in the background (no need to redirect the user to this page this time). Again, the things in parentheses should be replaced with your own client_id
, client_secret
, redirect_uri
, and the code
the API just sent you for the user.
-
URL:
https://www.hackerschool.com/oauth/token
-
POST Request Parameters:
grant_type=authorization_code&client_id=(my_client_id)&client_secret=(my_client_secret)&redirect_uri=(my_redirect_uri)&code=(the authorization code you just got from the GET request the API returned)
This will return a JSON response that contains, among other things, the parameters access_token
and refresh_token
. There are other parameters, but these two are the most important for signing your API calls.
{"access_token": "some-access-token",
"token_type": "bearer",
"expires_in": 7200,
"refresh_token": "some-refresh-token",
"scope": "public"}
Save the access_token
and refresh_token
values somewhere persistent that you will be able to reuse for that user when your app launches in the future. In iOS, NSUserDefaults
is a good place to store them.
Every time you make a call to the API for that user, just sign the request with the access_token
. You can even use a GET request to sign the call. For example: https://www.hackerschool.com/api/v1/people/me?access_token=(your stored access token for this user)
will return a JSON string containing this user's profile info.
It's probably better instead of passing the access_token
in a GET request, to send it in the Authorization header.
Authorization: Bearer (my-access-token-here)
In Objective-C, this would look sort of like:
NSURL *profileURL = [NSURL URLWithString:@"https://www.hackerschool.com/api/v1/people/me"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:profileURL];
[request setValue:[NSString stringWithFormat:@"Bearer %@", [[NSUserDefaults standardUserDefaults] objectForKey:kSHAccessTokenKey]] forHTTPHeaderField:@"Authorization"];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// handle request completion here
}]
Eventually (after 2 hours I believe), your access_token
will expire. Suddenly, instead of returning the JSON you were expecting, it will return JSON containing a message
with the value unauthorized
.
{"message": "unauthorized"}
When this happens, it's time to try refreshing your access_token
. Just like getting the original access_token
, this needs to be a POST request to the oauth/token
URL, but with slightly different POST request parameters, since we are now using the refresh_token
to get the access_token
, instead of using the code
to get the access_token
.
-
URL:
https://www.hackerschool.com/oauth/token
-
POST Request Parameters:
grant_type=refresh_token&client_id=(my_client_id)&client_secret=(my_client_secret)&refresh_token=(the refresh token for this user that you saved earlier)
If this returns any kind of error (the API will return a JSON error
if something's not properly authorized), I just have the user log in again.
Because the login in iOAuth redirects to the Safari browser, the user might already be logged in there, in which case they will be quickly redirected back to your app with the new access_token
and refresh_token
your app needs to sign their requests.