Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Token revocation rfc7009 #630

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions AppAuth.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Source/AppAuthCore/OIDError.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,11 @@ typedef NS_ENUM(NSInteger, OIDErrorCodeOAuth) {
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError
*/
OIDErrorCodeOAuthInvalidClientMetadata = -13,

/*! @remarks unsupported_token_type
@see https://tools.ietf.org/html/rfc7009#section-2.2.1
*/
OIDErrorCodeOAuthUnsupportedTokenType = -14,

/*! @brief An authorization error occurring on the client rather than the server. For example,
due to a state mismatch or misconfiguration. Should be treated as an unrecoverable
Expand Down
1 change: 1 addition & 0 deletions Source/AppAuthCore/OIDErrorUtilities.m
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ + (OIDErrorCodeOAuth)OAuthErrorCodeFromString:(NSString *)errorCode {
@"invalid_client": @(OIDErrorCodeOAuthInvalidClient),
@"invalid_grant": @(OIDErrorCodeOAuthInvalidGrant),
@"unsupported_grant_type": @(OIDErrorCodeOAuthUnsupportedGrantType),
@"unsupported_token_type": @(OIDErrorCodeOAuthUnsupportedTokenType),
};
NSNumber *code = errorCodes[errorCode];
if (code) {
Expand Down
89 changes: 89 additions & 0 deletions Source/AppAuthCore/OIDRevokeTokenRequest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*! @file OIDRevokeTokenRequest.h
@brief AppAuth iOS SDK
@copyright
Copyright 2017 The AppAuth Authors. All Rights Reserved.
@copydetails
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 <Foundation/Foundation.h>

@class OIDServiceConfiguration;

NS_ASSUME_NONNULL_BEGIN

/*! @brief Represents a revoke token request.
@see https://tools.ietf.org/html/rfc7009#section-2.1
*/
@interface OIDRevokeTokenRequest : NSObject <NSCopying, NSSecureCoding>

/*! @brief The service's configuration.
@remarks This configuration specifies how to connect to a particular OAuth provider.
Configurations may be created manually, or via an OpenID Connect Discovery Document.
*/
@property(nonatomic, readonly) OIDServiceConfiguration *configuration;

/*! @brief REQUIRED. The token that the client wants to get revoked.
@remarks token
*/
@property(nonatomic, readonly) NSString *token;

/*! @brief OPTIONAL. A hint about the type of the token
submitted for revocation. Clients MAY pass this parameter in
order to help the authorization server to optimize the token
lookup.
@remarks token_type_hint
*/
@property(nonatomic, readonly, nullable) NSString *tokenTypeHint;

/*! @brief The client identifier.
@remarks client_id
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
*/
@property(nonatomic, readonly) NSString *clientID;

/*! @brief The client secret.
@remarks client_secret
@see https://tools.ietf.org/html/rfc6749#section-2.3.1
*/
@property(nonatomic, readonly, nullable) NSString *clientSecret;

/*! @internal
@brief Unavailable. Please use @c initWithConfiguration:token:tokenTypeHint:clientID:clientSecret:.
*/
- (instancetype)init NS_UNAVAILABLE;

/*! @brief Designated initializer.
@param configuration The service's configuration.
@param token The previously issued ID Token
@param tokenTypeHint The client's post-logout redirect URI.
*/
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
token:(NSString *)token
tokenTypeHint:(nullable NSString *)tokenTypeHint
clientID:(NSString *)clientID
clientSecret:(nullable NSString *)clientSecret
NS_DESIGNATED_INITIALIZER;

/*! @brief Designated initializer for NSSecureCoding.
@param aDecoder Unarchiver object to decode
*/
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;

/*! @brief Constructs an @c NSURLRequest representing the revoke token request.
@return An @c NSURLRequest representing the token request.
*/
- (NSURLRequest *)URLRequest;
@end

NS_ASSUME_NONNULL_END
203 changes: 203 additions & 0 deletions Source/AppAuthCore/OIDRevokeTokenRequest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/*! @file OIDEndSessionRequest.m
@brief AppAuth iOS SDK
@copyright
Copyright 2017 The AppAuth Authors. All Rights Reserved.
@copydetails
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 "OIDRevokeTokenRequest.h"

#import "OIDDefines.h"
#import "OIDServiceConfiguration.h"
#import "OIDURLQueryComponent.h"
#import "OIDTokenUtilities.h"

/*! @brief The key for the @c configuration property for @c NSSecureCoding
*/
static NSString *const kConfigurationKey = @"configuration";

/*! @brief Key used to encode the @c token property for @c NSSecureCoding, and on the URL request.
*/
static NSString *const kTokenKey = @"token";

/*! @brief Key used to encode the @c tokenTypeHint property for @c NSSecureCoding, and on the URL request.
*/
static NSString *const kTokenTypeHintKey = @"token_type_hint";

/*! @brief Key used to encode the @c clientID property for @c NSSecureCoding
*/
static NSString *const kClientIDKey = @"client_id";

/*! @brief Key used to encode the @c clientSecret property for @c NSSecureCoding
*/
static NSString *const kClientSecretKey = @"client_secret";

/*! @brief Assertion text for missing revoke_token_endpoint.
*/
static NSString *const OIDMissingRevokeTokenEndpointMessage =
@"The service configuration is missing an revoke_token_endpoint.";

@implementation OIDRevokeTokenRequest

- (instancetype)init
OID_UNAVAILABLE_USE_INITIALIZER(
@selector(initWithConfiguration:
token:
tokenTypeHint:
clientID:
clientSecret:)
)

- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
token:(NSString *)token
tokenTypeHint:(nullable NSString *)tokenTypeHint
clientID:(NSString *)clientID
clientSecret:(nullable NSString *)clientSecret
{
self = [super init];
if (self) {
_configuration = [configuration copy];
_token = [token copy];
_tokenTypeHint = [tokenTypeHint copy];
_clientID = [clientID copy];
_clientSecret = [clientSecret copy];
}
return self;
}

#pragma mark - NSCopying

- (instancetype)copyWithZone:(nullable NSZone *)zone {
// The documentation for NSCopying specifically advises us to return a reference to the original
// instance in the case where instances are immutable (as ours is):
// "Implement NSCopying by retaining the original instead of creating a new copy when the class
// and its contents are immutable."
return self;
}

#pragma mark - NSSecureCoding

+ (BOOL)supportsSecureCoding {
return YES;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
OIDServiceConfiguration *configuration = [aDecoder decodeObjectOfClass:[OIDServiceConfiguration class] forKey:kConfigurationKey];

NSString *token = [aDecoder decodeObjectOfClass:[NSString class] forKey:kTokenKey];
NSString *tokenTypeHint = [aDecoder decodeObjectOfClass:[NSString class] forKey:kTokenTypeHintKey];
NSString *clientID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kClientIDKey];
NSString *clientSecret = [aDecoder decodeObjectOfClass:[NSString class] forKey:kClientSecretKey];

self = [super init];
if (self) {
_configuration = [configuration copy];
_token = [token copy];
_tokenTypeHint = [tokenTypeHint copy];
_clientID = [clientID copy];
_clientSecret = [clientSecret copy];
}
return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_configuration forKey:kConfigurationKey];
[aCoder encodeObject:_token forKey:kTokenKey];
[aCoder encodeObject:_tokenTypeHint forKey:kTokenTypeHintKey];
[aCoder encodeObject:_clientID forKey:kClientIDKey];
[aCoder encodeObject:_clientSecret forKey:kClientSecretKey];
}

#pragma mark - NSObject overrides

- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, request: %@>",
NSStringFromClass([self class]),
(void *)self,
self.revokeTokenRequestURL];
}

#pragma mark -

/*! @brief Constructs the request URI.
@return A URL representing the token revocation request.
@see https://tools.ietf.org/html/rfc7009#section-2.1
*/
- (NSURL *)revokeTokenRequestURL {
return _configuration.revocationEndpoint;
}

/*! @brief Constructs the request body data by combining the request parameters using the
"application/x-www-form-urlencoded" format.
@return The data to pass to the token revocation request URL.
@see https://tools.ietf.org/html/rfc7009#section-2.1
*/
- (OIDURLQueryComponent *)revokeTokenRequestBody {
OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init];

// Add parameters, as applicable.
[query addParameter:kTokenKey value:_token];

if (_tokenTypeHint) {
[query addParameter:kTokenTypeHintKey value:_tokenTypeHint];
}

return query;
}

- (NSURLRequest *)URLRequest {
static NSString *const kHTTPPost = @"POST";
static NSString *const kHTTPContentTypeHeaderKey = @"Content-Type";
static NSString *const kHTTPContentTypeHeaderValue =
@"application/x-www-form-urlencoded; charset=UTF-8";

NSURL *tokenRequestURL = [self revokeTokenRequestURL];
NSMutableURLRequest *URLRequest = [[NSURLRequest requestWithURL:tokenRequestURL] mutableCopy];
URLRequest.HTTPMethod = kHTTPPost;
[URLRequest setValue:kHTTPContentTypeHeaderValue forHTTPHeaderField:kHTTPContentTypeHeaderKey];

OIDURLQueryComponent *bodyParameters = [self revokeTokenRequestBody];
NSMutableDictionary *httpHeaders = [[NSMutableDictionary alloc] init];

if (_clientSecret) {
// The client id and secret are encoded using the "application/x-www-form-urlencoded"
// encoding algorithm per RFC 6749 Section 2.3.1.
// https://tools.ietf.org/html/rfc6749#section-2.3.1
NSString *encodedClientID = [OIDTokenUtilities formUrlEncode:_clientID];
NSString *encodedClientSecret = [OIDTokenUtilities formUrlEncode:_clientSecret];

NSString *credentials =
[NSString stringWithFormat:@"%@:%@", encodedClientID, encodedClientSecret];
NSData *plainData = [credentials dataUsingEncoding:NSUTF8StringEncoding];
NSString *basicAuth = [plainData base64EncodedStringWithOptions:kNilOptions];

NSString *authValue = [NSString stringWithFormat:@"Basic %@", basicAuth];
[httpHeaders setObject:authValue forKey:@"Authorization"];
} else {
[bodyParameters addParameter:kClientIDKey value:_clientID];
}

// Constructs request with the body string and headers.
NSString *bodyString = [bodyParameters URLEncodedParameters];
NSData *body = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
URLRequest.HTTPBody = body;

for (id header in httpHeaders) {
[URLRequest setValue:httpHeaders[header] forHTTPHeaderField:header];
}

return URLRequest;
}

@end
47 changes: 47 additions & 0 deletions Source/AppAuthCore/OIDRevokeTokenResponse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*! @file OIDRevokeTokenResponse.h
@brief AppAuth iOS SDK
@copyright
Copyright 2017 The AppAuth Authors. All Rights Reserved.
@copydetails
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 <Foundation/Foundation.h>

@class OIDRevokeTokenRequest;

NS_ASSUME_NONNULL_BEGIN

/*! @brief Represents the response to a Revoke Token request.
@see https://tools.ietf.org/html/rfc7009#section-2.2
*/
@interface OIDRevokeTokenResponse : NSObject <NSCopying, NSSecureCoding>

/*! @brief The request which was serviced.
*/
@property(nonatomic, readonly) OIDRevokeTokenRequest *request;

/*! @internal
@brief Unavailable. Please use initWithRequest:.
*/
- (instancetype)init NS_UNAVAILABLE;

/*! @brief Designated initializer.
@param request The serviced request.
*/
- (instancetype)initWithRequest:(OIDRevokeTokenRequest *)request
NS_DESIGNATED_INITIALIZER;

@end

NS_ASSUME_NONNULL_END
Loading