Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Refactored OAuth support for merge into master. fixes #84, #211

Cleaned up @rodchile's excellent work integration OAuth 1.0 and 2.0 into RestKit. Changes
are as follows:

* Introduced new RKRequestAuthenticationType to replace the forceBasicAuthentication and other
    methods for influencing how authorization works.
* Moved TDOAuth code into Vendor/
* Renamed authorization code flow classes and delegate methods for clarity.
  • Loading branch information...
commit eb887e38c663c52603a78424e56fe30aae2fd912 1 parent 144533c
@blakewatters blakewatters authored
View
3  .gitignore
@@ -21,4 +21,5 @@ Docs/API
Examples/RKDiscussionBoardExample/discussion_board_backend/public/system/attachments/*
# UISpecRunner cached build path
-.uispec.app
+.uispec.app
+Specs/Runner/UISpec
View
1  Code/Network/Network.h
@@ -26,3 +26,4 @@
#import "RKReachabilityObserver.h"
#import "RKRequestQueue.h"
#import "RKNotifications.h"
+#import "RKClientOAuth.h"
View
68 Code/Network/RKClient.h
@@ -154,9 +154,15 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
*/
@interface RKClient : NSObject {
NSString *_baseURL;
+ RKRequestAuthenticationType _authenticationType;
NSString *_username;
NSString *_password;
- BOOL _forceBasicAuthentication;
+ NSString *_OAuth1ConsumerKey;
+ NSString *_OAuth1ConsumerSecret;
+ NSString *_OAuth1AccessToken;
+ NSString *_OAuth1AccessTokenSecret;
+ NSString *_OAuth2AccessToken;
+ NSString *_OAuth2RefreshToken;
NSMutableDictionary *_HTTPHeaders;
RKReachabilityObserver *_baseURLReachabilityObserver;
NSString *_serviceUnavailableAlertTitle;
@@ -249,10 +255,23 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
#endif
/////////////////////////////////////////////////////////////////////////
-/// @name HTTP Authentication
+/// @name Authentication
/////////////////////////////////////////////////////////////////////////
/**
+ The type of authentication to use for this request.
+
+ When configured to RKRequestAuthenticationTypeHTTPBasic, RestKit will add
+ an Authorization header establishing login via HTTP Basic. This is an optimization
+ that skips the challenge portion of the request.
+
+ **Default**: RKRequestAuthenticationTypeNone
+
+ @see RKRequestAuthenticationType
+ */
+@property (nonatomic, assign) RKRequestAuthenticationType authenticationType;
+
+/**
* The username to use for authentication via HTTP AUTH
*/
@property(nonatomic, retain) NSString *username;
@@ -262,14 +281,43 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
*/
@property(nonatomic, retain) NSString *password;
+/*** @name OAuth Secrets */
+
/**
- When YES, RKRequest objects dispatched through the client will have an HTTP Basic
- Authorization header added before being sent.
-
- This avoids an HTTP AUTH challenge before authentication and can be used to force
- authentication is situations where an AUTH challenge is not issued
+ The OAuth 1.0 consumer key
+ */
+@property(nonatomic,retain) NSString *OAuth1ConsumerKey;
+
+/**
+ The OAuth 1.0 consumer secret
+ */
+@property(nonatomic,retain) NSString *OAuth1ConsumerSecret;
+
+/**
+ The OAuth 1.0 access token
+ */
+@property(nonatomic,retain) NSString *OAuth1AccessToken;
+
+/**
+ The OAuth 1.0 access token secret
+ */
+@property(nonatomic,retain) NSString *OAuth1AccessTokenSecret;
+
+/*** @name OAuth2 Secrets */
+
+/**
+ The OAuth 2.0 access token
*/
-@property(nonatomic, assign) BOOL forceBasicAuthentication;
+@property(nonatomic,retain) NSString *OAuth2AccessToken;
+
+/**
+ The OAuth 2.0 refresh token. Used to retrieve a new access token before expiration
+ */
+@property(nonatomic,retain) NSString *OAuth2RefreshToken;
+
+/////////////////////////////////////////////////////////////////////////
+/// @name Reachability & Service Availability Alerting
+/////////////////////////////////////////////////////////////////////////
/**
* The RKReachabilityObserver used to monitor whether or not the client has a connection
@@ -283,10 +331,6 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
*/
@property(nonatomic, readonly) RKReachabilityObserver *baseURLReachabilityObserver;
-/////////////////////////////////////////////////////////////////////////
-/// @name Service Availability Alerting
-/////////////////////////////////////////////////////////////////////////
-
/**
* The title to use in the alert shown when a request encounters a
* ServiceUnavailable (503) response.
View
25 Code/Network/RKClient.m
@@ -68,9 +68,15 @@
@implementation RKClient
@synthesize baseURL = _baseURL;
+@synthesize authenticationType = _authenticationType;
@synthesize username = _username;
@synthesize password = _password;
-@synthesize forceBasicAuthentication = _forceBasicAuthentication;
+@synthesize OAuth1ConsumerKey = _OAuth1ConsumerKey;
+@synthesize OAuth1ConsumerSecret = _OAuth1ConsumerSecret;
+@synthesize OAuth1AccessToken = _OAuth1AccessToken;
+@synthesize OAuth1AccessTokenSecret = _OAuth1AccessTokenSecret;
+@synthesize OAuth2AccessToken = _OAuth2AccessToken;
+@synthesize OAuth2RefreshToken = _OAuth2RefreshToken;
@synthesize HTTPHeaders = _HTTPHeaders;
#ifdef RESTKIT_SSL_VALIDATION
@synthesize additionalRootCertificates = _additionalRootCertificates;
@@ -102,7 +108,6 @@ + (RKClient *)clientWithBaseURL:(NSString *)baseURL username:(NSString *)usernam
RKClient *client = [RKClient clientWithBaseURL:baseURL];
client.username = username;
client.password = password;
-
return client;
}
@@ -201,16 +206,22 @@ - (NSURL *)URLForResourcePath:(NSString *)resourcePath queryParams:(NSDictionary
- (void)setupRequest:(RKRequest *)request {
request.additionalHTTPHeaders = _HTTPHeaders;
+ request.authenticationType = self.authenticationType;
request.username = self.username;
request.password = self.password;
- request.forceBasicAuthentication = self.forceBasicAuthentication;
request.cachePolicy = self.cachePolicy;
request.cache = self.requestCache;
- request.queue = self.requestQueue;
+ request.queue = self.requestQueue;
- if (! [self.requestQueue isKindOfClass:[RKRequestQueue class]]) {
- NSLog(@"BREAK)@($&@()$");
- }
+ // OAuth 1 Parameters
+ request.OAuth1AccessToken = self.OAuth1AccessToken;
+ request.OAuth1AccessTokenSecret = self.OAuth1AccessTokenSecret;
+ request.OAuth1ConsumerKey = self.OAuth1ConsumerKey;
+ request.OAuth1ConsumerSecret = self.OAuth1ConsumerSecret;
+
+ // OAuth2 Parameters
+ request.OAuth2AccessToken = self.OAuth2AccessToken;
+ request.OAuth2RefreshToken = self.OAuth2RefreshToken;
}
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)header {
View
127 Code/Network/RKOAuthClient.h
@@ -0,0 +1,127 @@
+//
+// RKOAuthClient.h
+// RestKit
+//
+// Created by Rodrigo Garcia on 7/20/11.
+// Copyright 2011 RestKit. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <UIKit/UIKit.h>
+#import <Foundation/Foundation.h>
+#import "RKClient.h"
+#import "RKRequest.h"
+
+/**
+ Defines error codes for OAuth client errors
+ */
+typedef enum RKOAuthClientErrors {
+ RKOAuthClientErrorInvalidGrant = 3001, // An invalid authorization code was encountered
+ RKOAuthClientErrorUnauthorizedClient = 3002, // The client is not authorized to perform the action
+ RKOAuthClientErrorInvalidClient = 3003, //
+ RKOAuthClientErrorInvalidRequest = 3004, //
+ RKOAuthClientErrorUnsupportedGrantType = 3005, //
+ RKOAuthClientErrorInvalidScope = 3006, //
+ RKOAuthClientErrorRequestError = 3007 //
+} RKOAuthClientErrorCode;
+
+@protocol RKOAuthClientDelegate;
+
+/**
+ An OAuth client implementation used for OAuth 2 authorization code flow.
+ */
+@interface RKOAuthClient : NSObject <RKRequestDelegate> {
+ NSString *_clientID;
+ NSString *_clientSecret;
+ NSString *_authorizationCode;
+ NSString *_authorizationURL;
+ NSString *_callbackURL;
+ NSString *_accessToken;
+ id<RKOAuthClientDelegate> _delegate;
+}
+
+// General properties of the client
+@property(nonatomic,retain) NSString *authorizationCode;
+
+// OAuth Client ID and Secret
+@property(nonatomic,retain) NSString *clientID;
+@property(nonatomic,retain) NSString *clientSecret;
+
+// OAuth EndPoints
+@property(nonatomic,retain) NSString *authorizationURL;
+@property(nonatomic,retain) NSString *callbackURL;
+
+/**
+ Returns the access token retrieved
+ */
+@property (nonatomic, readonly) NSString *accessToken;
+
+// Client Delegate
+@property (nonatomic,assign) id<RKOAuthClientDelegate> delegate;
+
+- (id)initWithClientID:(NSString *)clientId
+ secret:(NSString *)secret
+ delegate:(id<RKOAuthClientDelegate>)delegate;
+
+- (void)validateAuthorizationCode;
+
++ (RKOAuthClient *)clientWithClientID:(NSString *)clientId
+ secret:(NSString *)secret
+ delegate:(id<RKOAuthClientDelegate>)delegate;
+
+@end
+
+/**
+ * Lifecycle events for RKClientOAuth
+ */
+@protocol RKOAuthClientDelegate <NSObject>
+@required
+
+/**
+ * Sent when a new access token has being acquired
+ */
+- (void)OAuthClient:(RKOAuthClient *)client didAcquireAccessToken:(NSString *)token;
+
+/**
+ * Sent when an access token request has failed due an invalid authorization code
+ */
+- (void)OAuthClient:(RKOAuthClient *)client didFailWithInvalidGrantError:(NSError *)error;
+
+@optional
+
+/**
+ * Other OAuth2 protocol exceptions for the authorization code flow
+ */
+
+/**
+ Sent to the delegate when the OAuth client encounters any error
+ */
+- (void)OAuthClient:(RKOAuthClient *)client didFailWithError:(NSError *)error;
+
+- (void)OAuthClient:(RKOAuthClient *)client didFailWithUnauthorizedClientError:(NSError *)error;
+
+- (void)OAuthClient:(RKOAuthClient *)client didFailWithInvalidClientError:(NSError *)error;
+
+- (void)OAuthClient:(RKOAuthClient *)client didFailWithInvalidRequestError:(NSError *)error;
+
+- (void)OAuthClient:(RKOAuthClient *)client didFailWithUnsupportedGrantTypeError:(NSError *)error;
+
+- (void)OAuthClient:(RKOAuthClient *)client didFailWithInvalidScopeError:(NSError *)error;
+
+/**
+ Sent to the delegate when an authorization code flow request failed loading due to an error
+ */
+- (void)OAuthClient:(RKOAuthClient *)client didFailLoadingRequest:(RKRequest *)request withError:(NSError *)error;
+
+@end
View
180 Code/Network/RKOAuthClient.m
@@ -0,0 +1,180 @@
+//
+// RKOAuthClient.m
+// RestKit
+//
+// Created by Rodrigo Garcia on 7/20/11.
+// Copyright 2011 RestKit. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "RKOAuthClient.h"
+#import "../Support/Errors.h"
+
+@implementation RKOAuthClient
+
+@synthesize clientID = _clientID;
+@synthesize clientSecret = _clientSecret;
+@synthesize authorizationCode = _authorizationCode;
+@synthesize authorizationURL = _authorizationURL;
+@synthesize callbackURL = _callbackURL;
+@synthesize delegate = _delegate;
+@synthesize accessToken = _accessToken;
+
++ (RKOAuthClient *)clientWithClientID:(NSString *)clientId
+ secret:(NSString *)secret
+ delegate:(id<RKOAuthClientDelegate>)delegate {
+ RKOAuthClient *client = [[[self alloc] initWithClientID:clientId secret:secret delegate:delegate] autorelease];
+ return client;
+}
+
+- (id)initWithClientID:(NSString *)clientId
+ secret:(NSString *)secret
+ delegate:(id<RKOAuthClientDelegate>)delegate
+{
+ self = [super init];
+ if (self) {
+ _clientID = [clientId copy];
+ _clientSecret = [secret copy];
+ _delegate = delegate;
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ [_clientID release];
+ [_clientSecret release];
+ [_accessToken release];
+
+ [super dealloc];
+}
+
+- (void)validateAuthorizationCode {
+ NSString *httpBody = [NSString stringWithFormat:@"client_id=%@&client_secret=%@&code=%@&redirect_uri=%@&grant_type=authorization_code",
+ _clientID, _clientSecret, _authorizationCode, _callbackURL];
+ RKClient *requestClient = [RKClient clientWithBaseURL:_authorizationURL];
+ RKRequest *theRequest = [requestClient requestWithResourcePath:@"" delegate:self];
+ [theRequest setHTTPBodyString:httpBody];
+ [theRequest setMethod:RKRequestMethodPOST];
+ [theRequest send];
+}
+
+- (void)request:(RKRequest *)request didLoadResponse:(RKResponse *)response {
+ NSError *error = nil;
+ NSString *errorResponse = nil;
+
+ //Use the parsedBody answer in NSDictionary
+
+ NSDictionary* oauthResponse = (NSDictionary *) [response parsedBody:&error];
+ if ([oauthResponse isKindOfClass:[NSDictionary class]]) {
+
+ //Check the if an access token comes in the response
+ _accessToken = [[oauthResponse objectForKey:@"access_token"] copy];
+ errorResponse = [oauthResponse objectForKey:@"error"];
+
+ if (_accessToken) {
+ // W00T We got an accessToken
+ [self.delegate OAuthClient:self didAcquireAccessToken:_accessToken];
+
+ return;
+ } else if (errorResponse) {
+ // Heads-up! There is an error in the response
+ // The possible errors are defined in the OAuth2 Protocol
+
+ RKOAuthClientErrorCode errorCode;
+ NSString *errorDescription = [oauthResponse objectForKey:@"error_description"];
+
+ if ([errorResponse isEqualToString:@"invalid_grant"]) {
+ errorCode = RKOAuthClientErrorInvalidGrant;
+ }
+ else if([errorResponse isEqualToString:@"unauthorized_client"]){
+ errorCode = RKOAuthClientErrorUnauthorizedClient;
+ }
+ else if([errorResponse isEqualToString:@"invalid_client"]){
+ errorCode = RKOAuthClientErrorInvalidClient;
+ }
+ else if([errorResponse isEqualToString:@"invalid_request"]){
+ errorCode = RKOAuthClientErrorInvalidRequest;
+ }
+ else if([errorResponse isEqualToString:@"unsupported_grant_type"]){
+ errorCode = RKOAuthClientErrorUnsupportedGrantType;
+ }
+ else if([errorResponse isEqualToString:@"invalid_scope"]){
+ errorCode = RKOAuthClientErrorInvalidScope;
+ }
+
+ NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ errorDescription, NSLocalizedDescriptionKey, nil];
+ NSError *error = [NSError errorWithDomain:RKRestKitErrorDomain code:errorCode userInfo:userInfo];
+
+
+ // Inform the delegate of what happened
+ if ([self.delegate respondsToSelector:@selector(OAuthClient:didFailWithError:)]) {
+ [self.delegate OAuthClient:self didFailWithError:error];
+ }
+
+ // Invalid grant
+ if (errorCode == RKOAuthClientErrorInvalidGrant && [self.delegate respondsToSelector:@selector(OAuthClient:didFailWithInvalidGrantError:)]) {
+ [self.delegate OAuthClient:self didFailWithInvalidGrantError:error];
+ }
+
+ // Unauthorized client
+ if (errorCode == RKOAuthClientErrorUnauthorizedClient && [self.delegate respondsToSelector:@selector(OAuthClient:didFailWithUnauthorizedClientError:)]) {
+ [self.delegate OAuthClient:self didFailWithUnauthorizedClientError:error];
+ }
+
+ // Invalid client
+ if (errorCode == RKOAuthClientErrorInvalidClient && [self.delegate respondsToSelector:@selector(OAuthClient:didFailWithInvalidClientError:)]) {
+ [self.delegate OAuthClient:self didFailWithInvalidClientError:error];
+ }
+
+ // Invalid request
+ if (errorCode == RKOAuthClientErrorInvalidRequest && [self.delegate respondsToSelector:@selector(OAuthClient:didFailWithInvalidRequestError:)]) {
+ [self.delegate OAuthClient:self didFailWithInvalidRequestError:error];
+ }
+
+ // Unsupported grant type
+ if (errorCode == RKOAuthClientErrorUnsupportedGrantType && [self.delegate respondsToSelector:@selector(OAuthClient:didFailWithUnsupportedGrantTypeError:)]) {
+ [self.delegate OAuthClient:self didFailWithUnsupportedGrantTypeError:error];
+ }
+
+ // Invalid scope
+ if (errorCode == RKOAuthClientErrorInvalidScope && [self.delegate respondsToSelector:@selector(OAuthClient:didFailWithInvalidScopeError:)]) {
+ [self.delegate OAuthClient:self didFailWithInvalidScopeError:error];
+ }
+ }
+ } else if (error) {
+ if ([self.delegate respondsToSelector:@selector(OAuthClient:didFailWithError:)]) {
+ [self.delegate OAuthClient:self didFailWithError:error];
+ }
+ } else {
+ // TODO: Logging...
+ }
+}
+
+
+- (void)request:(RKRequest *)request didFailLoadWithError:(NSError *)error {
+ NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ error, NSUnderlyingErrorKey, nil];
+ NSError *clientError = [NSError errorWithDomain:RKRestKitErrorDomain code:RKOAuthClientErrorRequestError userInfo:userInfo];
+ if ([self.delegate respondsToSelector:@selector(OAuthClient:didFailLoadingRequest:withError:)]) {
+ [self.delegate OAuthClient:self didFailLoadingRequest:request withError:clientError];
+ }
+
+ if ([self.delegate respondsToSelector:@selector(OAuthClient:didFailWithError:)]) {
+ [self.delegate OAuthClient:self didFailWithError:clientError];
+ }
+}
+
+@end
View
154 Code/Network/RKRequest.h
@@ -81,6 +81,14 @@ typedef enum RKRequestBackgroundPolicy {
} RKRequestBackgroundPolicy;
#endif
+typedef enum {
+ RKRequestAuthenticationTypeNone = 0, // Disable the use of authentication
+ RKRequestAuthenticationTypeHTTP, // Use NSURLConnection's HTTP AUTH auto-negotiation
+ RKRequestAuthenticationTypeHTTPBasic, // Force the use of HTTP Basic authentication. This will supress AUTH challenges
+ RKRequestAuthenticationTypeOAuth1, // Enable the use of OAuth 1.0 authentication
+ RKRequestAuthenticationTypeOAuth2 // Enable the use of OAuth 2.0 authentication
+} RKRequestAuthenticationType;
+
@class RKResponse, RKRequestQueue;
@protocol RKRequestDelegate;
@@ -88,22 +96,28 @@ typedef enum RKRequestBackgroundPolicy {
Models the request portion of an HTTP request/response cycle.
*/
@interface RKRequest : NSObject {
- NSURL* _URL;
- NSMutableURLRequest* _URLRequest;
- NSURLConnection* _connection;
- NSDictionary* _additionalHTTPHeaders;
- NSObject<RKRequestSerializable>* _params;
- NSObject<RKRequestDelegate>* _delegate;
+ NSURL *_URL;
+ NSMutableURLRequest *_URLRequest;
+ NSURLConnection *_connection;
+ NSDictionary *_additionalHTTPHeaders;
+ NSObject<RKRequestSerializable> *_params;
+ NSObject<RKRequestDelegate> *_delegate;
id _userData;
- NSString* _username;
- NSString* _password;
+ RKRequestAuthenticationType _authenticationType;
+ NSString *_username;
+ NSString *_password;
+ NSString *_OAuth1ConsumerKey;
+ NSString *_OAuth1ConsumerSecret;
+ NSString *_OAuth1AccessToken;
+ NSString *_OAuth1AccessTokenSecret;
+ NSString *_OAuth2AccessToken;
+ NSString *_OAuth2RefreshToken;
RKRequestMethod _method;
BOOL _isLoading;
BOOL _isLoaded;
RKRequestCachePolicy _cachePolicy;
BOOL _sentSynchronously;
- BOOL _forceBasicAuthentication;
- RKRequestCache* _cache;
+ RKRequestCache *_cache;
NSTimeInterval _cacheTimeoutInterval;
RKRequestQueue *_queue;
@@ -116,12 +130,12 @@ typedef enum RKRequestBackgroundPolicy {
/**
* The URL this request is loading
*/
-@property(nonatomic, retain) NSURL* URL;
+@property(nonatomic, retain) NSURL *URL;
/**
* The resourcePath portion of this loader's URL
*/
-@property (nonatomic, retain) NSString* resourcePath;
+@property (nonatomic, retain) NSString *resourcePath;
/**
* The HTTP verb the request is sent via
@@ -133,7 +147,7 @@ typedef enum RKRequestBackgroundPolicy {
/**
* A serializable collection of parameters sent as the HTTP Body of the request
*/
-@property(nonatomic, retain) NSObject<RKRequestSerializable>* params;
+@property(nonatomic, retain) NSObject<RKRequestSerializable> *params;
/**
* The delegate to inform when the request is completed
@@ -141,12 +155,12 @@ typedef enum RKRequestBackgroundPolicy {
* If the object implements the RKRequestDelegate protocol,
* it will receive request lifecycle event messages.
*/
-@property(nonatomic, assign) NSObject<RKRequestDelegate>* delegate;
+@property(nonatomic, assign) NSObject<RKRequestDelegate> *delegate;
/**
* A Dictionary of additional HTTP Headers to send with the request
*/
-@property(nonatomic, retain) NSDictionary* additionalHTTPHeaders;
+@property(nonatomic, retain) NSDictionary *additionalHTTPHeaders;
/**
* An opaque pointer to associate user defined data with the request.
@@ -154,6 +168,21 @@ typedef enum RKRequestBackgroundPolicy {
@property(nonatomic, retain) id userData;
/**
+ * The underlying NSMutableURLRequest sent for this request
+ */
+@property(nonatomic, readonly) NSMutableURLRequest *URLRequest;
+
+/**
+ * The HTTP method as a string used for this request
+ */
+@property(nonatomic, readonly) NSString *HTTPMethod;
+
+/**
+ The request queue that this request belongs to
+ */
+@property (nonatomic, assign) RKRequestQueue *queue;
+
+/**
* The policy to take on transition to the background (iOS 4.x and higher only)
*
* Default: RKRequestBackgroundPolicyCancel
@@ -163,33 +192,66 @@ typedef enum RKRequestBackgroundPolicy {
@property(nonatomic, readonly) UIBackgroundTaskIdentifier backgroundTaskIdentifier;
#endif
+/////////////////////////////////////////////////////////////////////////
+/// @name Authentication
+/////////////////////////////////////////////////////////////////////////
+
/**
- * Credentials for HTTP AUTH Challenge
+ The type of authentication to use for this request.
+
+ When configured to RKRequestAuthenticationTypeHTTPBasic, RestKit will add
+ an Authorization header establishing login via HTTP Basic. This is an optimization
+ that skips the challenge portion of the request.
+
+ **Default**: RKRequestAuthenticationTypeNone
+
+ @see RKRequestAuthenticationType
*/
-@property(nonatomic, retain) NSString* username;
-@property(nonatomic, retain) NSString* password;
+@property (nonatomic, assign) RKRequestAuthenticationType authenticationType;
/**
- When YES, RestKit will assume you are using HTTP Basic Authentication
- and add an Authorization header to the request. This will force authentication
- without being challenged.
+ The username to use for an HTTP Authentication
*/
-@property(nonatomic, assign) BOOL forceBasicAuthentication;
+@property(nonatomic, retain) NSString *username;
/**
- * The underlying NSMutableURLRequest sent for this request
+ The password to use for an HTTP Authentication
*/
-@property(nonatomic, readonly) NSMutableURLRequest* URLRequest;
+@property(nonatomic, retain) NSString *password;
+
+/*** @name OAuth Secrets */
/**
- * The HTTP method as a string used for this request
+ The OAuth 1.0 consumer key
*/
-@property(nonatomic, readonly) NSString* HTTPMethod;
+@property(nonatomic,retain) NSString *OAuth1ConsumerKey;
/**
- The request queue that this request belongs to
+ The OAuth 1.0 consumer secret
+ */
+@property(nonatomic,retain) NSString *OAuth1ConsumerSecret;
+
+/**
+ The OAuth 1.0 access token
+ */
+@property(nonatomic,retain) NSString *OAuth1AccessToken;
+
+/**
+ The OAuth 1.0 access token secret
+ */
+@property(nonatomic,retain) NSString *OAuth1AccessTokenSecret;
+
+/*** @name OAuth2 Secrets */
+
+/**
+ The OAuth 2.0 access token
+ */
+@property(nonatomic,retain) NSString *OAuth2AccessToken;
+
+/**
+ The OAuth 2.0 refresh token. Used to retrieve a new access token before expiration
*/
-@property (nonatomic, assign) RKRequestQueue* queue;
+@property(nonatomic,retain) NSString *OAuth2RefreshToken;
/////////////////////////////////////////////////////////////////////////
/// @name Cacheing
@@ -202,7 +264,7 @@ typedef enum RKRequestBackgroundPolicy {
The cacheKey is an MD5 value computed by hashing a combination of the destination
URL, the HTTP verb, and the request body (if possible)
*/
-@property (nonatomic, readonly) NSString* cacheKey;
+@property (nonatomic, readonly) NSString *cacheKey;
/**
The cache policy used when storing this request into the request cache
@@ -214,7 +276,7 @@ typedef enum RKRequestBackgroundPolicy {
Generally configured by the RKClient instance that minted this request
*/
-@property (nonatomic, retain) RKRequestCache* cache;
+@property (nonatomic, retain) RKRequestCache *cache;
/**
Returns YES if the request is cacheable
@@ -228,12 +290,12 @@ typedef enum RKRequestBackgroundPolicy {
/**
* The HTTP body as a NSData used for this request
*/
-@property (nonatomic, retain) NSData* HTTPBody;
+@property (nonatomic, retain) NSData *HTTPBody;
/**
* The HTTP body as a string used for this request
*/
-@property (nonatomic, retain) NSString* HTTPBodyString;
+@property (nonatomic, retain) NSString *HTTPBodyString;
/**
* The timeout interval within which the request should not be sent
@@ -248,17 +310,17 @@ typedef enum RKRequestBackgroundPolicy {
/**
* Return a REST request that is ready for dispatching
*/
-+ (RKRequest*)requestWithURL:(NSURL*)URL delegate:(id)delegate;
++ (RKRequest *)requestWithURL:(NSURL *)URL delegate:(id)delegate;
/**
* Initialize a synchronous request
*/
-- (id)initWithURL:(NSURL*)URL;
+- (id)initWithURL:(NSURL *)URL;
/**
* Initialize a REST request and prepare it for dispatching
*/
-- (id)initWithURL:(NSURL*)URL delegate:(id)delegate;
+- (id)initWithURL:(NSURL *)URL delegate:(id)delegate;
/**
* Setup the NSURLRequest. The request must be prepared right before dispatching
@@ -284,19 +346,19 @@ typedef enum RKRequestBackgroundPolicy {
/**
* Send the request synchronously and return a hydrated response object
*/
-- (RKResponse*)sendSynchronously;
+- (RKResponse *)sendSynchronously;
/**
* Callback performed to notify the request that the underlying NSURLConnection
* has failed with an error.
*/
-- (void)didFailLoadWithError:(NSError*)error;
+- (void)didFailLoadWithError:(NSError *)error;
/**
* Callback performed to notify the request that the underlying NSURLConnection
* has completed with a response.
*/
-- (void)didFinishLoad:(RKResponse*)response;
+- (void)didFinishLoad:(RKResponse *)response;
/**
* Cancels the underlying URL connection.
@@ -352,45 +414,45 @@ typedef enum RKRequestBackgroundPolicy {
/**
* Returns YES when the request was sent to the specified resource path
*/
-- (BOOL)wasSentToResourcePath:(NSString*)resourcePath;
+- (BOOL)wasSentToResourcePath:(NSString *)resourcePath;
@end
/**
* Lifecycle events for RKRequests
*/
-@protocol RKRequestDelegate
+@protocol RKRequestDelegate <NSObject>
@optional
/**
* Sent when a request has finished loading
*/
-- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response;
+- (void)request:(RKRequest *)request didLoadResponse:(RKResponse *)response;
/**
* Sent when a request has failed due to an error
*/
-- (void)request:(RKRequest*)request didFailLoadWithError:(NSError*)error;
+- (void)request:(RKRequest *)request didFailLoadWithError:(NSError *)error;
/**
* Sent when a request has started loading
*/
-- (void)requestDidStartLoad:(RKRequest*)request;
+- (void)requestDidStartLoad:(RKRequest *)request;
/**
* Sent when a request has uploaded data to the remote site
*/
-- (void)request:(RKRequest*)request didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite;
+- (void)request:(RKRequest *)request didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite;
/**
* Sent to the delegate when a request was cancelled
*/
-- (void)requestDidCancelLoad:(RKRequest*)request;
+- (void)requestDidCancelLoad:(RKRequest *)request;
/**
* Sent to the delegate when a request has timed out. This is sent when a
* backgrounded request expired before completion.
*/
-- (void)requestDidTimeout:(RKRequest*)request;
+- (void)requestDidTimeout:(RKRequest *)request;
@end
View
77 Code/Network/RKRequest.m
@@ -29,17 +29,35 @@
#import "NSString+MD5.h"
#import "RKLog.h"
#import "RKRequestCache.h"
+#import "TDOAuth.h"
+
// Set Logging Component
#undef RKLogComponent
#define RKLogComponent lcl_cRestKitNetwork
@implementation RKRequest
-
-@synthesize URL = _URL, URLRequest = _URLRequest, delegate = _delegate, additionalHTTPHeaders = _additionalHTTPHeaders,
- params = _params, userData = _userData, username = _username, password = _password, method = _method,
- forceBasicAuthentication = _forceBasicAuthentication, cachePolicy = _cachePolicy, cache = _cache,
- cacheTimeoutInterval = _cacheTimeoutInterval;
+@class TDOAuth;
+
+@synthesize URL = _URL;
+@synthesize URLRequest = _URLRequest;
+@synthesize delegate = _delegate;
+@synthesize additionalHTTPHeaders = _additionalHTTPHeaders;
+@synthesize params = _params;
+@synthesize userData = _userData;
+@synthesize authenticationType = _authenticationType;
+@synthesize username = _username;
+@synthesize password = _password;
+@synthesize method = _method;
+@synthesize cachePolicy = _cachePolicy;
+@synthesize cache = _cache;
+@synthesize cacheTimeoutInterval = _cacheTimeoutInterval;
+@synthesize OAuth1ConsumerKey = _OAuth1ConsumerKey;
+@synthesize OAuth1ConsumerSecret = _OAuth1ConsumerSecret;
+@synthesize OAuth1AccessToken = _OAuth1AccessToken;
+@synthesize OAuth1AccessTokenSecret = _OAuth1AccessTokenSecret;
+@synthesize OAuth2AccessToken = _OAuth2AccessToken;
+@synthesize OAuth2RefreshToken = _OAuth2RefreshToken;
@synthesize queue = _queue;
#if TARGET_OS_IPHONE
@@ -55,7 +73,7 @@ - (id)initWithURL:(NSURL*)URL {
if (self) {
_URL = [URL retain];
[self reset];
- _forceBasicAuthentication = NO;
+ _authenticationType = RKRequestAuthenticationTypeNone;
_cachePolicy = RKRequestCachePolicyDefault;
_cacheTimeoutInterval = 0;
}
@@ -137,7 +155,19 @@ - (void)dealloc {
[_password release];
_password = nil;
[_cache release];
- _cache = nil;
+ _cache = nil;
+ [_OAuth1ConsumerKey release];
+ _OAuth1ConsumerKey = nil;
+ [_OAuth1ConsumerSecret release];
+ _OAuth1ConsumerSecret = nil;
+ [_OAuth1AccessToken release];
+ _OAuth1AccessToken = nil;
+ [_OAuth1AccessTokenSecret release];
+ _OAuth1AccessTokenSecret = nil;
+ [_OAuth2AccessToken release];
+ _OAuth2AccessToken = nil;
+ [_OAuth2RefreshToken release];
+ _OAuth2RefreshToken = nil;
// Cleanup a background task if there is any
[self cleanupBackgroundTask];
@@ -148,6 +178,7 @@ - (void)dealloc {
- (BOOL)shouldSendParams {
return (_params && (_method != RKRequestMethodGET && _method != RKRequestMethodHEAD));
}
+
- (void)setRequestBody {
if ([self shouldSendParams]) {
// Prefer the use of a stream over a raw body
@@ -198,7 +229,7 @@ - (void)addHeadersToRequest {
}
// Add authentication headers so we don't have to deal with an extra cycle for each message requiring basic auth.
- if (self.forceBasicAuthentication) {
+ if (self.authenticationType == RKRequestAuthenticationTypeHTTPBasic) {
CFHTTPMessageRef dummyRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, (CFStringRef)[self HTTPMethod], (CFURLRef)[self URL], kCFHTTPVersion1_1);
CFHTTPMessageAddAuthentication(dummyRequest, nil, (CFStringRef)_username, (CFStringRef)_password,kCFHTTPAuthenticationSchemeBasic, FALSE);
@@ -208,6 +239,35 @@ - (void)addHeadersToRequest {
CFRelease(authorizationString);
}
+ // Add OAuth headers if is need it
+ // OAuth 1
+ if(self.authenticationType == RKRequestAuthenticationTypeOAuth1){
+ NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
+ for(NSString *parameter in [[_URL query] componentsSeparatedByString:@"&"]) {
+ NSArray *keyValuePair = [parameter componentsSeparatedByString:@"="];
+ [parameters setValue:[[keyValuePair objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
+ forKey:[keyValuePair objectAtIndex:0]];
+ }
+
+ NSURLRequest *echo = [TDOAuth URLRequestForPath:[self resourcePath]
+ GETParameters:parameters
+ scheme:[_URL scheme]
+ host:[_URL host]
+ consumerKey:self.OAuth1ConsumerKey
+ consumerSecret:self.OAuth1ConsumerSecret
+ accessToken:self.OAuth1AccessToken
+ tokenSecret:self.OAuth1AccessTokenSecret];
+ [_URLRequest setValue:[echo valueForHTTPHeaderField:@"Authorization"] forHTTPHeaderField:@"Authorization"];
+ [_URLRequest setValue:[echo valueForHTTPHeaderField:@"Accept-Encoding"] forHTTPHeaderField:@"Accept-Encoding"];
+ [_URLRequest setValue:[echo valueForHTTPHeaderField:@"User-Agent"] forHTTPHeaderField:@"User-Agent"];
+ }
+
+ // OAuth 2 valid request
+ if(self.authenticationType == RKRequestAuthenticationTypeOAuth2){
+ NSString *authorizationString = [NSString stringWithFormat:@"OAuth2 %@",self.OAuth2AccessToken];
+ [_URLRequest setValue:authorizationString forHTTPHeaderField:@"Authorization"];
+ }
+
if (self.cachePolicy & RKRequestCachePolicyEtag) {
NSString* etag = [self.cache etagForRequest:self];
if (etag) {
@@ -288,6 +348,7 @@ - (void)fireAsynchronousRequest {
}
RKResponse* response = [[[RKResponse alloc] initWithRequest:self] autorelease];
+
_connection = [[NSURLConnection connectionWithRequest:_URLRequest delegate:response] retain];
[[NSNotificationCenter defaultCenter] postNotificationName:RKRequestSentNotification object:self userInfo:nil];
View
2  Examples/RKTwitter/Classes/RKTwitterAppDelegate.m
@@ -18,7 +18,7 @@ @implementation RKTwitterAppDelegate
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
-// RKLogConfigureByName("RestKit/Network*", RKLogLevelTrace);
+ RKLogConfigureByName("RestKit/Network*", RKLogLevelTrace);
// Initialize RestKit
RKObjectManager* objectManager = [RKObjectManager objectManagerWithBaseURL:@"http://twitter.com"];
View
1  Examples/RKTwitter/Classes/RKTwitterViewController.m
@@ -19,7 +19,6 @@ - (void)loadTimeline {
// Load the object model via RestKit
RKObjectManager* objectManager = [RKObjectManager sharedManager];
objectManager.client.baseURL = @"http://www.twitter.com";
-
[objectManager loadObjectsAtResourcePath:@"/status/user_timeline/RestKit" delegate:self block:^(RKObjectLoader* loader) {
// Twitter returns statuses as a naked array in JSON, so we instruct the loader
// to user the appropriate object mapping
View
1  Examples/RKTwitter/RKTwitter.xcodeproj/project.pbxproj
@@ -462,6 +462,7 @@
isa = XCBuildConfiguration;
buildSettings = {
BUILD_STYLE = Debug;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
View
29 RestKit.xcodeproj/project.pbxproj
@@ -252,6 +252,8 @@
25A1CB52138419D900A7D5C9 /* libRestKitXMLParserLibxml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 25BD43BD1340315800DBACDD /* libRestKitXMLParserLibxml.a */; };
25AA85D613B1065000A95E2A /* RKObjectLoaderSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 25D1983F1368A9CE0090B617 /* RKObjectLoaderSpec.m */; };
25ACA5A11428FC260037FE98 /* RKObjectiveCPlusPlusSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 25ACA5A01428FC260037FE98 /* RKObjectiveCPlusPlusSpec.mm */; };
+ 25ACA5B21429054D0037FE98 /* TDOAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 25ACA5B01429054D0037FE98 /* TDOAuth.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 25ACA5B31429054D0037FE98 /* TDOAuth.m in Sources */ = {isa = PBXBuildFile; fileRef = 25ACA5B11429054D0037FE98 /* TDOAuth.m */; };
25ACF1AD13BB9D490067B380 /* RKURLSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 25ACF1AC13BB9D480067B380 /* RKURLSpec.m */; };
25B72A5813FBEDEE009A340D /* container_attributes.xml in Resources */ = {isa = PBXBuildFile; fileRef = 25B72A5713FBEDEE009A340D /* container_attributes.xml */; };
25B72A5A13FBF13B009A340D /* attributes_without_text_content.xml in Resources */ = {isa = PBXBuildFile; fileRef = 25B72A5913FBF13B009A340D /* attributes_without_text_content.xml */; };
@@ -320,6 +322,9 @@
73C89EF312A5BB9A000FE600 /* RKReachabilityObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C89EF012A5BB9A000FE600 /* RKReachabilityObserver.m */; };
73FE56C7126CB91600E0F30B /* RKURL.h in Headers */ = {isa = PBXBuildFile; fileRef = 73FE56C4126CB91600E0F30B /* RKURL.h */; settings = {ATTRIBUTES = (Public, ); }; };
73FE56C8126CB91600E0F30B /* RKURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 73FE56C5126CB91600E0F30B /* RKURL.m */; };
+ AF26F36A13EB40A9007F4868 /* RKOAuthClientSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = AF26F36913EB40A9007F4868 /* RKOAuthClientSpec.m */; };
+ AFD8CC8D13D8E61200E63E62 /* RKOAuthClient.h in Headers */ = {isa = PBXBuildFile; fileRef = AFD8CC8B13D8E61200E63E62 /* RKOAuthClient.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ AFD8CC8E13D8E61200E63E62 /* RKOAuthClient.m in Sources */ = {isa = PBXBuildFile; fileRef = AFD8CC8C13D8E61200E63E62 /* RKOAuthClient.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -686,6 +691,8 @@
25A1CB49138413DF00A7D5C9 /* RKMIMETypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKMIMETypes.h; sourceTree = "<group>"; };
25A1CB4A138413E000A7D5C9 /* RKMIMETypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKMIMETypes.m; sourceTree = "<group>"; };
25ACA5A01428FC260037FE98 /* RKObjectiveCPlusPlusSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RKObjectiveCPlusPlusSpec.mm; sourceTree = "<group>"; };
+ 25ACA5B01429054D0037FE98 /* TDOAuth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TDOAuth.h; sourceTree = "<group>"; };
+ 25ACA5B11429054D0037FE98 /* TDOAuth.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TDOAuth.m; sourceTree = "<group>"; };
25ACF1AC13BB9D480067B380 /* RKURLSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKURLSpec.m; sourceTree = "<group>"; };
25B72A5713FBEDEE009A340D /* container_attributes.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = container_attributes.xml; sourceTree = "<group>"; };
25B72A5913FBF13B009A340D /* attributes_without_text_content.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = attributes_without_text_content.xml; sourceTree = "<group>"; };
@@ -762,6 +769,9 @@
73FE56C4126CB91600E0F30B /* RKURL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKURL.h; sourceTree = "<group>"; };
73FE56C5126CB91600E0F30B /* RKURL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKURL.m; sourceTree = "<group>"; };
AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ AF26F36913EB40A9007F4868 /* RKOAuthClientSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKOAuthClientSpec.m; sourceTree = "<group>"; };
+ AFD8CC8B13D8E61200E63E62 /* RKOAuthClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKOAuthClient.h; sourceTree = "<group>"; };
+ AFD8CC8C13D8E61200E63E62 /* RKOAuthClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKOAuthClient.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -1083,6 +1093,8 @@
25A1CB3B1383F7DF00A7D5C9 /* RKRequestSerialization.h */,
25A1CB3C1383F7E100A7D5C9 /* RKRequestSerialization.m */,
257FB7051395DABB003A628E /* RKRequest_Internals.h */,
+ AFD8CC8B13D8E61200E63E62 /* RKOAuthClient.h */,
+ AFD8CC8C13D8E61200E63E62 /* RKOAuthClient.m */,
);
path = Network;
sourceTree = "<group>";
@@ -1246,6 +1258,7 @@
2590E6711252357200531FA8 /* Vendor */ = {
isa = PBXGroup;
children = (
+ 25ACA5AF1429054D0037FE98 /* TDOAuth */,
258F8500141061C3007AABCD /* PortableStaticLibrary.xcconfig */,
250D5BD913A0698100471F0E /* LibComponentLogging */,
37CA4C5D1410A7CE009A3DCE /* SOCKit */,
@@ -1461,6 +1474,7 @@
250C296B13411E60000A3551 /* RKRequestQueueSpec.m */,
25ACF1AC13BB9D480067B380 /* RKURLSpec.m */,
2559208213BD1A4000E9C29C /* RKParamsSpec.m */,
+ AF26F36913EB40A9007F4868 /* RKOAuthClientSpec.m */,
);
path = Network;
sourceTree = "<group>";
@@ -1529,6 +1543,16 @@
path = XML;
sourceTree = "<group>";
};
+ 25ACA5AF1429054D0037FE98 /* TDOAuth */ = {
+ isa = PBXGroup;
+ children = (
+ 25ACA5B01429054D0037FE98 /* TDOAuth.h */,
+ 25ACA5B11429054D0037FE98 /* TDOAuth.m */,
+ );
+ name = TDOAuth;
+ path = Vendor/TDOAuth;
+ sourceTree = "<group>";
+ };
25D638E01351268D000879B1 /* Other Frameworks */ = {
isa = PBXGroup;
children = (
@@ -1627,6 +1651,7 @@
253A08E0125522E300976E89 /* RKResponse.h in Headers */,
73C89EF212A5BB9A000FE600 /* RKReachabilityObserver.h in Headers */,
2538C05C12A6C44A0006903C /* RKRequestQueue.h in Headers */,
+ AFD8CC8D13D8E61200E63E62 /* RKOAuthClient.h in Headers */,
250C29FD134185D2000A3551 /* RKNetwork.h in Headers */,
3F278A44139D2AFF009AC3FA /* RKRequestCache.h in Headers */,
25A1CB3F138404CF00A7D5C9 /* RKRequestSerialization.h in Headers */,
@@ -1634,6 +1659,7 @@
3F278A40139D2AFF009AC3FA /* NSData+MD5.h in Headers */,
3F278A42139D2AFF009AC3FA /* NSString+MD5.h in Headers */,
257FB7061395DABB003A628E /* RKRequest_Internals.h in Headers */,
+ 25ACA5B21429054D0037FE98 /* TDOAuth.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2168,6 +2194,8 @@
3F278A41139D2AFF009AC3FA /* NSData+MD5.m in Sources */,
3F278A43139D2AFF009AC3FA /* NSString+MD5.m in Sources */,
3F278A45139D2AFF009AC3FA /* RKRequestCache.m in Sources */,
+ AFD8CC8E13D8E61200E63E62 /* RKOAuthClient.m in Sources */,
+ 25ACA5B31429054D0037FE98 /* TDOAuth.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2325,6 +2353,7 @@
37DEBA651411298300FDF847 /* NSStringRestKitSpec.m in Sources */,
3703ABB0141941FB00DB697A /* RKDotNetDateFormatterSpec.m in Sources */,
37959B201426D4E500F9A8C6 /* RKObjectMappingProviderSpec.m in Sources */,
+ AF26F36A13EB40A9007F4868 /* RKOAuthClientSpec.m in Sources */,
25ACA5A11428FC260037FE98 /* RKObjectiveCPlusPlusSpec.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
View
68 Specs/Network/RKOAuthClientSpec.m
@@ -0,0 +1,68 @@
+//
+// RKOAuthClientSpec.m
+// RestKit
+//
+// Created by Rodrigo Garcia on 8/4/11.
+// Copyright 2011 RestKit. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "RKSpecEnvironment.h"
+
+@interface RKOAuthClientSpec : RKSpec
+
+@end
+
+@implementation RKOAuthClientSpec
+
+- (void)itShouldGetAccessToken{
+ RKSpecResponseLoader *loader = [RKSpecResponseLoader responseLoader];
+ RKOAuthClient *client = RKSpecNewOAuthClient(loader);
+ client.authorizationCode = @"1234";
+ client.callbackURL = @"http://someURL.com";
+ [client validateAuthorizationCode];
+ [loader waitForResponse];
+ assertThatBool(loader.success, is(equalToBool(YES)));
+}
+
+- (void)itShouldNotGetAccessToken{
+ RKSpecResponseLoader *loader = [RKSpecResponseLoader responseLoader];
+ RKOAuthClient *client = RKSpecNewOAuthClient(loader);
+ client.authorizationCode = @"someInvalidAuthorizationCode";
+ client.callbackURL = @"http://someURL.com";
+ [client validateAuthorizationCode];
+ [loader waitForResponse];
+
+ assertThatBool(loader.success, is(equalToBool(NO)));
+
+}
+- (void)itShouldGetProtectedResource{
+ //TODO: Encapsulate this code in a correct manner
+ RKSpecResponseLoader *loader = [RKSpecResponseLoader responseLoader];
+ RKOAuthClient *client = RKSpecNewOAuthClient(loader);
+ client.authorizationCode = @"1234";
+ client.callbackURL = @"http://someURL.com";
+ [client validateAuthorizationCode];
+
+ RKSpecResponseLoader* resourceLoader = [RKSpecResponseLoader responseLoader];
+ RKClient *requestClient = [RKClient clientWithBaseURL:[client authorizationURL]];
+ requestClient.OAuth2AccessToken = client.accessToken;
+ requestClient.authenticationType = RKRequestAuthenticationTypeOAuth2;
+ RKRequest *request = [requestClient requestWithResourcePath:@"/me" delegate:resourceLoader];
+ [request send];
+ [resourceLoader waitForResponse];
+ assertThatBool(loader.success, is(equalToBool(YES)));
+}
+
+@end
View
1  Specs/Runner/RKSpecEnvironment.h
@@ -47,6 +47,7 @@ void RKSpecStubNetworkAvailability(BOOL isNetworkAvailable);
// Helpers for returning new instances that clear global state
RKClient* RKSpecNewClient(void);
RKObjectManager* RKSpecNewObjectManager(void);
+RKOAuthClient* RKSpecNewOAuthClient(RKSpecResponseLoader* loader);
RKManagedObjectStore* RKSpecNewManagedObjectStore(void);
void RKSpecClearCacheDirectory(void);
View
8 Specs/Runner/RKSpecEnvironment.m
@@ -49,6 +49,14 @@ void RKSpecStubNetworkAvailability(BOOL isNetworkAvailable) {
return client;
}
+RKOAuthClient* RKSpecNewOAuthClient(RKSpecResponseLoader* loader){
+ [loader setTimeout:10];
+ RKOAuthClient* client = [RKOAuthClient clientWithClientID:@"appID" secret:@"appSecret" delegate:loader];
+ client.authorizationURL = [NSString stringWithFormat:@"%@/oauth/authorize",RKSpecGetBaseURL()];
+ return client;
+}
+
+
RKObjectManager* RKSpecNewObjectManager(void) {
[RKObjectMapping setDefaultDateFormatters:nil];
RKObjectManager* objectManager = [RKObjectManager objectManagerWithBaseURL:RKSpecGetBaseURL()];
View
2  Specs/Runner/RKSpecResponseLoader.h
@@ -21,7 +21,7 @@
#import <Foundation/Foundation.h>
#import "RKObjectLoader.h"
-@interface RKSpecResponseLoader : NSObject <RKObjectLoaderDelegate> {
+@interface RKSpecResponseLoader : NSObject <RKObjectLoaderDelegate, RKOAuthClientDelegate> {
BOOL _awaitingResponse;
BOOL _success;
BOOL _wasCancelled;
View
15 Specs/Runner/RKSpecResponseLoader.m
@@ -39,7 +39,7 @@ + (RKSpecResponseLoader*)responseLoader {
- (id)init {
self = [super init];
if (self) {
- _timeout = 3;
+ _timeout = 4;
_awaitingResponse = NO;
}
@@ -118,4 +118,17 @@ - (void)objectLoaderDidLoadUnexpectedResponse:(RKObjectLoader*)objectLoader {
_unknownResponse = YES;
}
+#pragma mark - OAuth delegates
+
+- (void)OAuthClient:(RKOAuthClient *)client didAcquireAccessToken:(NSString *)token {
+ _awaitingResponse = NO;
+ _success = YES;
+}
+
+
+- (void)OAuthClient:(RKOAuthClient *)client didFailWithInvalidGrantError:(NSError *)error {
+ _awaitingResponse = NO;
+ _success = NO;
+}
+
@end
View
2  Specs/Server/lib/restkit.rb
@@ -1 +1,3 @@
require 'restkit/network/authentication'
+require 'restkit/network/oauth2'
+
View
37 Specs/Server/lib/restkit/network/oauth2.rb
@@ -0,0 +1,37 @@
+module RestKit
+ module Network
+ class OAuth2 < Sinatra::Base
+ ACCESS_TOKEN = '581b50dca15a9d41eb280d5cbd52c7da4fb564621247848171508dd9d0dfa551a2efe9d06e110e62335abf13b6446a5c49e4bf6007cd90518fbbb0d1535b4dbc'
+
+ post '/oauth/authorize' do
+ authorization_code = params[:code]
+ response = ""
+ if "1234" == authorization_code
+ response = { 'access_token' => ACCESS_TOKEN, 'timeout' => 31337 }.to_json
+ else
+ response = {'error' => 'invalid_grant', 'error_description' => 'authorization code not valid'}.to_json
+ end
+ content_type 'application/json'
+ response
+ end
+
+ get '/me' do
+ access_token = params[:Authorization]
+ tokenHeader = 'OAuth2 ' + ACCESS_TOKEN
+ response = ''
+ if access_token.nil?
+ status 401
+ response = {'message' => "A valid access_token is required to access."}.to_json
+ elsif tokenHeader == access_token
+ response = {'user_id' => 1, 'name' => 'Rod'}
+ else
+ status 401
+ response = {'message' => "Bad credentials"}.to_json
+ end
+ content_type 'application/json'
+ response
+ end
+
+ end
+ end
+end
View
3  Specs/Server/server.rb
@@ -13,12 +13,14 @@
require 'restkit/network/authentication'
require 'restkit/network/etags'
require 'restkit/network/timeout'
+require 'restkit/network/oauth2'
class RestKit::SpecServer < Sinatra::Base
self.app_file = __FILE__
use RestKit::Network::Authentication
use RestKit::Network::ETags
use RestKit::Network::Timeout
+ use RestKit::Network::OAuth2
configure do
register Sinatra::Reloader
@@ -146,7 +148,6 @@ class RestKit::SpecServer < Sinatra::Base
status 200
"Uploaded successfully to '#{upload_path}'"
end
-
# Return 200 after a delay
get '/ok-with-delay/:delay' do
sleep params[:delay].to_f
View
13 Vendor/TDOAuth/README
@@ -0,0 +1,13 @@
+A BSD-licensed single-header-single-source OAuth1 implementation.
+
+Usage is fully documented in the header.
+
+You will need to make your Xcode project link against CommonCrypto. Otherwise
+just drag and drop. Or better yet, use git submodules:
+
+ git submodule add https://github.com/tweetdeck/TDOAuth
+
+Tested against Foursquare and Twitter. If you test it against something else,
+please fork and list the services you used it against here. Thanks.
+
+—Max Howell <max@tweetdeck.com>
View
152 Vendor/TDOAuth/TDOAuth.h
@@ -0,0 +1,152 @@
+/*
+ Copyright 2011 TweetDeck Inc. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY TWEETDECK INC. ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ EVENT SHALL TWEETDECK INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ The views and conclusions contained in the software and documentation are
+ those of the authors and should not be interpreted as representing official
+ policies, either expressed or implied, of TweetDeck Inc.
+*/
+
+#import <Foundation/Foundation.h>
+
+/**
+ This OAuth implementation doesn't cover the whole spec (eg. it’s HMAC only).
+ But you'll find it works with almost all the OAuth implementations you need
+ to interact with in the wild. How ace is that?!
+*/
+
+@interface TDOAuth : NSObject {
+ NSURL *url;
+ NSString *signature_secret;
+ NSDictionary *params; // these are pre-percent encoded
+ NSString *method;
+}
+
+/**
+ @p unencodeParameters may be nil. Objects in the dictionary must be strings.
+ You are contracted to consume the NSURLRequest *immediately*. Don't put the
+ queryParameters in the path as a query string! Path MUST start with a slash!
+ Don't percent encode anything!
+*/
++ (NSURLRequest *)URLRequestForPath:(NSString *)unencodedPath_WITHOUT_Query
+ GETParameters:(NSDictionary *)unencodedParameters
+ host:(NSString *)host
+ consumerKey:(NSString *)consumerKey
+ consumerSecret:(NSString *)consumerSecret
+ accessToken:(NSString *)accessToken
+ tokenSecret:(NSString *)tokenSecret;
+
+/**
+ Sometimes the service in question insists on HTTPS for everything. They
+ shouldn't, since the whole point of OAuth1 is that you *don't* need HTTPS.
+ But whatever I guess.
+*/
++ (NSURLRequest *)URLRequestForPath:(NSString *)unencodedPath_WITHOUT_Query
+ GETParameters:(NSDictionary *)unencodedParameters
+ scheme:(NSString *)scheme
+ host:(NSString *)host
+ consumerKey:(NSString *)consumerKey
+ consumerSecret:(NSString *)consumerSecret
+ accessToken:(NSString *)accessToken
+ tokenSecret:(NSString *)tokenSecret;
+
+/**
+ We always POST with HTTPS. This is because at least half the time the user's
+ data is at least somewhat private, but also because apparently some carriers
+ mangle POST requests and break them. We saw this in France for example.
+ READ THE DOCUMENTATION FOR GET AS IT APPLIES HERE TOO!
+*/
++ (NSURLRequest *)URLRequestForPath:(NSString *)unencodedPath
+ POSTParameters:(NSDictionary *)unencodedParameters
+ host:(NSString *)host
+ consumerKey:(NSString *)consumerKey
+ consumerSecret:(NSString *)consumerSecret
+ accessToken:(NSString *)accessToken
+ tokenSecret:(NSString *)tokenSecret;
+@end
+
+
+/**
+ XAuth example (because you may otherwise be scratching your head):
+
+ NSURLRequest *xauth = [TDOAuth URLRequestForPath:@"/oauth/access_token"
+ POSTParameters:[NSDictionary dictionaryWithObjectsAndKeys:
+ username, @"x_auth_username",
+ password, @"x_auth_password",
+ @"client_auth", @"x_auth_mode",
+ nil]
+ host:@"api.twitter.com"
+ consumerKey:CONSUMER_KEY
+ consumerSecret:CONSUMER_SECRET
+ accessToken:nil
+ tokenSecret:nil];
+
+ OAuth Echo example (we have found that some consumers require HTTPS for the
+ echo, so to be safe we always do it):
+
+ NSURLRequest *echo = [TDOAuth URLRequestForPath:@"/1/account/verify_credentials.json"
+ GETParameters:nil
+ scheme:@"https"
+ host:@"api.twitter.com"
+ consumerKey:CONSUMER_KEY
+ consumerSecret:CONSUMER_SECRET
+ accessToken:accessToken
+ tokenSecret:tokenSecret];
+ NSMutableURLRequest *rq = [NSMutableURLRequest new];
+ [rq setValue:[[echo URL] absoluteString] forHTTPHeaderField:@"X-Auth-Service-Provider"];
+ [rq setValue:[echo valueForHTTPHeaderField:@"Authorization"] forHTTPHeaderField:@"X-Verify-Credentials-Authorization"];
+ // Now consume rq with an NSURLConnection
+ [rq release];
+*/
+
+
+/**
+ Suggested usage would be to make some categories for this class that
+ automatically adds both secrets, both tokens and host information. This
+ makes usage less cumbersome. Eg:
+
+ [TwitterOAuth GET:@"/1/statuses/home_timeline.json"];
+ [TwitterOAuth GET:@"/1/statuses/home_timeline.json" queryParameters:dictionary];
+
+ At TweetDeck we have TDAccount classes that represent separate user logins
+ for different services when instantiated.
+*/
+
+
+/**
+ OAuth requires the UTC timestamp we send to be accurate. The user's device
+ may not be, and often isn't. To work around this you should set this to the
+ UTC timestamp that you get back in HTTP header from OAuth servers.
+*/
+extern int TDOAuthUTCTimeOffset;
+
+
+
+@interface NSString (TweetDeck)
+- (NSString*)pcen;
+@end
+
+@interface NSMutableString (TweetDeck)
+- (NSMutableString *)add:(NSString *)s;
+- (NSMutableString *)chomp;
+@end
View
304 Vendor/TDOAuth/TDOAuth.m
@@ -0,0 +1,304 @@
+/*
+ Copyright 2011 TweetDeck Inc. All rights reserved.
+
+ Design and implementation, Max Howell, @mxcl.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY TweetDeck Inc. ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ EVENT SHALL TweetDeck Inc. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ The views and conclusions contained in the software and documentation are
+ those of the authors and should not be interpreted as representing official
+ policies, either expressed or implied, of TweetDeck Inc.
+*/
+
+#import "TDOAuth.h"
+#import <CommonCrypto/CommonHMAC.h>
+
+#ifndef TDOAuthURLRequestTimeout
+#define TDOAuthURLRequestTimeout 30.0
+#endif
+#define TDUserAgent @"RoDPOC"
+#ifndef TDUserAgent
+#warning Don't be a n00b! #define TDUserAgent!
+#endif
+
+int TDOAuthUTCTimeOffset = 0;
+
+
+
+@implementation NSString (TweetDeck)
+- (id)pcen {
+ NSString* rv = (NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef) self, NULL, (CFStringRef) @"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8);
+ return [rv autorelease];
+}
+@end
+
+@implementation NSNumber (TweetDeck)
+- (id)pcen {
+ // We permit NSNumbers as parameters, so we need to handle this function call
+ return [self stringValue];
+}
+@end
+
+@implementation NSMutableString (TweetDeck)
+- (id)add:(NSString *)s {
+ if ([s isKindOfClass:[NSString class]])
+ [self appendString:s];
+ if ([s isKindOfClass:[NSNumber class]])
+ [self appendString:[(NSNumber *)s stringValue]];
+ return self;
+}
+- (id)chomp {
+ const int N = [self length] - 1;
+ if (N >= 0)
+ [self deleteCharactersInRange:NSMakeRange(N, 1)];
+ return self;
+}
+@end
+
+
+
+// If your input string isn't 20 characters this won't work.
+static NSString* base64(const uint8_t* input) {
+ static const char map[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ NSMutableData* data = [NSMutableData dataWithLength:28];
+ uint8_t* out = (uint8_t*) data.mutableBytes;
+
+ for (int i = 0; i < 20;) {
+ int v = 0;
+ for (const int N = i + 3; i < N; i++) {
+ v <<= 8;
+ v |= 0xFF & input[i];
+ }
+ *out++ = map[v >> 18 & 0x3F];
+ *out++ = map[v >> 12 & 0x3F];
+ *out++ = map[v >> 6 & 0x3F];
+ *out++ = map[v >> 0 & 0x3F];
+ }
+ out[-2] = map[(input[19] & 0x0F) << 2];
+ out[-1] = '=';
+ return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
+}
+
+static NSString* nonce() {
+ CFUUIDRef uuid = CFUUIDCreate(NULL);
+ CFStringRef s = CFUUIDCreateString(NULL, uuid);
+ CFRelease(uuid);
+ return [(id)s autorelease];
+}
+
+static NSString* timestamp() {
+ time_t t;
+ time(&t);
+ mktime(gmtime(&t));
+ return [NSString stringWithFormat:@"%u", t + TDOAuthUTCTimeOffset];
+}
+
+
+
+@implementation TDOAuth
+
+- (id)initWithConsumerKey:(NSString *)consumerKey
+ consumerSecret:(NSString *)consumerSecret
+ accessToken:(NSString *)accessToken
+ tokenSecret:(NSString *)tokenSecret
+{
+ params = [NSDictionary dictionaryWithObjectsAndKeys:
+ consumerKey, @"oauth_consumer_key",
+ nonce(), @"oauth_nonce",
+ timestamp(), @"oauth_timestamp",
+ @"1.0", @"oauth_version",
+ @"HMAC-SHA1", @"oauth_signature_method",
+ accessToken, @"oauth_token",
+ // LEAVE accessToken last or you'll break XAuth attempts
+ nil];
+ signature_secret = [NSString stringWithFormat:@"%@&%@", consumerSecret, tokenSecret ?: @""];
+ return self;
+}
+
+- (NSString *)signature_base {
+ NSMutableString *p3 = [NSMutableString stringWithCapacity:256];
+ NSArray *keys = [[params allKeys] sortedArrayUsingSelector:@selector(compare:)];
+ for (NSString *key in keys)
+ [[[[p3 add:[key pcen]] add:@"="] add:[params objectForKey:key]] add:@"&"];
+ [p3 chomp];
+
+ return [NSString stringWithFormat:@"%@&%@%%3A%%2F%%2F%@%@&%@",
+ method,
+ url.scheme.lowercaseString,
+ url.host.lowercaseString.pcen,
+ url.path.pcen,
+ p3.pcen];
+}
+
+- (NSString *)signature {
+ NSData *sigbase = [[self signature_base] dataUsingEncoding:NSUTF8StringEncoding];
+ NSData *secret = [signature_secret dataUsingEncoding:NSUTF8StringEncoding];
+
+ uint8_t digest[20] = {0};
+ CCHmacContext cx;
+ CCHmacInit(&cx, kCCHmacAlgSHA1, secret.bytes, secret.length);
+ CCHmacUpdate(&cx, sigbase.bytes, sigbase.length);
+ CCHmacFinal(&cx, digest);
+
+ return base64(digest);
+}
+
+- (NSString *)authorizationHeader {
+ NSMutableString *header = [NSMutableString stringWithCapacity:512];
+ [header add:@"OAuth "];
+ for (NSString *key in params.allKeys)
+ [[[[header add:key] add:@"=\""] add:[params objectForKey:key]] add:@"\", "];
+ [[[header add:@"oauth_signature=\""] add:self.signature.pcen] add:@"\""];
+ return header;
+}
+
+- (NSMutableURLRequest *)request {
+ //TODO timeout interval depends on connectivity status
+ NSMutableURLRequest *rq = [NSMutableURLRequest requestWithURL:url
+ cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
+ timeoutInterval:TDOAuthURLRequestTimeout];
+#ifdef TDUserAgent
+ [rq setValue:TDUserAgent forHTTPHeaderField:@"User-Agent"];
+#endif
+ [rq setValue:[self authorizationHeader] forHTTPHeaderField:@"Authorization"];
+ [rq setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
+ [rq setHTTPMethod:method];
+ return rq;
+}
+
+// unencodedParameters are encoded and added to self->params, returns encoded queryString
+- (id)addParameters:(NSDictionary *)unencodedParameters {
+ if (!unencodedParameters.count)
+ return nil;
+
+ NSMutableString *queryString = [NSMutableString string];
+ NSMutableDictionary *encodedParameters = [NSMutableDictionary dictionaryWithDictionary:params];
+ for (NSString *key in unencodedParameters.allKeys) {
+ NSString *enkey = key.pcen;
+ NSString *envalue = [[unencodedParameters objectForKey:key] pcen];
+ [encodedParameters setObject:envalue forKey:enkey];
+ [[[[queryString add:enkey] add:@"="] add:envalue] add:@"&"];
+ }
+ [queryString chomp];
+
+ params = encodedParameters;
+
+ return queryString;
+}
+
++ (NSURLRequest *)URLRequestForPath:(NSString *)unencodedPathWithoutQuery
+ GETParameters:(NSDictionary *)unencodedParameters
+ host:(NSString *)host
+ consumerKey:(NSString *)consumerKey
+ consumerSecret:(NSString *)consumerSecret
+ accessToken:(NSString *)accessToken
+ tokenSecret:(NSString *)tokenSecret
+{
+ return [self URLRequestForPath:unencodedPathWithoutQuery
+ GETParameters:unencodedParameters
+ scheme:@"http"
+ host:host
+ consumerKey:consumerKey
+ consumerSecret:consumerSecret
+ accessToken:accessToken
+ tokenSecret:tokenSecret];
+}
+
++ (NSURLRequest *)URLRequestForPath:(NSString *)unencodedPathWithoutQuery
+ GETParameters:(NSDictionary *)unencodedParameters
+ scheme:(NSString *)scheme
+ host:(NSString *)host
+ consumerKey:(NSString *)consumerKey
+ consumerSecret:(NSString *)consumerSecret
+ accessToken:(NSString *)accessToken
+ tokenSecret:(NSString *)tokenSecret;
+{
+ if (!host || !unencodedPathWithoutQuery)
+ return nil;
+
+ TDOAuth *oauth = [[TDOAuth alloc] initWithConsumerKey:consumerKey
+ consumerSecret:consumerSecret
+ accessToken:accessToken
+ tokenSecret:tokenSecret];
+
+ // We don't use pcen as we don't want to percent encode eg. /, this
+ // is perhaps not the most all encompassing solution, but in practice
+ // it works everywhere and means that programmer error is *much* less
+ // likely.
+ NSString *encodedPathWithoutQuery = [unencodedPathWithoutQuery stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+
+ id path = [oauth addParameters:unencodedParameters];
+ if (path) {
+ [path insertString:@"?" atIndex:0];
+ [path insertString:encodedPathWithoutQuery atIndex:0];
+ } else {
+ path = encodedPathWithoutQuery;
+ }
+
+ oauth->method = @"GET";
+ oauth->url = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@://%@%@", scheme, host, path]];
+
+ NSURLRequest *rq = [oauth request];
+
+ NSLog(@"URL: %@",[[rq URL ] absoluteString]);
+
+
+ [oauth->url release];
+ [oauth release];
+ return rq;
+}
+
++ (NSURLRequest *)URLRequestForPath:(NSString *)unencodedPath
+ POSTParameters:(NSDictionary *)unencodedParameters
+ host:(NSString *)host
+ consumerKey:(NSString *)consumerKey
+ consumerSecret:(NSString *)consumerSecret
+ accessToken:(NSString *)accessToken
+ tokenSecret:(NSString *)tokenSecret
+{
+ if (!host || !unencodedPath)
+ return nil;
+
+ TDOAuth *oauth = [[TDOAuth alloc] initWithConsumerKey:consumerKey
+ consumerSecret:consumerSecret
+ accessToken:accessToken
+ tokenSecret:tokenSecret];
+ oauth->url = [[NSURL alloc] initWithScheme:@"https" host:host path:unencodedPath];
+ oauth->method = @"POST";
+
+ NSMutableString *postbody = [oauth addParameters:unencodedParameters];
+ NSMutableURLRequest *rq = [oauth request];
+
+ if (postbody.length) {
+ [rq setHTTPBody:[postbody dataUsingEncoding:NSUTF8StringEncoding]];
+ [rq setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
+ [rq setValue:[NSString stringWithFormat:@"%u", rq.HTTPBody.length] forHTTPHeaderField:@"Content-Length"];
+ }
+
+ [oauth->url release];
+ [oauth release];
+
+ return rq;
+}
+
+@end
Please sign in to comment.
Something went wrong with that request. Please try again.