Permalink
Browse files

Merge branch 'refs/heads/password-auth'

  • Loading branch information...
brentdax committed Oct 19, 2012
2 parents 16871fc + dfcafc0 commit fa36e58df9e4ea12e71d51116a0a2777de492bf0
Showing with 122 additions and 10 deletions.
  1. +9 −0 ANAuthenticator.h
  2. +63 −1 ANAuthenticator.m
  3. +7 −0 ANRequest.h
  4. +37 −9 ANRequest.m
  5. +4 −0 AppNetKit.h
  6. +2 −0 AppNetKit.m
View
@@ -21,6 +21,9 @@ extern NSString * const ANScopeExport;
+ (ANAuthenticator*)sharedAuthenticator;
@property (strong,nonatomic) NSString * clientID;
+
+#pragma mark OAuth authentication flow
+
@property (strong,nonatomic) NSURL * redirectURL;
@property (assign,nonatomic) BOOL omitsPaymentOptions;
@@ -30,4 +33,10 @@ extern NSString * const ANScopeExport;
- (BOOL)isRedirectURL:(NSURL*)url;
- (NSString*)accessTokenFromRedirectURL:(NSURL*)redirectURL error:(NSError**)error;
+#pragma mark Password authentication flow
+
+@property (strong,nonatomic) NSString * passwordGrantSecret;
+
+- (void)accessTokenForScopes:(NSArray *)scopes withUsername:(NSString *)username password:(NSString *)password completion:(void (^)(NSString *accessToken, id rep, NSError * error))completion;
+
@end
View
@@ -9,6 +9,7 @@
#import "ANAuthenticator.h"
#import "AppNetKit.h"
#import "NSDictionary+Parameters.h"
+#import "ANRequest.h"
NSString * const ANScopeStream = @"stream";
NSString * const ANScopeEmail = @"email";
@@ -30,12 +31,20 @@ - (id)initSingleton {
return self;
}
+- (NSString*)parameterForScopes:(NSArray*)scopes {
+ return [scopes componentsJoinedByString:@" "];
+}
+
- (NSString*)parametersForScopes:(NSArray *)scopes {
+ NSParameterAssert(scopes);
+ NSAssert(self.clientID, @"ANAuthenticator.clientID not set");
+ NSAssert(self.redirectURL, @"ANAuthenticator.redirectURL not set");
+
NSMutableDictionary * params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
self.clientID, @"client_id",
@"token", @"response_type",
self.redirectURL.absoluteString, @"redirect_uri",
- [scopes componentsJoinedByString:@" "], @"scope",
+ [self parameterForScopes:scopes], @"scope",
nil];
if(self.omitsPaymentOptions) {
@@ -56,6 +65,7 @@ - (NSURL *)URLToAuthorizeForScopes:(NSArray *)scopes {
}
- (BOOL)isRedirectURL:(NSURL *)url {
+ NSAssert(self.redirectURL, @"ANAuthenticator.redirectURL has not been set");
return [url.scheme isEqualToString:self.redirectURL.scheme] && [url.host isEqualToString:self.redirectURL.host] && [url.path isEqualToString:self.redirectURL.path];
}
@@ -103,6 +113,58 @@ - (NSString*)accessTokenFromRedirectURL:(NSURL*)redirectURL error:(NSError *__au
return nil;
}
+- (void)accessTokenForScopes:(NSArray *)scopes withUsername:(NSString *)username password:(NSString *)password completion:(void (^)(NSString *accessToken, id rep, NSError * error))completion {
+ NSAssert(self.clientID, @"ANAuthenticator.clientID not set");
+ NSAssert(self.passwordGrantSecret, @"You must set ANAuthenticator.passwordGrantSecret before calling -%@", NSStringFromSelector(_cmd));
+
+ ANMutableRequest *authRequest = [[ANMutableRequest alloc] initWithSession:ANSession.defaultSession];
+
+ authRequest.URL = [NSURL URLWithString:@"https://alpha.app.net/oauth/access_token"];
+ authRequest.method = ANRequestMethodPost;
+ authRequest.parameterEncoding = ANRequestParameterEncodingURL;
+ authRequest.parameters = [NSDictionary dictionaryWithObjectsAndKeys:@"password", @"grant_type", self.clientID, @"client_id", self.passwordGrantSecret, @"password_grant_secret", username, @"username", password, @"password", [self parameterForScopes:scopes], @"scope", nil];
+
+ [authRequest sendRequestWithRepresentationCompletion:^(ANResponse *response, id rep, NSError *error) {
+ if (error) {
+ NSDictionary *errorResults = [error.userInfo objectForKey:@"json"];
+
+ if ([errorResults isKindOfClass:NSDictionary.class]) {
+ NSString *errorMessage = [errorResults objectForKey:@"error"];
+ NSString *errorText = [errorResults objectForKey:@"error_text"];
+ NSString *errorTitle = [errorResults objectForKey:@"error_title"];
+
+ NSMutableDictionary *userInfo = [NSMutableDictionary new];
+ [userInfo setObject:errorMessage forKey:NSLocalizedDescriptionKey];
+ [userInfo setObject:error forKey:NSUnderlyingErrorKey];
+
+ if (errorText) {
+ [userInfo setObject:errorText forKey:ANPasswordErrorTextKey];
+ }
+ if (errorTitle) {
+ [userInfo setObject:errorText forKey:ANPasswordErrorTitleKey];
+ }
+
+ error = [NSError errorWithDomain:ANErrorDomain code:ANGenericError userInfo:userInfo];
+ }
+
+ if (completion) {
+ completion(nil, nil, error);
+ }
+ return;
+ }
+
+ NSString *accessToken = nil;
+
+ if([rep isKindOfClass:NSDictionary.class]) {
+ accessToken = [rep objectForKey:@"access_token"];
+ }
+
+ if (completion) {
+ completion(accessToken, rep, nil);
+ }
+ }];
+}
+
#pragma mark Singleton machinery
+ (ANAuthenticator *)sharedAuthenticator {
View
@@ -17,6 +17,11 @@ typedef enum {
ANRequestMethodDelete
} ANRequestMethod;
+typedef enum {
+ ANRequestParameterEncodingJSON,
+ ANRequestParameterEncodingURL
+} ANRequestParameterEncoding;
+
@interface ANRequest : NSObject <NSMutableCopying>
- (id)initWithSession:(ANSession*)session;
@@ -26,6 +31,7 @@ typedef enum {
@property (readonly) NSURL * URL;
@property (readonly) NSDictionary * parameters;
@property (readonly) ANRequestMethod method;
+@property (readonly) ANRequestParameterEncoding parameterEncoding;
@property (readonly) NSMutableURLRequest * URLRequest;
@@ -44,5 +50,6 @@ typedef enum {
@property (readwrite,strong) NSURL * URL;
@property (readwrite,strong) NSDictionary * parameters;
@property (readwrite,assign) ANRequestMethod method;
+@property (readwrite,assign) ANRequestParameterEncoding parameterEncoding;
@end
View
@@ -32,6 +32,7 @@ - (id)mutableCopyWithZone:(NSZone *)zone {
req.URL = self.URL;
req.parameters = self.parameters;
req.method = self.method;
+ req.parameterEncoding = self.parameterEncoding;
return req;
}
@@ -68,6 +69,10 @@ - (NSURL *)fullURL {
return [NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", self.URL.absoluteString, params.queryString]];
}
+- (ANRequestParameterEncoding)parameterEncoding {
+ return ANRequestParameterEncodingJSON;
+}
+
- (NSData *)body {
if(self.method == ANRequestMethodGet) {
return nil;
@@ -77,12 +82,22 @@ - (NSData *)body {
if(params.count == 0) {
return nil;
}
-
- NSError * error;
- NSData * data = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
-
- NSAssert(data, @"Params could not be serialized to JSON--error: %@", error.localizedDescription);
-
+
+ NSData * data = nil;
+ NSError * error = nil;
+
+ switch (self.parameterEncoding) {
+ case ANRequestParameterEncodingURL:
+ data = params.formBodyData;
+ break;
+
+ case ANRequestParameterEncodingJSON:
+ default:
+ data = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
+ NSAssert(data, @"Params could not be serialized to JSON--error: %@", error.localizedDescription);
+ break;
+ }
+
return data;
}
@@ -100,7 +115,16 @@ - (NSMutableURLRequest *)URLRequest {
req.HTTPBody = self.body;
if(req.HTTPBody) {
- [req setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
+ switch (self.parameterEncoding) {
+ case ANRequestParameterEncodingURL:
+ [req setValue:@"application/x-www-form-urlencoded; charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
+ break;
+
+ case ANRequestParameterEncodingJSON:
+ default:
+ [req setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
+ break;
+ }
}
return req;
@@ -171,7 +195,11 @@ - (void)sendRequestWithRepresentationCompletion:(void (^)(ANResponse *, id, NSEr
if(!error) {
error = jsonError;
}
- if(error) {
+ if(error && json) {
+ NSMutableDictionary *userInfo = [error.userInfo mutableCopy];
+ [userInfo setObject:json forKey:@"json"];
+ error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
+
json = nil;
}
@@ -183,6 +211,6 @@ - (void)sendRequestWithRepresentationCompletion:(void (^)(ANResponse *, id, NSEr
@implementation ANMutableRequest
-@synthesize URL, parameters, method;
+@synthesize URL, parameters, method, parameterEncoding;
@end
View
@@ -13,6 +13,10 @@
extern NSString * const ANErrorDomain;
extern NSString * const ANExplanationURLKey;
+// Used with password auth
+extern NSString * const ANPasswordErrorTitleKey;
+extern NSString * const ANPasswordErrorTextKey;
+
typedef enum {
ANGenericError,
ANOAuthInvalidRequestError,
View
@@ -14,3 +14,5 @@
NSString * const ANErrorDomain = @"ANErrorDomain";
NSString * const ANExplanationURLKey = @"ANExplanationURL";
+NSString * const ANPasswordErrorTitleKey = @"error_title";
+NSString * const ANPasswordErrorTextKey = @"error_text";

0 comments on commit fa36e58

Please sign in to comment.