adding pods method of package managing
This commit is contained in:
+272
@@ -0,0 +1,272 @@
|
||||
/*! @file OIDAuthState.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 OIDAuthorizationRequest;
|
||||
@class OIDAuthorizationResponse;
|
||||
@class OIDAuthState;
|
||||
@class OIDRegistrationResponse;
|
||||
@class OIDTokenResponse;
|
||||
@class OIDTokenRequest;
|
||||
@protocol OIDAuthStateChangeDelegate;
|
||||
@protocol OIDAuthStateErrorDelegate;
|
||||
@protocol OIDExternalUserAgent;
|
||||
@protocol OIDExternalUserAgentSession;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Represents a block used to call an action with a fresh access token.
|
||||
@param accessToken A valid access token if available.
|
||||
@param idToken A valid ID token if available.
|
||||
@param error The error if an error occurred.
|
||||
*/
|
||||
typedef void (^OIDAuthStateAction)(NSString *_Nullable accessToken,
|
||||
NSString *_Nullable idToken,
|
||||
NSError *_Nullable error);
|
||||
|
||||
/*! @brief The method called when the @c
|
||||
OIDAuthState.authStateByPresentingAuthorizationRequest:presentingViewController:callback:
|
||||
method has completed or failed.
|
||||
@param authState The auth state, if the authorization request succeeded.
|
||||
@param error The error if an error occurred.
|
||||
*/
|
||||
typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authState,
|
||||
NSError *_Nullable error);
|
||||
|
||||
/*! @brief A convenience class that retains the auth state between @c OIDAuthorizationResponse%s
|
||||
and @c OIDTokenResponse%s.
|
||||
*/
|
||||
@interface OIDAuthState : NSObject <NSSecureCoding>
|
||||
|
||||
/*! @brief The most recent refresh token received from the server.
|
||||
@discussion Rather than using this property directly, you should call
|
||||
@c OIDAuthState.performActionWithFreshTokens:.
|
||||
@remarks refresh_token
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *refreshToken;
|
||||
|
||||
/*! @brief The scope of the current authorization grant.
|
||||
@discussion This represents the latest scope returned by the server and may be a subset of the
|
||||
scope that was initially granted.
|
||||
@remarks scope
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *scope;
|
||||
|
||||
/*! @brief The most recent authorization response used to update the authorization state. For the
|
||||
implicit flow, this will contain the latest access token.
|
||||
*/
|
||||
@property(nonatomic, readonly) OIDAuthorizationResponse *lastAuthorizationResponse;
|
||||
|
||||
/*! @brief The most recent token response used to update this authorization state. This will
|
||||
contain the latest access token.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) OIDTokenResponse *lastTokenResponse;
|
||||
|
||||
/*! @brief The most recent registration response used to update this authorization state. This will
|
||||
contain the latest client credentials.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) OIDRegistrationResponse *lastRegistrationResponse;
|
||||
|
||||
/*! @brief The authorization error that invalidated this @c OIDAuthState.
|
||||
@discussion The authorization error encountered by @c OIDAuthState or set by the user via
|
||||
@c OIDAuthState.updateWithAuthorizationError: that invalidated this @c OIDAuthState.
|
||||
Authorization errors from @c OIDAuthState will always have a domain of
|
||||
@c ::OIDOAuthAuthorizationErrorDomain or @c ::OIDOAuthTokenErrorDomain. Note: that after
|
||||
unarchiving the @c OIDAuthState object, the \NSError_userInfo property of this error will
|
||||
be nil.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSError *authorizationError;
|
||||
|
||||
/*! @brief Returns YES if the authorization state is not known to be invalid.
|
||||
@discussion Returns YES if no OAuth errors have been received, and the last call resulted in a
|
||||
successful access token or id token. This does not mean that the access is fresh - just
|
||||
that it was valid the last time it was used. Note that network and other transient errors
|
||||
do not invalidate the authorized state. If NO, you should authenticate the user again,
|
||||
using a fresh authorization request. Invalid @c OIDAuthState objects may still be useful in
|
||||
that case, to hint at the previously authorized user and streamline the re-authentication
|
||||
experience.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL isAuthorized;
|
||||
|
||||
/*! @brief The @c OIDAuthStateChangeDelegate delegate.
|
||||
@discussion Use the delegate to observe state changes (and update storage) as well as error
|
||||
states.
|
||||
*/
|
||||
@property(nonatomic, weak, nullable) id<OIDAuthStateChangeDelegate> stateChangeDelegate;
|
||||
|
||||
/*! @brief The @c OIDAuthStateErrorDelegate delegate.
|
||||
@discussion Use the delegate to observe state changes (and update storage) as well as error
|
||||
states.
|
||||
*/
|
||||
@property(nonatomic, weak, nullable) id<OIDAuthStateErrorDelegate> errorDelegate;
|
||||
|
||||
/*! @brief Convenience method to create a @c OIDAuthState by presenting an authorization request
|
||||
and performing the authorization code exchange in the case of code flow requests. For
|
||||
the hybrid flow, the caller should validate the id_token and c_hash, then perform the token
|
||||
request (@c OIDAuthorizationService.performTokenRequest:callback:)
|
||||
and update the OIDAuthState with the results (@c
|
||||
OIDAuthState.updateWithTokenResponse:error:).
|
||||
@param authorizationRequest The authorization request to present.
|
||||
@param externalUserAgent A external user agent that can present an external user-agent request.
|
||||
@param callback The method called when the request has completed or failed.
|
||||
@return A @c OIDExternalUserAgentSession instance which will terminate when it
|
||||
receives a @c OIDExternalUserAgentSession.cancel message, or after processing a
|
||||
@c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message.
|
||||
*/
|
||||
+ (id<OIDExternalUserAgentSession>)
|
||||
authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest
|
||||
externalUserAgent:(id<OIDExternalUserAgent>)externalUserAgent
|
||||
callback:(OIDAuthStateAuthorizationCallback)callback;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use @c initWithAuthorizationResponse:.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Creates an auth state from an authorization response.
|
||||
@param authorizationResponse The authorization response.
|
||||
*/
|
||||
- (instancetype)initWithAuthorizationResponse:(OIDAuthorizationResponse *)authorizationResponse;
|
||||
|
||||
/*! @brief Creates an auth state from an authorization and token response.
|
||||
@param authorizationResponse The authorization response.
|
||||
@param tokenResponse The token response.
|
||||
*/
|
||||
- (instancetype)initWithAuthorizationResponse:(OIDAuthorizationResponse *)authorizationResponse
|
||||
tokenResponse:(nullable OIDTokenResponse *)tokenResponse;
|
||||
|
||||
/*! @brief Creates an auth state from an registration response.
|
||||
@param registrationResponse The registration response.
|
||||
*/
|
||||
- (instancetype)initWithRegistrationResponse:(OIDRegistrationResponse *)registrationResponse;
|
||||
|
||||
/*! @brief Creates an auth state from an authorization, token and registration response.
|
||||
@param authorizationResponse The authorization response.
|
||||
@param tokenResponse The token response.
|
||||
@param registrationResponse The registration response.
|
||||
*/
|
||||
- (instancetype)initWithAuthorizationResponse:
|
||||
(nullable OIDAuthorizationResponse *)authorizationResponse
|
||||
tokenResponse:(nullable OIDTokenResponse *)tokenResponse
|
||||
registrationResponse:(nullable OIDRegistrationResponse *)registrationResponse
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/*! @brief Updates the authorization state based on a new authorization response.
|
||||
@param authorizationResponse The new authorization response to update the state with.
|
||||
@param error Any error encountered when performing the authorization request. Errors in the
|
||||
domain @c ::OIDOAuthAuthorizationErrorDomain are reflected in the auth state, other errors
|
||||
are assumed to be transient, and ignored.
|
||||
@discussion Typically called with the response from an incremental authorization request,
|
||||
or if using the implicit flow. Will clear the @c #lastTokenResponse property.
|
||||
*/
|
||||
- (void)updateWithAuthorizationResponse:(nullable OIDAuthorizationResponse *)authorizationResponse
|
||||
error:(nullable NSError *)error;
|
||||
|
||||
/*! @brief Updates the authorization state based on a new token response.
|
||||
@param tokenResponse The new token response to update the state from.
|
||||
@param error Any error encountered when performing the authorization request. Errors in the
|
||||
domain @c ::OIDOAuthTokenErrorDomain are reflected in the auth state, other errors
|
||||
are assumed to be transient, and ignored.
|
||||
@discussion Typically called with the response from an authorization code exchange, or a token
|
||||
refresh.
|
||||
*/
|
||||
- (void)updateWithTokenResponse:(nullable OIDTokenResponse *)tokenResponse
|
||||
error:(nullable NSError *)error;
|
||||
|
||||
/*! @brief Updates the authorization state based on a new registration response.
|
||||
@param registrationResponse The new registration response to update the state with.
|
||||
@discussion Typically called with the response from a successful client registration
|
||||
request. Will reset the auth state.
|
||||
*/
|
||||
- (void)updateWithRegistrationResponse:(nullable OIDRegistrationResponse *)registrationResponse;
|
||||
|
||||
/*! @brief Updates the authorization state based on an authorization error.
|
||||
@param authorizationError The authorization error.
|
||||
@discussion Call this method if you receive an authorization error during an API call to
|
||||
invalidate the authentication state of this @c OIDAuthState. Don't call with errors
|
||||
unrelated to authorization, such as transient network errors.
|
||||
The OIDAuthStateErrorDelegate.authState:didEncounterAuthorizationError: method of
|
||||
@c #errorDelegate will be called with the error.
|
||||
You may optionally use the convenience method
|
||||
OIDErrorUtilities.resourceServerAuthorizationErrorWithCode:errorResponse:underlyingError:
|
||||
to create \NSError objects for use here.
|
||||
The latest error received is stored in @c #authorizationError. Note: that after unarchiving
|
||||
this object, the \NSError_userInfo property of this error will be nil.
|
||||
*/
|
||||
- (void)updateWithAuthorizationError:(NSError *)authorizationError;
|
||||
|
||||
/*! @brief Calls the block with a valid access token (refreshing it first, if needed), or if a
|
||||
refresh was needed and failed, with the error that caused it to fail.
|
||||
@param action The block to execute with a fresh token. This block will be executed on the main
|
||||
thread.
|
||||
*/
|
||||
- (void)performActionWithFreshTokens:(OIDAuthStateAction)action;
|
||||
|
||||
/*! @brief Calls the block with a valid access token (refreshing it first, if needed), or if a
|
||||
refresh was needed and failed, with the error that caused it to fail.
|
||||
@param action The block to execute with a fresh token. This block will be executed on the main
|
||||
thread.
|
||||
@param additionalParameters Additional parameters for the token request if token is
|
||||
refreshed.
|
||||
*/
|
||||
- (void)performActionWithFreshTokens:(OIDAuthStateAction)action
|
||||
additionalRefreshParameters:
|
||||
(nullable NSDictionary<NSString *, NSString *> *)additionalParameters;
|
||||
|
||||
/*! @brief Calls the block with a valid access token (refreshing it first, if needed), or if a
|
||||
refresh was needed and failed, with the error that caused it to fail.
|
||||
@param action The block to execute with a fresh token. This block will be executed on the main
|
||||
thread.
|
||||
@param additionalParameters Additional parameters for the token request if token is
|
||||
refreshed.
|
||||
@param dispatchQueue The dispatchQueue on which to dispatch the action block.
|
||||
*/
|
||||
- (void)performActionWithFreshTokens:(OIDAuthStateAction)action
|
||||
additionalRefreshParameters:
|
||||
(nullable NSDictionary<NSString *, NSString *> *)additionalParameters
|
||||
dispatchQueue:(dispatch_queue_t)dispatchQueue;
|
||||
|
||||
/*! @brief Forces a token refresh the next time @c OIDAuthState.performActionWithFreshTokens: is
|
||||
called, even if the current tokens are considered valid.
|
||||
*/
|
||||
- (void)setNeedsTokenRefresh;
|
||||
|
||||
/*! @brief Creates a token request suitable for refreshing an access token.
|
||||
@return A @c OIDTokenRequest suitable for using a refresh token to obtain a new access token.
|
||||
@discussion After performing the refresh, call @c OIDAuthState.updateWithTokenResponse:error:
|
||||
to update the authorization state based on the response. Rather than doing the token refresh
|
||||
yourself, you should use @c OIDAuthState.performActionWithFreshTokens:.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-1.5
|
||||
*/
|
||||
- (nullable OIDTokenRequest *)tokenRefreshRequest;
|
||||
|
||||
/*! @brief Creates a token request suitable for refreshing an access token.
|
||||
@param additionalParameters Additional parameters for the token request.
|
||||
@return A @c OIDTokenRequest suitable for using a refresh token to obtain a new access token.
|
||||
@discussion After performing the refresh, call @c OIDAuthState.updateWithTokenResponse:error:
|
||||
to update the authorization state based on the response. Rather than doing the token refresh
|
||||
yourself, you should use @c OIDAuthState.performActionWithFreshTokens:.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-1.5
|
||||
*/
|
||||
- (nullable OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters:
|
||||
(nullable NSDictionary<NSString *, NSString *> *)additionalParameters;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+570
@@ -0,0 +1,570 @@
|
||||
/*! @file OIDAuthState.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDAuthState.h"
|
||||
|
||||
#import "OIDAuthStateChangeDelegate.h"
|
||||
#import "OIDAuthStateErrorDelegate.h"
|
||||
#import "OIDAuthorizationRequest.h"
|
||||
#import "OIDAuthorizationResponse.h"
|
||||
#import "OIDAuthorizationService.h"
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDError.h"
|
||||
#import "OIDErrorUtilities.h"
|
||||
#import "OIDRegistrationResponse.h"
|
||||
#import "OIDTokenRequest.h"
|
||||
#import "OIDTokenResponse.h"
|
||||
#import "OIDTokenUtilities.h"
|
||||
|
||||
/*! @brief Key used to encode the @c refreshToken property for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kRefreshTokenKey = @"refreshToken";
|
||||
|
||||
/*! @brief Key used to encode the @c needsTokenRefresh property for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kNeedsTokenRefreshKey = @"needsTokenRefresh";
|
||||
|
||||
/*! @brief Key used to encode the @c scope property for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kScopeKey = @"scope";
|
||||
|
||||
/*! @brief Key used to encode the @c lastAuthorizationResponse property for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kLastAuthorizationResponseKey = @"lastAuthorizationResponse";
|
||||
|
||||
/*! @brief Key used to encode the @c lastTokenResponse property for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kLastTokenResponseKey = @"lastTokenResponse";
|
||||
|
||||
/*! @brief Key used to encode the @c lastOAuthError property for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kAuthorizationErrorKey = @"authorizationError";
|
||||
|
||||
/*! @brief The exception thrown when a developer tries to create a refresh request from an
|
||||
authorization request with no authorization code.
|
||||
*/
|
||||
static NSString *const kRefreshTokenRequestException =
|
||||
@"Attempted to create a token refresh request from a token response with no refresh token.";
|
||||
|
||||
/*! @brief Number of seconds the access token is refreshed before it actually expires.
|
||||
*/
|
||||
static const NSUInteger kExpiryTimeTolerance = 60;
|
||||
|
||||
/*! @brief Object to hold OIDAuthState pending actions.
|
||||
*/
|
||||
@interface OIDAuthStatePendingAction : NSObject
|
||||
@property(nonatomic, readonly, nullable) OIDAuthStateAction action;
|
||||
@property(nonatomic, readonly, nullable) dispatch_queue_t dispatchQueue;
|
||||
@end
|
||||
@implementation OIDAuthStatePendingAction
|
||||
- (id)initWithAction:(OIDAuthStateAction)action andDispatchQueue:(dispatch_queue_t)dispatchQueue {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_action = action;
|
||||
_dispatchQueue = dispatchQueue;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface OIDAuthState ()
|
||||
|
||||
/*! @brief The access token generated by the authorization server.
|
||||
@discussion Rather than using this property directly, you should call
|
||||
@c OIDAuthState.withFreshTokenPerformAction:.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *accessToken;
|
||||
|
||||
/*! @brief The approximate expiration date & time of the access token.
|
||||
@discussion Rather than using this property directly, you should call
|
||||
@c OIDAuthState.withFreshTokenPerformAction:.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDate *accessTokenExpirationDate;
|
||||
|
||||
/*! @brief ID Token value associated with the authenticated session.
|
||||
@discussion Rather than using this property directly, you should call
|
||||
OIDAuthState.withFreshTokenPerformAction:.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *idToken;
|
||||
|
||||
/*! @brief Private method, called when the internal state changes.
|
||||
*/
|
||||
- (void)didChangeState;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation OIDAuthState {
|
||||
/*! @brief Array of pending actions (use @c _pendingActionsSyncObject to synchronize access).
|
||||
*/
|
||||
NSMutableArray *_pendingActions;
|
||||
|
||||
/*! @brief Object for synchronizing access to @c pendingActions.
|
||||
*/
|
||||
id _pendingActionsSyncObject;
|
||||
|
||||
/*! @brief If YES, tokens will be refreshed on the next API call regardless of expiry.
|
||||
*/
|
||||
BOOL _needsTokenRefresh;
|
||||
}
|
||||
|
||||
#pragma mark - Convenience initializers
|
||||
|
||||
+ (id<OIDExternalUserAgentSession>)
|
||||
authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest
|
||||
externalUserAgent:(id<OIDExternalUserAgent>)externalUserAgent
|
||||
callback:(OIDAuthStateAuthorizationCallback)callback {
|
||||
// presents the authorization request
|
||||
id<OIDExternalUserAgentSession> authFlowSession = [OIDAuthorizationService
|
||||
presentAuthorizationRequest:authorizationRequest
|
||||
externalUserAgent:externalUserAgent
|
||||
callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse,
|
||||
NSError *_Nullable authorizationError) {
|
||||
// inspects response and processes further if needed (e.g. authorization
|
||||
// code exchange)
|
||||
if (authorizationResponse) {
|
||||
if ([authorizationRequest.responseType
|
||||
isEqualToString:OIDResponseTypeCode]) {
|
||||
// if the request is for the code flow (NB. not hybrid), assumes the
|
||||
// code is intended for this client, and performs the authorization
|
||||
// code exchange
|
||||
OIDTokenRequest *tokenExchangeRequest =
|
||||
[authorizationResponse tokenExchangeRequest];
|
||||
[OIDAuthorizationService performTokenRequest:tokenExchangeRequest
|
||||
originalAuthorizationResponse:authorizationResponse
|
||||
callback:^(OIDTokenResponse *_Nullable tokenResponse,
|
||||
NSError *_Nullable tokenError) {
|
||||
OIDAuthState *authState;
|
||||
if (tokenResponse) {
|
||||
authState = [[OIDAuthState alloc]
|
||||
initWithAuthorizationResponse:
|
||||
authorizationResponse
|
||||
tokenResponse:tokenResponse];
|
||||
}
|
||||
callback(authState, tokenError);
|
||||
}];
|
||||
} else {
|
||||
// hybrid flow (code id_token). Two possible cases:
|
||||
// 1. The code is not for this client, ie. will be sent to a
|
||||
// webservice that performs the id token verification and token
|
||||
// exchange
|
||||
// 2. The code is for this client and, for security reasons, the
|
||||
// application developer must verify the id_token signature and
|
||||
// c_hash before calling the token endpoint
|
||||
OIDAuthState *authState = [[OIDAuthState alloc]
|
||||
initWithAuthorizationResponse:authorizationResponse];
|
||||
callback(authState, authorizationError);
|
||||
}
|
||||
} else {
|
||||
callback(nil, authorizationError);
|
||||
}
|
||||
}];
|
||||
return authFlowSession;
|
||||
}
|
||||
|
||||
#pragma mark - Initializers
|
||||
|
||||
- (nonnull instancetype)init
|
||||
OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithAuthorizationResponse:tokenResponse:))
|
||||
|
||||
/*! @brief Creates an auth state from an authorization response.
|
||||
@param authorizationResponse The authorization response.
|
||||
*/
|
||||
- (instancetype)initWithAuthorizationResponse:(OIDAuthorizationResponse *)authorizationResponse {
|
||||
return [self initWithAuthorizationResponse:authorizationResponse tokenResponse:nil];
|
||||
}
|
||||
|
||||
|
||||
/*! @brief Designated initializer.
|
||||
@param authorizationResponse The authorization response.
|
||||
@discussion Creates an auth state from an authorization response and token response.
|
||||
*/
|
||||
- (instancetype)initWithAuthorizationResponse:(OIDAuthorizationResponse *)authorizationResponse
|
||||
tokenResponse:(nullable OIDTokenResponse *)tokenResponse {
|
||||
return [self initWithAuthorizationResponse:authorizationResponse
|
||||
tokenResponse:tokenResponse
|
||||
registrationResponse:nil];
|
||||
}
|
||||
|
||||
/*! @brief Creates an auth state from an registration response.
|
||||
@param registrationResponse The registration response.
|
||||
*/
|
||||
- (instancetype)initWithRegistrationResponse:(OIDRegistrationResponse *)registrationResponse {
|
||||
return [self initWithAuthorizationResponse:nil
|
||||
tokenResponse:nil
|
||||
registrationResponse:registrationResponse];
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthorizationResponse:
|
||||
(nullable OIDAuthorizationResponse *)authorizationResponse
|
||||
tokenResponse:(nullable OIDTokenResponse *)tokenResponse
|
||||
registrationResponse:(nullable OIDRegistrationResponse *)registrationResponse {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_pendingActionsSyncObject = [[NSObject alloc] init];
|
||||
|
||||
if (registrationResponse) {
|
||||
[self updateWithRegistrationResponse:registrationResponse];
|
||||
}
|
||||
|
||||
if (authorizationResponse) {
|
||||
[self updateWithAuthorizationResponse:authorizationResponse error:nil];
|
||||
}
|
||||
|
||||
if (tokenResponse) {
|
||||
[self updateWithTokenResponse:tokenResponse error:nil];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - NSObject overrides
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"<%@: %p, isAuthorized: %@, refreshToken: \"%@\", "
|
||||
"scope: \"%@\", accessToken: \"%@\", "
|
||||
"accessTokenExpirationDate: %@, idToken: \"%@\", "
|
||||
"lastAuthorizationResponse: %@, lastTokenResponse: %@, "
|
||||
"lastRegistrationResponse: %@, authorizationError: %@>",
|
||||
NSStringFromClass([self class]),
|
||||
(void *)self,
|
||||
(self.isAuthorized) ? @"YES" : @"NO",
|
||||
[OIDTokenUtilities redact:_refreshToken],
|
||||
_scope,
|
||||
[OIDTokenUtilities redact:self.accessToken],
|
||||
self.accessTokenExpirationDate,
|
||||
[OIDTokenUtilities redact:self.idToken],
|
||||
_lastAuthorizationResponse,
|
||||
_lastTokenResponse,
|
||||
_lastRegistrationResponse,
|
||||
_authorizationError];
|
||||
}
|
||||
|
||||
#pragma mark - NSSecureCoding
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
_lastAuthorizationResponse = [aDecoder decodeObjectOfClass:[OIDAuthorizationResponse class]
|
||||
forKey:kLastAuthorizationResponseKey];
|
||||
_lastTokenResponse = [aDecoder decodeObjectOfClass:[OIDTokenResponse class]
|
||||
forKey:kLastTokenResponseKey];
|
||||
self = [self initWithAuthorizationResponse:_lastAuthorizationResponse
|
||||
tokenResponse:_lastTokenResponse];
|
||||
if (self) {
|
||||
_authorizationError =
|
||||
[aDecoder decodeObjectOfClass:[NSError class] forKey:kAuthorizationErrorKey];
|
||||
_scope = [aDecoder decodeObjectOfClass:[NSString class] forKey:kScopeKey];
|
||||
_refreshToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:kRefreshTokenKey];
|
||||
_needsTokenRefresh = [aDecoder decodeBoolForKey:kNeedsTokenRefreshKey];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:_lastAuthorizationResponse forKey:kLastAuthorizationResponseKey];
|
||||
[aCoder encodeObject:_lastTokenResponse forKey:kLastTokenResponseKey];
|
||||
if (_authorizationError) {
|
||||
NSError *codingSafeAuthorizationError = [NSError errorWithDomain:_authorizationError.domain
|
||||
code:_authorizationError.code
|
||||
userInfo:nil];
|
||||
[aCoder encodeObject:codingSafeAuthorizationError forKey:kAuthorizationErrorKey];
|
||||
}
|
||||
[aCoder encodeObject:_scope forKey:kScopeKey];
|
||||
[aCoder encodeObject:_refreshToken forKey:kRefreshTokenKey];
|
||||
[aCoder encodeBool:_needsTokenRefresh forKey:kNeedsTokenRefreshKey];
|
||||
}
|
||||
|
||||
#pragma mark - Private convenience getters
|
||||
|
||||
- (NSString *)accessToken {
|
||||
if (_authorizationError) {
|
||||
return nil;
|
||||
}
|
||||
return _lastTokenResponse ? _lastTokenResponse.accessToken
|
||||
: _lastAuthorizationResponse.accessToken;
|
||||
}
|
||||
|
||||
- (NSString *)tokenType {
|
||||
if (_authorizationError) {
|
||||
return nil;
|
||||
}
|
||||
return _lastTokenResponse ? _lastTokenResponse.tokenType
|
||||
: _lastAuthorizationResponse.tokenType;
|
||||
}
|
||||
|
||||
- (NSDate *)accessTokenExpirationDate {
|
||||
if (_authorizationError) {
|
||||
return nil;
|
||||
}
|
||||
return _lastTokenResponse ? _lastTokenResponse.accessTokenExpirationDate
|
||||
: _lastAuthorizationResponse.accessTokenExpirationDate;
|
||||
}
|
||||
|
||||
- (NSString *)idToken {
|
||||
if (_authorizationError) {
|
||||
return nil;
|
||||
}
|
||||
return _lastTokenResponse ? _lastTokenResponse.idToken
|
||||
: _lastAuthorizationResponse.idToken;
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (BOOL)isAuthorized {
|
||||
return !self.authorizationError && (self.accessToken || self.idToken || self.refreshToken);
|
||||
}
|
||||
|
||||
#pragma mark - Updating the state
|
||||
|
||||
- (void)updateWithRegistrationResponse:(OIDRegistrationResponse *)registrationResponse {
|
||||
_lastRegistrationResponse = registrationResponse;
|
||||
_refreshToken = nil;
|
||||
_scope = nil;
|
||||
_lastAuthorizationResponse = nil;
|
||||
_lastTokenResponse = nil;
|
||||
_authorizationError = nil;
|
||||
[self didChangeState];
|
||||
}
|
||||
|
||||
- (void)updateWithAuthorizationResponse:(nullable OIDAuthorizationResponse *)authorizationResponse
|
||||
error:(nullable NSError *)error {
|
||||
// If the error is an OAuth authorization error, updates the state. Other errors are ignored.
|
||||
if (error.domain == OIDOAuthAuthorizationErrorDomain) {
|
||||
[self updateWithAuthorizationError:error];
|
||||
return;
|
||||
}
|
||||
if (!authorizationResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
_lastAuthorizationResponse = authorizationResponse;
|
||||
|
||||
// clears the last token response and refresh token as these now relate to an old authorization
|
||||
// that is no longer relevant
|
||||
_lastTokenResponse = nil;
|
||||
_refreshToken = nil;
|
||||
_authorizationError = nil;
|
||||
|
||||
// if the response's scope is nil, it means that it equals that of the request
|
||||
// see: https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
_scope = (authorizationResponse.scope) ? authorizationResponse.scope
|
||||
: authorizationResponse.request.scope;
|
||||
|
||||
[self didChangeState];
|
||||
}
|
||||
|
||||
- (void)updateWithTokenResponse:(nullable OIDTokenResponse *)tokenResponse
|
||||
error:(nullable NSError *)error {
|
||||
if (_authorizationError) {
|
||||
// Calling updateWithTokenResponse while in an error state probably means the developer obtained
|
||||
// a new token and did the exchange without also calling updateWithAuthorizationResponse.
|
||||
// Attempts to handle gracefully, but warns the developer that this is unexpected.
|
||||
NSLog(@"OIDAuthState:updateWithTokenResponse should not be called in an error state [%@] call"
|
||||
"updateWithAuthorizationResponse with the result of the fresh authorization response"
|
||||
"first",
|
||||
_authorizationError);
|
||||
|
||||
_authorizationError = nil;
|
||||
}
|
||||
|
||||
// If the error is an OAuth authorization error, updates the state. Other errors are ignored.
|
||||
if (error.domain == OIDOAuthTokenErrorDomain) {
|
||||
[self updateWithAuthorizationError:error];
|
||||
return;
|
||||
}
|
||||
if (!tokenResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
_lastTokenResponse = tokenResponse;
|
||||
|
||||
// updates the scope and refresh token if they are present on the TokenResponse.
|
||||
// according to the spec, these may be changed by the server, including when refreshing the
|
||||
// access token. See: https://tools.ietf.org/html/rfc6749#section-5.1 and
|
||||
// https://tools.ietf.org/html/rfc6749#section-6
|
||||
if (tokenResponse.scope) {
|
||||
_scope = tokenResponse.scope;
|
||||
}
|
||||
if (tokenResponse.refreshToken) {
|
||||
_refreshToken = tokenResponse.refreshToken;
|
||||
}
|
||||
|
||||
[self didChangeState];
|
||||
}
|
||||
|
||||
- (void)updateWithAuthorizationError:(NSError *)oauthError {
|
||||
_authorizationError = oauthError;
|
||||
|
||||
[self didChangeState];
|
||||
|
||||
[_errorDelegate authState:self didEncounterAuthorizationError:oauthError];
|
||||
}
|
||||
|
||||
#pragma mark - OAuth Requests
|
||||
|
||||
- (OIDTokenRequest *)tokenRefreshRequest {
|
||||
return [self tokenRefreshRequestWithAdditionalParameters:nil];
|
||||
}
|
||||
|
||||
- (OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters:
|
||||
(NSDictionary<NSString *, NSString *> *)additionalParameters {
|
||||
|
||||
// TODO: Add unit test to confirm exception is thrown when expected
|
||||
|
||||
if (!_refreshToken) {
|
||||
[OIDErrorUtilities raiseException:kRefreshTokenRequestException];
|
||||
}
|
||||
return [[OIDTokenRequest alloc]
|
||||
initWithConfiguration:_lastAuthorizationResponse.request.configuration
|
||||
grantType:OIDGrantTypeRefreshToken
|
||||
authorizationCode:nil
|
||||
redirectURL:nil
|
||||
clientID:_lastAuthorizationResponse.request.clientID
|
||||
clientSecret:_lastAuthorizationResponse.request.clientSecret
|
||||
scope:nil
|
||||
refreshToken:_refreshToken
|
||||
codeVerifier:nil
|
||||
additionalParameters:additionalParameters];
|
||||
}
|
||||
|
||||
#pragma mark - Stateful Actions
|
||||
|
||||
- (void)didChangeState {
|
||||
[_stateChangeDelegate didChangeState:self];
|
||||
}
|
||||
|
||||
- (void)setNeedsTokenRefresh {
|
||||
_needsTokenRefresh = YES;
|
||||
}
|
||||
|
||||
- (void)performActionWithFreshTokens:(OIDAuthStateAction)action {
|
||||
[self performActionWithFreshTokens:action additionalRefreshParameters:nil];
|
||||
}
|
||||
|
||||
- (void)performActionWithFreshTokens:(OIDAuthStateAction)action
|
||||
additionalRefreshParameters:
|
||||
(nullable NSDictionary<NSString *, NSString *> *)additionalParameters {
|
||||
[self performActionWithFreshTokens:action
|
||||
additionalRefreshParameters:additionalParameters
|
||||
dispatchQueue:dispatch_get_main_queue()];
|
||||
}
|
||||
|
||||
- (void)performActionWithFreshTokens:(OIDAuthStateAction)action
|
||||
additionalRefreshParameters:
|
||||
(nullable NSDictionary<NSString *, NSString *> *)additionalParameters
|
||||
dispatchQueue:(dispatch_queue_t)dispatchQueue {
|
||||
|
||||
if ([self isTokenFresh]) {
|
||||
// access token is valid within tolerance levels, perform action
|
||||
dispatch_async(dispatchQueue, ^{
|
||||
action(self.accessToken, self.idToken, nil);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_refreshToken) {
|
||||
// no refresh token available and token has expired
|
||||
NSError *tokenRefreshError = [
|
||||
OIDErrorUtilities errorWithCode:OIDErrorCodeTokenRefreshError
|
||||
underlyingError:nil
|
||||
description:@"Unable to refresh expired token without a refresh token."];
|
||||
dispatch_async(dispatchQueue, ^{
|
||||
action(nil, nil, tokenRefreshError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// access token is expired, first refresh the token, then perform action
|
||||
NSAssert(_pendingActionsSyncObject, @"_pendingActionsSyncObject cannot be nil", @"");
|
||||
OIDAuthStatePendingAction* pendingAction =
|
||||
[[OIDAuthStatePendingAction alloc] initWithAction:action andDispatchQueue:dispatchQueue];
|
||||
@synchronized(_pendingActionsSyncObject) {
|
||||
// if a token is already in the process of being refreshed, adds to pending actions
|
||||
if (_pendingActions) {
|
||||
[_pendingActions addObject:pendingAction];
|
||||
return;
|
||||
}
|
||||
|
||||
// creates a list of pending actions, starting with this one
|
||||
_pendingActions = [NSMutableArray arrayWithObject:pendingAction];
|
||||
}
|
||||
|
||||
// refresh the tokens
|
||||
OIDTokenRequest *tokenRefreshRequest =
|
||||
[self tokenRefreshRequestWithAdditionalParameters:additionalParameters];
|
||||
[OIDAuthorizationService performTokenRequest:tokenRefreshRequest
|
||||
originalAuthorizationResponse:_lastAuthorizationResponse
|
||||
callback:^(OIDTokenResponse *_Nullable response,
|
||||
NSError *_Nullable error) {
|
||||
// update OIDAuthState based on response
|
||||
if (response) {
|
||||
self->_needsTokenRefresh = NO;
|
||||
[self updateWithTokenResponse:response error:nil];
|
||||
} else {
|
||||
if (error.domain == OIDOAuthTokenErrorDomain) {
|
||||
self->_needsTokenRefresh = NO;
|
||||
[self updateWithAuthorizationError:error];
|
||||
} else {
|
||||
if ([self->_errorDelegate respondsToSelector:
|
||||
@selector(authState:didEncounterTransientError:)]) {
|
||||
[self->_errorDelegate authState:self didEncounterTransientError:error];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nil the pending queue and process everything that was queued up
|
||||
NSArray *actionsToProcess;
|
||||
@synchronized(self->_pendingActionsSyncObject) {
|
||||
actionsToProcess = self->_pendingActions;
|
||||
self->_pendingActions = nil;
|
||||
}
|
||||
for (OIDAuthStatePendingAction* actionToProcess in actionsToProcess) {
|
||||
dispatch_async(actionToProcess.dispatchQueue, ^{
|
||||
actionToProcess.action(self.accessToken, self.idToken, error);
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
/*! @fn isTokenFresh
|
||||
@brief Determines whether a token refresh request must be made to refresh the tokens.
|
||||
*/
|
||||
- (BOOL)isTokenFresh {
|
||||
if (_needsTokenRefresh) {
|
||||
// forced refresh
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (!self.accessTokenExpirationDate) {
|
||||
// if there is no expiration time but we have an access token, it is assumed to never expire
|
||||
return !!self.accessToken;
|
||||
}
|
||||
|
||||
// has the token expired?
|
||||
BOOL tokenFresh = [self.accessTokenExpirationDate timeIntervalSinceNow] > kExpiryTimeTolerance;
|
||||
return tokenFresh;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*! @file OIDAuthStateChangeDelegate.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 OIDAuthState;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @protocol OIDAuthStateChangeDelegate
|
||||
@brief Delegate of the OIDAuthState used to monitor various changes in state.
|
||||
*/
|
||||
@protocol OIDAuthStateChangeDelegate <NSObject>
|
||||
|
||||
/*! @brief Called when the authorization state changes and any backing storage needs to be updated.
|
||||
@param state The @c OIDAuthState that changed.
|
||||
@discussion If you are storing the authorization state, you should update the storage when the
|
||||
state changes.
|
||||
*/
|
||||
- (void)didChangeState:(OIDAuthState *)state;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,62 @@
|
||||
/*! @file OIDAuthStateErrorDelegate.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 OIDAuthState;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @protocol OIDAuthStateErrorDelegate
|
||||
@brief Delegate of the OIDAuthState used to monitor errors.
|
||||
*/
|
||||
@protocol OIDAuthStateErrorDelegate <NSObject>
|
||||
|
||||
/*! @brief Called when an authentication occurs, which indicates the auth session is invalid.
|
||||
@param state The @c OIDAuthState on which the error occurred.
|
||||
@param error The authorization error.
|
||||
@discussion This is a hard error (not a transient network issue) that indicates a problem with
|
||||
the authorization. You should stop using the @c OIDAuthState when such an error is
|
||||
encountered. If the \NSError_code is @c ::OIDErrorCodeOAuthInvalidGrant then
|
||||
the session may be recoverable with user interaction (i.e. re-authentication). In all cases
|
||||
you should consider the user unauthorized, and remove locally cached resources that require
|
||||
that authorization. @c OIDAuthState will call this method automatically if it encounters
|
||||
an OAuth error (that is, an HTTP 400 response with a valid OAuth error response) during
|
||||
authorization or token refresh (such as performed automatically when using
|
||||
@c OIDAuthState.performActionWithFreshTokens:). You can signal authorization errors with
|
||||
@c OIDAuthState.updateWithAuthorizationError:.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
- (void)authState:(OIDAuthState *)state didEncounterAuthorizationError:(NSError *)error;
|
||||
|
||||
@optional
|
||||
|
||||
/*! @brief Called when a network or other transient error occurs.
|
||||
@param state The @c OIDAuthState on which the error occurred.
|
||||
@param error The transient error.
|
||||
@discussion This is a soft error, typically network related. The @c OIDAuthState is likely
|
||||
still valid, and should not be discarded. Retry the request using an incremental backoff
|
||||
strategy. This is only called when using the @c OIDAuthState convenience methods such as
|
||||
@c OIDAuthState.performActionWithFreshTokens:. If you are refreshing the tokens yourself
|
||||
outside of @c OIDAuthState class, it will never be called.
|
||||
*/
|
||||
- (void)authState:(OIDAuthState *)state didEncounterTransientError:(NSError *)error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,250 @@
|
||||
/*! @file OIDAuthorizationRequest.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
// These files only declare string constants useful for constructing a @c OIDAuthorizationRequest,
|
||||
// so they are imported here for convenience.
|
||||
#import "OIDExternalUserAgentRequest.h"
|
||||
#import "OIDResponseTypes.h"
|
||||
#import "OIDScopes.h"
|
||||
|
||||
@class OIDServiceConfiguration;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief The @c code_challenge_method value for the S256 code challenge.
|
||||
@see https://tools.ietf.org/html/rfc7636#section-4.3
|
||||
*/
|
||||
extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256;
|
||||
|
||||
|
||||
/*! @brief Represents an authorization request.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.1
|
||||
*/
|
||||
@interface OIDAuthorizationRequest :
|
||||
NSObject<NSCopying, NSSecureCoding, OIDExternalUserAgentRequest>
|
||||
|
||||
/*! @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 The expected response type.
|
||||
@remarks response_type
|
||||
@discussion Generally 'code' if pure OAuth, otherwise a space-delimited list of of response
|
||||
types including 'code', 'token', and 'id_token' for OpenID Connect.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.1.1
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *responseType;
|
||||
|
||||
/*! @brief The client identifier.
|
||||
@remarks client_id
|
||||
@see https://tools.ietf.org/html/rfc6749#section-2.2
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *clientID;
|
||||
|
||||
/*! @brief The client secret.
|
||||
@remarks client_secret
|
||||
@discussion The client secret is used to prove that identity of the client when exchaning an
|
||||
authorization code for an access token.
|
||||
The client secret is not passed in the authorizationRequestURL. It is only used when
|
||||
exchanging the authorization code for an access token.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-2.3.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *clientSecret;
|
||||
|
||||
/*! @brief The value of the scope parameter is expressed as a list of space-delimited,
|
||||
case-sensitive strings.
|
||||
@remarks scope
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.3
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *scope;
|
||||
|
||||
/*! @brief The client's redirect URI.
|
||||
@remarks redirect_uri
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.1.2
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *redirectURL;
|
||||
|
||||
/*! @brief An opaque value used by the client to maintain state between the request and callback.
|
||||
@remarks state
|
||||
@discussion If this value is not explicitly set, this library will automatically add state and
|
||||
perform appropriate validation of the state in the authorization response. It is recommended
|
||||
that the default implementation of this parameter be used wherever possible. Typically used
|
||||
to prevent CSRF attacks, as recommended in RFC6819 Section 5.3.5.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.1
|
||||
@see https://tools.ietf.org/html/rfc6819#section-5.3.5
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *state;
|
||||
|
||||
/*! @brief String value used to associate a Client session with an ID Token, and to mitigate replay
|
||||
attacks. The value is passed through unmodified from the Authentication Request to the ID
|
||||
Token. Sufficient entropy MUST be present in the nonce values used to prevent attackers from
|
||||
guessing values.
|
||||
@remarks nonce
|
||||
@discussion If this value is not explicitly set, this library will automatically add nonce and
|
||||
perform appropriate validation of the nonce in the ID Token.
|
||||
@see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *nonce;
|
||||
|
||||
/*! @brief The PKCE code verifier.
|
||||
@remarks code_verifier
|
||||
@discussion The code verifier itself is not included in the authorization request that is sent
|
||||
on the wire, but needs to be in the token exchange request.
|
||||
@c OIDAuthorizationResponse.tokenExchangeRequest will create a @c OIDTokenRequest that
|
||||
includes this parameter automatically.
|
||||
@see https://tools.ietf.org/html/rfc7636#section-4.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *codeVerifier;
|
||||
|
||||
/*! @brief The PKCE code challenge, derived from #codeVerifier.
|
||||
@remarks code_challenge
|
||||
@see https://tools.ietf.org/html/rfc7636#section-4.2
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *codeChallenge;
|
||||
|
||||
/*! @brief The method used to compute the @c #codeChallenge
|
||||
@remarks code_challenge_method
|
||||
@see https://tools.ietf.org/html/rfc7636#section-4.3
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *codeChallengeMethod;
|
||||
|
||||
/*! @brief The client's additional authorization parameters.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDictionary<NSString *, NSString *> *additionalParameters;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use
|
||||
@c initWithConfiguration:clientId:scopes:redirectURL:responseType:additionalParameters:.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Creates an authorization request with opinionated defaults (a secure @c state, and
|
||||
PKCE with S256 as the @c code_challenge_method).
|
||||
@param configuration The service's configuration.
|
||||
@param clientID The client identifier.
|
||||
@param scopes An array of scopes to combine into a single scope string per the OAuth2 spec.
|
||||
@param redirectURL The client's redirect URI.
|
||||
@param responseType The expected response type.
|
||||
@param additionalParameters The client's additional authorization parameters.
|
||||
@remarks This convenience initializer generates a state parameter and PKCE challenges
|
||||
automatically.
|
||||
*/
|
||||
- (instancetype)
|
||||
initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
clientId:(NSString *)clientID
|
||||
scopes:(nullable NSArray<NSString *> *)scopes
|
||||
redirectURL:(NSURL *)redirectURL
|
||||
responseType:(NSString *)responseType
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters;
|
||||
|
||||
/*! @brief Creates an authorization request with opinionated defaults (a secure @c state, @c nonce,
|
||||
and PKCE with S256 as the @c code_challenge_method).
|
||||
@param configuration The service's configuration.
|
||||
@param clientID The client identifier.
|
||||
@param clientSecret The client secret.
|
||||
@param scopes An array of scopes to combine into a single scope string per the OAuth2 spec.
|
||||
@param redirectURL The client's redirect URI.
|
||||
@param responseType The expected response type.
|
||||
@param additionalParameters The client's additional authorization parameters.
|
||||
@remarks This convenience initializer generates a state parameter and PKCE challenges
|
||||
automatically.
|
||||
*/
|
||||
- (instancetype)
|
||||
initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
clientId:(NSString *)clientID
|
||||
clientSecret:(nullable NSString *)clientSecret
|
||||
scopes:(nullable NSArray<NSString *> *)scopes
|
||||
redirectURL:(NSURL *)redirectURL
|
||||
responseType:(NSString *)responseType
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters;
|
||||
|
||||
/*! @brief Designated initializer.
|
||||
@param configuration The service's configuration.
|
||||
@param clientID The client identifier.
|
||||
@param scope A scope string per the OAuth2 spec (a space-delimited set of scopes).
|
||||
@param redirectURL The client's redirect URI.
|
||||
@param responseType The expected response type.
|
||||
@param state An opaque value used by the client to maintain state between the request and
|
||||
callback.
|
||||
@param nonce String value used to associate a Client session with an ID Token. Can be set to nil
|
||||
if not using OpenID Connect, although pure OAuth servers should ignore params they don't
|
||||
understand anyway.
|
||||
@param codeVerifier The PKCE code verifier. See @c OIDAuthorizationRequest.generateCodeVerifier.
|
||||
@param codeChallenge The PKCE code challenge, calculated from the code verifier such as with
|
||||
@c OIDAuthorizationRequest.codeChallengeS256ForVerifier:.
|
||||
@param codeChallengeMethod The PKCE code challenge method.
|
||||
::OIDOAuthorizationRequestCodeChallengeMethodS256 when
|
||||
@c OIDAuthorizationRequest.codeChallengeS256ForVerifier: is used to create the code
|
||||
challenge.
|
||||
@param additionalParameters The client's additional authorization parameters.
|
||||
*/
|
||||
- (instancetype)
|
||||
initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
clientId:(NSString *)clientID
|
||||
clientSecret:(nullable NSString *)clientSecret
|
||||
scope:(nullable NSString *)scope
|
||||
redirectURL:(nullable NSURL *)redirectURL
|
||||
responseType:(NSString *)responseType
|
||||
state:(nullable NSString *)state
|
||||
nonce:(nullable NSString *)nonce
|
||||
codeVerifier:(nullable NSString *)codeVerifier
|
||||
codeChallenge:(nullable NSString *)codeChallenge
|
||||
codeChallengeMethod:(nullable NSString *)codeChallengeMethod
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/*! @brief Constructs the request URI by adding the request parameters to the query component of the
|
||||
authorization endpoint URI using the "application/x-www-form-urlencoded" format.
|
||||
@return A URL representing the authorization request.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.1
|
||||
*/
|
||||
- (NSURL *)authorizationRequestURL;
|
||||
|
||||
/*! @brief Generates an OAuth state param using a random source.
|
||||
@return The generated state.
|
||||
@see https://tools.ietf.org/html/rfc6819#section-5.3.5
|
||||
*/
|
||||
+ (nullable NSString *)generateState;
|
||||
|
||||
/*! @brief Constructs a PKCE-compliant code verifier.
|
||||
@return The generated code verifier.
|
||||
@see https://tools.ietf.org/html/rfc7636#section-4.1
|
||||
*/
|
||||
+ (nullable NSString *)generateCodeVerifier;
|
||||
|
||||
/*! @brief Creates a PKCE S256 codeChallenge from the codeVerifier.
|
||||
@param codeVerifier The code verifier from which the code challenge will be derived.
|
||||
@return The generated code challenge.
|
||||
@details Generate a secure code verifier to pass into this method with
|
||||
@c OIDAuthorizationRequest.generateCodeVerifier. The matching @c #codeChallengeMethod for
|
||||
@c #codeChallenge%s created by this method is
|
||||
::OIDOAuthorizationRequestCodeChallengeMethodS256.
|
||||
@see https://tools.ietf.org/html/rfc7636#section-4.1
|
||||
*/
|
||||
+ (nullable NSString *)codeChallengeS256ForVerifier:(nullable NSString *)codeVerifier;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,351 @@
|
||||
/*! @file OIDAuthorizationRequest.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDAuthorizationRequest.h"
|
||||
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDScopeUtilities.h"
|
||||
#import "OIDServiceConfiguration.h"
|
||||
#import "OIDTokenUtilities.h"
|
||||
#import "OIDURLQueryComponent.h"
|
||||
|
||||
/*! @brief The key for the @c configuration property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kConfigurationKey = @"configuration";
|
||||
|
||||
/*! @brief Key used to encode the @c responseType property for @c NSSecureCoding, and on the URL
|
||||
request.
|
||||
*/
|
||||
static NSString *const kResponseTypeKey = @"response_type";
|
||||
|
||||
/*! @brief Key used to encode the @c clientID property for @c NSSecureCoding, and on the URL
|
||||
request.
|
||||
*/
|
||||
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 Key used to encode the @c scope property for @c NSSecureCoding, and on the URL request.
|
||||
*/
|
||||
static NSString *const kScopeKey = @"scope";
|
||||
|
||||
/*! @brief Key used to encode the @c redirectURL property for @c NSSecureCoding, and on the URL
|
||||
request.
|
||||
*/
|
||||
static NSString *const kRedirectURLKey = @"redirect_uri";
|
||||
|
||||
/*! @brief Key used to encode the @c state property for @c NSSecureCoding, and on the URL request.
|
||||
*/
|
||||
static NSString *const kStateKey = @"state";
|
||||
|
||||
/*! @brief Key used to encode the @c nonce property for @c NSSecureCoding, and on the URL request.
|
||||
*/
|
||||
static NSString *const kNonceKey = @"nonce";
|
||||
|
||||
/*! @brief Key used to encode the @c codeVerifier property for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kCodeVerifierKey = @"code_verifier";
|
||||
|
||||
/*! @brief Key used to send the @c codeChallenge on the URL request.
|
||||
*/
|
||||
static NSString *const kCodeChallengeKey = @"code_challenge";
|
||||
|
||||
/*! @brief Key used to send the @c codeChallengeMethod on the URL request.
|
||||
*/
|
||||
static NSString *const kCodeChallengeMethodKey = @"code_challenge_method";
|
||||
|
||||
/*! @brief Key used to encode the @c additionalParameters property for
|
||||
@c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kAdditionalParametersKey = @"additionalParameters";
|
||||
|
||||
/*! @brief Number of random bytes generated for the @ state.
|
||||
*/
|
||||
static NSUInteger const kStateSizeBytes = 32;
|
||||
|
||||
/*! @brief Number of random bytes generated for the @ codeVerifier.
|
||||
*/
|
||||
static NSUInteger const kCodeVerifierBytes = 32;
|
||||
|
||||
/*! @brief Assertion text for unsupported response types.
|
||||
*/
|
||||
static NSString *const OIDOAuthUnsupportedResponseTypeMessage =
|
||||
@"The response_type \"%@\" isn't supported. AppAuth only supports the \"code\" or \"code id_token\" response_type.";
|
||||
|
||||
/*! @brief Code challenge request method.
|
||||
*/
|
||||
NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256 = @"S256";
|
||||
|
||||
@implementation OIDAuthorizationRequest
|
||||
|
||||
- (instancetype)init
|
||||
OID_UNAVAILABLE_USE_INITIALIZER(
|
||||
@selector(initWithConfiguration:
|
||||
clientId:
|
||||
scopes:
|
||||
redirectURL:
|
||||
responseType:
|
||||
additionalParameters:)
|
||||
)
|
||||
|
||||
/*! @brief Check if the response type is one AppAuth supports
|
||||
@remarks AppAuth only supports the `code` and `code id_token` response types.
|
||||
@see https://github.com/openid/AppAuth-iOS/issues/98
|
||||
@see https://github.com/openid/AppAuth-iOS/issues/292
|
||||
*/
|
||||
+ (BOOL)isSupportedResponseType:(NSString *)responseType
|
||||
{
|
||||
NSString *codeIdToken = [@[OIDResponseTypeCode, OIDResponseTypeIDToken]
|
||||
componentsJoinedByString:@" "];
|
||||
NSString *idTokenCode = [@[OIDResponseTypeIDToken, OIDResponseTypeCode]
|
||||
componentsJoinedByString:@" "];
|
||||
|
||||
return [responseType isEqualToString:OIDResponseTypeCode]
|
||||
|| [responseType isEqualToString:codeIdToken]
|
||||
|| [responseType isEqualToString:idTokenCode];
|
||||
}
|
||||
|
||||
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
clientId:(NSString *)clientID
|
||||
clientSecret:(nullable NSString *)clientSecret
|
||||
scope:(nullable NSString *)scope
|
||||
redirectURL:(NSURL *)redirectURL
|
||||
responseType:(NSString *)responseType
|
||||
state:(nullable NSString *)state
|
||||
nonce:(nullable NSString *)nonce
|
||||
codeVerifier:(nullable NSString *)codeVerifier
|
||||
codeChallenge:(nullable NSString *)codeChallenge
|
||||
codeChallengeMethod:(nullable NSString *)codeChallengeMethod
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_configuration = [configuration copy];
|
||||
_clientID = [clientID copy];
|
||||
_clientSecret = [clientSecret copy];
|
||||
_scope = [scope copy];
|
||||
_redirectURL = [redirectURL copy];
|
||||
_responseType = [responseType copy];
|
||||
if (![[self class] isSupportedResponseType:_responseType]) {
|
||||
NSAssert(NO, OIDOAuthUnsupportedResponseTypeMessage, _responseType);
|
||||
return nil;
|
||||
}
|
||||
_state = [state copy];
|
||||
_nonce = [nonce copy];
|
||||
_codeVerifier = [codeVerifier copy];
|
||||
_codeChallenge = [codeChallenge copy];
|
||||
_codeChallengeMethod = [codeChallengeMethod copy];
|
||||
|
||||
_additionalParameters =
|
||||
[[NSDictionary alloc] initWithDictionary:additionalParameters copyItems:YES];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)
|
||||
initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
clientId:(NSString *)clientID
|
||||
clientSecret:(NSString *)clientSecret
|
||||
scopes:(nullable NSArray<NSString *> *)scopes
|
||||
redirectURL:(NSURL *)redirectURL
|
||||
responseType:(NSString *)responseType
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters {
|
||||
|
||||
// generates PKCE code verifier and challenge
|
||||
NSString *codeVerifier = [[self class] generateCodeVerifier];
|
||||
NSString *codeChallenge = [[self class] codeChallengeS256ForVerifier:codeVerifier];
|
||||
|
||||
return [self initWithConfiguration:configuration
|
||||
clientId:clientID
|
||||
clientSecret:clientSecret
|
||||
scope:[OIDScopeUtilities scopesWithArray:scopes]
|
||||
redirectURL:redirectURL
|
||||
responseType:responseType
|
||||
state:[[self class] generateState]
|
||||
nonce:[[self class] generateState]
|
||||
codeVerifier:codeVerifier
|
||||
codeChallenge:codeChallenge
|
||||
codeChallengeMethod:OIDOAuthorizationRequestCodeChallengeMethodS256
|
||||
additionalParameters:additionalParameters];
|
||||
}
|
||||
|
||||
- (instancetype)
|
||||
initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
clientId:(NSString *)clientID
|
||||
scopes:(nullable NSArray<NSString *> *)scopes
|
||||
redirectURL:(NSURL *)redirectURL
|
||||
responseType:(NSString *)responseType
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters {
|
||||
return [self initWithConfiguration:configuration
|
||||
clientId:clientID
|
||||
clientSecret:nil
|
||||
scopes:scopes
|
||||
redirectURL:redirectURL
|
||||
responseType:responseType
|
||||
additionalParameters:additionalParameters];
|
||||
}
|
||||
|
||||
#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 *responseType = [aDecoder decodeObjectOfClass:[NSString class] forKey:kResponseTypeKey];
|
||||
NSString *clientID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kClientIDKey];
|
||||
NSString *clientSecret = [aDecoder decodeObjectOfClass:[NSString class] forKey:kClientSecretKey];
|
||||
NSString *scope = [aDecoder decodeObjectOfClass:[NSString class] forKey:kScopeKey];
|
||||
NSURL *redirectURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kRedirectURLKey];
|
||||
NSString *state = [aDecoder decodeObjectOfClass:[NSString class] forKey:kStateKey];
|
||||
NSString *nonce = [aDecoder decodeObjectOfClass:[NSString class] forKey:kNonceKey];
|
||||
NSString *codeVerifier = [aDecoder decodeObjectOfClass:[NSString class] forKey:kCodeVerifierKey];
|
||||
NSString *codeChallenge =
|
||||
[aDecoder decodeObjectOfClass:[NSString class] forKey:kCodeChallengeKey];
|
||||
NSString *codeChallengeMethod =
|
||||
[aDecoder decodeObjectOfClass:[NSString class] forKey:kCodeChallengeMethodKey];
|
||||
NSSet *additionalParameterCodingClasses = [NSSet setWithArray:@[
|
||||
[NSDictionary class],
|
||||
[NSString class]
|
||||
]];
|
||||
NSDictionary *additionalParameters =
|
||||
[aDecoder decodeObjectOfClasses:additionalParameterCodingClasses
|
||||
forKey:kAdditionalParametersKey];
|
||||
|
||||
self = [self initWithConfiguration:configuration
|
||||
clientId:clientID
|
||||
clientSecret:clientSecret
|
||||
scope:scope
|
||||
redirectURL:redirectURL
|
||||
responseType:responseType
|
||||
state:state
|
||||
nonce:nonce
|
||||
codeVerifier:codeVerifier
|
||||
codeChallenge:codeChallenge
|
||||
codeChallengeMethod:codeChallengeMethod
|
||||
additionalParameters:additionalParameters];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:_configuration forKey:kConfigurationKey];
|
||||
[aCoder encodeObject:_responseType forKey:kResponseTypeKey];
|
||||
[aCoder encodeObject:_clientID forKey:kClientIDKey];
|
||||
[aCoder encodeObject:_clientSecret forKey:kClientSecretKey];
|
||||
[aCoder encodeObject:_scope forKey:kScopeKey];
|
||||
[aCoder encodeObject:_redirectURL forKey:kRedirectURLKey];
|
||||
[aCoder encodeObject:_state forKey:kStateKey];
|
||||
[aCoder encodeObject:_nonce forKey:kNonceKey];
|
||||
[aCoder encodeObject:_codeVerifier forKey:kCodeVerifierKey];
|
||||
[aCoder encodeObject:_codeChallenge forKey:kCodeChallengeKey];
|
||||
[aCoder encodeObject:_codeChallengeMethod forKey:kCodeChallengeMethodKey];
|
||||
[aCoder encodeObject:_additionalParameters forKey:kAdditionalParametersKey];
|
||||
}
|
||||
|
||||
#pragma mark - NSObject overrides
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"<%@: %p, request: %@>",
|
||||
NSStringFromClass([self class]),
|
||||
(void *)self,
|
||||
self.authorizationRequestURL];
|
||||
}
|
||||
|
||||
#pragma mark - State and PKCE verifier/challenge generation Methods
|
||||
|
||||
+ (nullable NSString *)generateCodeVerifier {
|
||||
return [OIDTokenUtilities randomURLSafeStringWithSize:kCodeVerifierBytes];
|
||||
}
|
||||
|
||||
+ (nullable NSString *)generateState {
|
||||
return [OIDTokenUtilities randomURLSafeStringWithSize:kStateSizeBytes];
|
||||
}
|
||||
|
||||
+ (nullable NSString *)codeChallengeS256ForVerifier:(NSString *)codeVerifier {
|
||||
if (!codeVerifier) {
|
||||
return nil;
|
||||
}
|
||||
// generates the code_challenge per spec https://tools.ietf.org/html/rfc7636#section-4.2
|
||||
// code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
|
||||
// NB. the ASCII conversion on the code_verifier entropy was done at time of generation.
|
||||
NSData *sha256Verifier = [OIDTokenUtilities sha256:codeVerifier];
|
||||
return [OIDTokenUtilities encodeBase64urlNoPadding:sha256Verifier];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (NSURL *)authorizationRequestURL {
|
||||
OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init];
|
||||
|
||||
// Required parameters.
|
||||
[query addParameter:kResponseTypeKey value:_responseType];
|
||||
[query addParameter:kClientIDKey value:_clientID];
|
||||
|
||||
// Add any additional parameters the client has specified.
|
||||
[query addParameters:_additionalParameters];
|
||||
|
||||
// Add optional parameters, as applicable.
|
||||
if (_redirectURL) {
|
||||
[query addParameter:kRedirectURLKey value:_redirectURL.absoluteString];
|
||||
}
|
||||
if (_scope) {
|
||||
[query addParameter:kScopeKey value:_scope];
|
||||
}
|
||||
if (_state) {
|
||||
[query addParameter:kStateKey value:_state];
|
||||
}
|
||||
if (_nonce) {
|
||||
[query addParameter:kNonceKey value:_nonce];
|
||||
}
|
||||
if (_codeChallenge) {
|
||||
[query addParameter:kCodeChallengeKey value:_codeChallenge];
|
||||
}
|
||||
if (_codeChallengeMethod) {
|
||||
[query addParameter:kCodeChallengeMethodKey value:_codeChallengeMethod];
|
||||
}
|
||||
|
||||
// Construct the URL:
|
||||
return [query URLByReplacingQueryInURL:_configuration.authorizationEndpoint];
|
||||
}
|
||||
|
||||
#pragma mark - OIDExternalUserAgentRequest
|
||||
|
||||
- (NSURL *)externalUserAgentRequestURL {
|
||||
return [self authorizationRequestURL];
|
||||
}
|
||||
|
||||
- (NSString *)redirectScheme {
|
||||
return [[self redirectURL] scheme];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,128 @@
|
||||
/*! @file OIDAuthorizationResponse.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 OIDAuthorizationRequest;
|
||||
@class OIDTokenRequest;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Represents the response to an authorization request.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthResponse
|
||||
*/
|
||||
@interface OIDAuthorizationResponse : NSObject <NSCopying, NSSecureCoding>
|
||||
|
||||
/*! @brief The request which was serviced.
|
||||
*/
|
||||
@property(nonatomic, readonly) OIDAuthorizationRequest *request;
|
||||
|
||||
/*! @brief The authorization code generated by the authorization server.
|
||||
@discussion Set when the response_type requested includes 'code'.
|
||||
@remarks code
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *authorizationCode;
|
||||
|
||||
/*! @brief REQUIRED if the "state" parameter was present in the client authorization request. The
|
||||
exact value received from the client.
|
||||
@remarks state
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *state;
|
||||
|
||||
/*! @brief The access token generated by the authorization server.
|
||||
@discussion Set when the response_type requested includes 'token'.
|
||||
@remarks access_token
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthResponse
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *accessToken;
|
||||
|
||||
/*! @brief The approximate expiration date & time of the access token.
|
||||
@discussion Set when the response_type requested includes 'token'.
|
||||
@remarks expires_in
|
||||
@seealso OIDAuthorizationResponse.accessToken
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthResponse
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDate *accessTokenExpirationDate;
|
||||
|
||||
/*! @brief Typically "Bearer" when present. Otherwise, another token_type value that the Client has
|
||||
negotiated with the Authorization Server.
|
||||
@discussion Set when the response_type requested includes 'token'.
|
||||
@remarks token_type
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthResponse
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *tokenType;
|
||||
|
||||
/*! @brief ID Token value associated with the authenticated session.
|
||||
@discussion Set when the response_type requested includes 'id_token'.
|
||||
@remarks id_token
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthResponse
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *idToken;
|
||||
|
||||
/*! @brief The scope of the access token. OPTIONAL, if identical to the scopes requested, otherwise,
|
||||
REQUIRED.
|
||||
@remarks scope
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *scope;
|
||||
|
||||
/*! @brief Additional parameters returned from the authorization server.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSDictionary<NSString *, NSObject<NSCopying> *> *additionalParameters;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use initWithRequest:parameters:.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Designated initializer.
|
||||
@param request The serviced request.
|
||||
@param parameters The decoded parameters returned from the Authorization Server.
|
||||
@remarks Known parameters are extracted from the @c parameters parameter and the normative
|
||||
properties are populated. Non-normative parameters are placed in the
|
||||
@c #additionalParameters dictionary.
|
||||
*/
|
||||
- (instancetype)initWithRequest:(OIDAuthorizationRequest *)request
|
||||
parameters:(NSDictionary<NSString *, NSObject<NSCopying> *> *)parameters
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/*! @brief Creates a token request suitable for exchanging an authorization code for an access
|
||||
token.
|
||||
@return A @c OIDTokenRequest suitable for exchanging an authorization code for an access
|
||||
token.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||
*/
|
||||
- (nullable OIDTokenRequest *)tokenExchangeRequest;
|
||||
|
||||
/*! @brief Creates a token request suitable for exchanging an authorization code for an access
|
||||
token.
|
||||
@param additionalParameters Additional parameters for the token request.
|
||||
@return A @c OIDTokenRequest suitable for exchanging an authorization code for an access
|
||||
token.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||
*/
|
||||
- (nullable OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters:
|
||||
(nullable NSDictionary<NSString *, NSString *> *)additionalParameters;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,210 @@
|
||||
/*! @file OIDAuthorizationResponse.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDAuthorizationResponse.h"
|
||||
|
||||
#import "OIDAuthorizationRequest.h"
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDError.h"
|
||||
#import "OIDFieldMapping.h"
|
||||
#import "OIDTokenRequest.h"
|
||||
#import "OIDTokenUtilities.h"
|
||||
|
||||
/*! @brief The key for the @c authorizationCode property in the incoming parameters and for
|
||||
@c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kAuthorizationCodeKey = @"code";
|
||||
|
||||
/*! @brief The key for the @c state property in the incoming parameters and for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kStateKey = @"state";
|
||||
|
||||
/*! @brief The key for the @c accessToken property in the incoming parameters and for
|
||||
@c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kAccessTokenKey = @"access_token";
|
||||
|
||||
/*! @brief The key for the @c accessTokenExpirationDate property in the incoming parameters and for
|
||||
@c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kExpiresInKey = @"expires_in";
|
||||
|
||||
/*! @brief The key for the @c tokenType property in the incoming parameters and for
|
||||
@c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kTokenTypeKey = @"token_type";
|
||||
|
||||
/*! @brief The key for the @c idToken property in the incoming parameters and for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kIDTokenKey = @"id_token";
|
||||
|
||||
/*! @brief The key for the @c scope property in the incoming parameters and for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kScopeKey = @"scope";
|
||||
|
||||
/*! @brief Key used to encode the @c additionalParameters property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kAdditionalParametersKey = @"additionalParameters";
|
||||
|
||||
/*! @brief Key used to encode the @c request property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kRequestKey = @"request";
|
||||
|
||||
/*! @brief The exception thrown when a developer tries to create a token exchange request from an
|
||||
authorization request with no authorization code.
|
||||
*/
|
||||
static NSString *const kTokenExchangeRequestException =
|
||||
@"Attempted to create a token exchange request from an authorization response with no "
|
||||
"authorization code.";
|
||||
|
||||
@implementation OIDAuthorizationResponse
|
||||
|
||||
/*! @brief Returns a mapping of incoming parameters to instance variables.
|
||||
@return A mapping of incoming parameters to instance variables.
|
||||
*/
|
||||
+ (NSDictionary<NSString *, OIDFieldMapping *> *)fieldMap {
|
||||
static NSMutableDictionary<NSString *, OIDFieldMapping *> *fieldMap;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
fieldMap = [NSMutableDictionary dictionary];
|
||||
fieldMap[kStateKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_state" type:[NSString class]];
|
||||
fieldMap[kAuthorizationCodeKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_authorizationCode" type:[NSString class]];
|
||||
fieldMap[kAccessTokenKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_accessToken" type:[NSString class]];
|
||||
fieldMap[kExpiresInKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_accessTokenExpirationDate"
|
||||
type:[NSDate class]
|
||||
conversion:^id _Nullable(NSObject *_Nullable value) {
|
||||
if (![value isKindOfClass:[NSNumber class]]) {
|
||||
return value;
|
||||
}
|
||||
NSNumber *valueAsNumber = (NSNumber *)value;
|
||||
return [NSDate dateWithTimeIntervalSinceNow:[valueAsNumber longLongValue]];
|
||||
}];
|
||||
fieldMap[kTokenTypeKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_tokenType" type:[NSString class]];
|
||||
fieldMap[kIDTokenKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_idToken" type:[NSString class]];
|
||||
fieldMap[kScopeKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_scope" type:[NSString class]];
|
||||
});
|
||||
return fieldMap;
|
||||
}
|
||||
|
||||
#pragma mark - Initializers
|
||||
|
||||
- (instancetype)init
|
||||
OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:))
|
||||
|
||||
- (instancetype)initWithRequest:(OIDAuthorizationRequest *)request
|
||||
parameters:(NSDictionary<NSString *, NSObject<NSCopying> *> *)parameters {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_request = [request copy];
|
||||
NSDictionary<NSString *, NSObject<NSCopying> *> *additionalParameters =
|
||||
[OIDFieldMapping remainingParametersWithMap:[[self class] fieldMap]
|
||||
parameters:parameters
|
||||
instance:self];
|
||||
_additionalParameters = additionalParameters;
|
||||
}
|
||||
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 {
|
||||
OIDAuthorizationRequest *request =
|
||||
[aDecoder decodeObjectOfClass:[OIDAuthorizationRequest class] forKey:kRequestKey];
|
||||
self = [self initWithRequest:request parameters:@{ }];
|
||||
if (self) {
|
||||
[OIDFieldMapping decodeWithCoder:aDecoder map:[[self class] fieldMap] instance:self];
|
||||
_additionalParameters = [aDecoder decodeObjectOfClasses:[OIDFieldMapping JSONTypes]
|
||||
forKey:kAdditionalParametersKey];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:_request forKey:kRequestKey];
|
||||
[OIDFieldMapping encodeWithCoder:aCoder map:[[self class] fieldMap] instance:self];
|
||||
[aCoder encodeObject:_additionalParameters forKey:kAdditionalParametersKey];
|
||||
}
|
||||
|
||||
#pragma mark - NSObject overrides
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"<%@: %p, authorizationCode: %@, state: \"%@\", accessToken: "
|
||||
"\"%@\", accessTokenExpirationDate: %@, tokenType: %@, "
|
||||
"idToken: \"%@\", scope: \"%@\", additionalParameters: %@, "
|
||||
"request: %@>",
|
||||
NSStringFromClass([self class]),
|
||||
(void *)self,
|
||||
_authorizationCode,
|
||||
_state,
|
||||
[OIDTokenUtilities redact:_accessToken],
|
||||
_accessTokenExpirationDate,
|
||||
_tokenType,
|
||||
[OIDTokenUtilities redact:_idToken],
|
||||
_scope,
|
||||
_additionalParameters,
|
||||
_request];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (OIDTokenRequest *)tokenExchangeRequest {
|
||||
return [self tokenExchangeRequestWithAdditionalParameters:nil];
|
||||
}
|
||||
|
||||
- (OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters:
|
||||
(NSDictionary<NSString *, NSString *> *)additionalParameters {
|
||||
// TODO: add a unit test to confirm exception is thrown when expected and the request is created
|
||||
// with the correct parameters.
|
||||
if (!_authorizationCode) {
|
||||
[NSException raise:kTokenExchangeRequestException
|
||||
format:kTokenExchangeRequestException];
|
||||
}
|
||||
return [[OIDTokenRequest alloc] initWithConfiguration:_request.configuration
|
||||
grantType:OIDGrantTypeAuthorizationCode
|
||||
authorizationCode:_authorizationCode
|
||||
redirectURL:_request.redirectURL
|
||||
clientID:_request.clientID
|
||||
clientSecret:_request.clientSecret
|
||||
scope:nil
|
||||
refreshToken:nil
|
||||
codeVerifier:_request.codeVerifier
|
||||
additionalParameters:additionalParameters];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,170 @@
|
||||
/*! @file OIDAuthorizationService.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 OIDAuthorization;
|
||||
@class OIDAuthorizationRequest;
|
||||
@class OIDAuthorizationResponse;
|
||||
@class OIDEndSessionRequest;
|
||||
@class OIDEndSessionResponse;
|
||||
@class OIDRegistrationRequest;
|
||||
@class OIDRegistrationResponse;
|
||||
@class OIDServiceConfiguration;
|
||||
@class OIDTokenRequest;
|
||||
@class OIDTokenResponse;
|
||||
@protocol OIDExternalUserAgent;
|
||||
@protocol OIDExternalUserAgentSession;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Represents the type of block used as a callback for creating a service configuration from
|
||||
a remote OpenID Connect Discovery document.
|
||||
@param configuration The service configuration, if available.
|
||||
@param error The error if an error occurred.
|
||||
*/
|
||||
typedef void (^OIDDiscoveryCallback)(OIDServiceConfiguration *_Nullable configuration,
|
||||
NSError *_Nullable error);
|
||||
|
||||
/*! @brief Represents the type of block used as a callback for various methods of
|
||||
@c OIDAuthorizationService.
|
||||
@param authorizationResponse The authorization response, if available.
|
||||
@param error The error if an error occurred.
|
||||
*/
|
||||
typedef void (^OIDAuthorizationCallback)(OIDAuthorizationResponse *_Nullable authorizationResponse,
|
||||
NSError *_Nullable error);
|
||||
|
||||
/*! @brief Block used as a callback for the end-session request of @c OIDAuthorizationService.
|
||||
@param endSessionResponse The end-session response, if available.
|
||||
@param error The error if an error occurred.
|
||||
*/
|
||||
typedef void (^OIDEndSessionCallback)(OIDEndSessionResponse *_Nullable endSessionResponse,
|
||||
NSError *_Nullable error);
|
||||
|
||||
/*! @brief Represents the type of block used as a callback for various methods of
|
||||
@c OIDAuthorizationService.
|
||||
@param tokenResponse The token response, if available.
|
||||
@param error The error if an error occurred.
|
||||
*/
|
||||
typedef void (^OIDTokenCallback)(OIDTokenResponse *_Nullable tokenResponse,
|
||||
NSError *_Nullable error);
|
||||
|
||||
/*! @brief Represents the type of dictionary used to specify additional querystring parameters
|
||||
when making authorization or token endpoint requests.
|
||||
*/
|
||||
typedef NSDictionary<NSString *, NSString *> *_Nullable OIDTokenEndpointParameters;
|
||||
|
||||
/*! @brief Represents the type of block used as a callback for various methods of
|
||||
@c OIDAuthorizationService.
|
||||
@param registrationResponse The registration response, if available.
|
||||
@param error The error if an error occurred.
|
||||
*/
|
||||
typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable registrationResponse,
|
||||
NSError *_Nullable error);
|
||||
|
||||
/*! @brief Performs various OAuth and OpenID Connect related calls via the user agent or
|
||||
\NSURLSession.
|
||||
*/
|
||||
@interface OIDAuthorizationService : NSObject
|
||||
|
||||
/*! @brief The service's configuration.
|
||||
@remarks Each authorization service is initialized with a configuration. This configuration
|
||||
specifies how to connect to a particular OAuth provider. Clients should use separate
|
||||
authorization service instances for each provider they wish to integrate with.
|
||||
Configurations may be created manually, or via an OpenID Connect Discovery Document.
|
||||
*/
|
||||
@property(nonatomic, readonly) OIDServiceConfiguration *configuration;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. This class should not be initialized.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Convenience method for creating an authorization service configuration from an OpenID
|
||||
Connect compliant issuer URL.
|
||||
@param issuerURL The service provider's OpenID Connect issuer.
|
||||
@param completion A block which will be invoked when the authorization service configuration has
|
||||
been created, or when an error has occurred.
|
||||
@see https://openid.net/specs/openid-connect-discovery-1_0.html
|
||||
*/
|
||||
+ (void)discoverServiceConfigurationForIssuer:(NSURL *)issuerURL
|
||||
completion:(OIDDiscoveryCallback)completion;
|
||||
|
||||
|
||||
/*! @brief Convenience method for creating an authorization service configuration from an OpenID
|
||||
Connect compliant identity provider's discovery document.
|
||||
@param discoveryURL The URL of the service provider's OpenID Connect discovery document.
|
||||
@param completion A block which will be invoked when the authorization service configuration has
|
||||
been created, or when an error has occurred.
|
||||
@see https://openid.net/specs/openid-connect-discovery-1_0.html
|
||||
*/
|
||||
+ (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL
|
||||
completion:(OIDDiscoveryCallback)completion;
|
||||
|
||||
/*! @brief Perform an authorization flow using a generic flow shim.
|
||||
@param request The authorization request.
|
||||
@param externalUserAgent Generic external user-agent that can present an authorization
|
||||
request.
|
||||
@param callback The method called when the request has completed or failed.
|
||||
@return A @c OIDExternalUserAgentSession instance which will terminate when it
|
||||
receives a @c OIDExternalUserAgentSession.cancel message, or after processing a
|
||||
@c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message.
|
||||
*/
|
||||
+ (id<OIDExternalUserAgentSession>) presentAuthorizationRequest:(OIDAuthorizationRequest *)request
|
||||
externalUserAgent:(id<OIDExternalUserAgent>)externalUserAgent
|
||||
callback:(OIDAuthorizationCallback)callback;
|
||||
|
||||
/*! @brief Perform a logout request.
|
||||
@param request The end-session logout request.
|
||||
@param externalUserAgent Generic external user-agent that can present user-agent requests.
|
||||
@param callback The method called when the request has completed or failed.
|
||||
@return A @c OIDExternalUserAgentSession instance which will terminate when it
|
||||
receives a @c OIDExternalUserAgentSession.cancel message, or after processing a
|
||||
@c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message.
|
||||
@see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout
|
||||
*/
|
||||
+ (id<OIDExternalUserAgentSession>)
|
||||
presentEndSessionRequest:(OIDEndSessionRequest *)request
|
||||
externalUserAgent:(id<OIDExternalUserAgent>)externalUserAgent
|
||||
callback:(OIDEndSessionCallback)callback;
|
||||
|
||||
/*! @brief Performs a token request.
|
||||
@param request The token request.
|
||||
@param callback The method called when the request has completed or failed.
|
||||
*/
|
||||
+ (void)performTokenRequest:(OIDTokenRequest *)request callback:(OIDTokenCallback)callback;
|
||||
|
||||
/*! @brief Performs a token request.
|
||||
@param request The token request.
|
||||
@param authorizationResponse The original authorization response related to this token request.
|
||||
@param callback The method called when the request has completed or failed.
|
||||
*/
|
||||
+ (void)performTokenRequest:(OIDTokenRequest *)request
|
||||
originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse
|
||||
callback:(OIDTokenCallback)callback;
|
||||
|
||||
/*! @brief Performs a registration request.
|
||||
@param request The registration request.
|
||||
@param completion The method called when the request has completed or failed.
|
||||
*/
|
||||
+ (void)performRegistrationRequest:(OIDRegistrationRequest *)request
|
||||
completion:(OIDRegistrationCompletion)completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,790 @@
|
||||
/*! @file OIDAuthorizationService.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDAuthorizationService.h"
|
||||
|
||||
#import "OIDAuthorizationRequest.h"
|
||||
#import "OIDAuthorizationResponse.h"
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDEndSessionRequest.h"
|
||||
#import "OIDEndSessionResponse.h"
|
||||
#import "OIDErrorUtilities.h"
|
||||
#import "OIDExternalUserAgent.h"
|
||||
#import "OIDExternalUserAgentSession.h"
|
||||
#import "OIDIDToken.h"
|
||||
#import "OIDRegistrationRequest.h"
|
||||
#import "OIDRegistrationResponse.h"
|
||||
#import "OIDServiceConfiguration.h"
|
||||
#import "OIDServiceDiscovery.h"
|
||||
#import "OIDTokenRequest.h"
|
||||
#import "OIDTokenResponse.h"
|
||||
#import "OIDURLQueryComponent.h"
|
||||
#import "OIDURLSessionProvider.h"
|
||||
|
||||
/*! @brief Path appended to an OpenID Connect issuer for discovery
|
||||
@see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
|
||||
*/
|
||||
static NSString *const kOpenIDConfigurationWellKnownPath = @".well-known/openid-configuration";
|
||||
|
||||
/*! @brief Max allowable iat (Issued At) time skew
|
||||
@see https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||
*/
|
||||
static int const kOIDAuthorizationSessionIATMaxSkew = 600;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OIDAuthorizationSession : NSObject<OIDExternalUserAgentSession>
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithRequest:(OIDAuthorizationRequest *)request
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OIDAuthorizationSession {
|
||||
OIDAuthorizationRequest *_request;
|
||||
id<OIDExternalUserAgent> _externalUserAgent;
|
||||
OIDAuthorizationCallback _pendingauthorizationFlowCallback;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRequest:(OIDAuthorizationRequest *)request {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_request = [request copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)presentAuthorizationWithExternalUserAgent:(id<OIDExternalUserAgent>)externalUserAgent
|
||||
callback:(OIDAuthorizationCallback)authorizationFlowCallback {
|
||||
_externalUserAgent = externalUserAgent;
|
||||
_pendingauthorizationFlowCallback = authorizationFlowCallback;
|
||||
BOOL authorizationFlowStarted =
|
||||
[_externalUserAgent presentExternalUserAgentRequest:_request session:self];
|
||||
if (!authorizationFlowStarted) {
|
||||
NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeSafariOpenError
|
||||
underlyingError:nil
|
||||
description:@"Unable to open Safari."];
|
||||
[self didFinishWithResponse:nil error:safariError];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
[self cancelWithCompletion:nil];
|
||||
}
|
||||
|
||||
- (void)cancelWithCompletion:(nullable void (^)(void))completion {
|
||||
[_externalUserAgent dismissExternalUserAgentAnimated:YES completion:^{
|
||||
NSError *error = [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow
|
||||
underlyingError:nil
|
||||
description:@"Authorization flow was cancelled."];
|
||||
[self didFinishWithResponse:nil error:error];
|
||||
if (completion) completion();
|
||||
}];
|
||||
}
|
||||
|
||||
/*! @brief Does the redirection URL equal another URL down to the path component?
|
||||
@param URL The first redirect URI to compare.
|
||||
@param redirectionURL The second redirect URI to compare.
|
||||
@return YES if the URLs match down to the path level (query params are ignored).
|
||||
*/
|
||||
+ (BOOL)URL:(NSURL *)URL matchesRedirectionURL:(NSURL *)redirectionURL {
|
||||
NSURL *standardizedURL = [URL standardizedURL];
|
||||
NSURL *standardizedRedirectURL = [redirectionURL standardizedURL];
|
||||
|
||||
return [standardizedURL.scheme caseInsensitiveCompare:standardizedRedirectURL.scheme] == NSOrderedSame
|
||||
&& OIDIsEqualIncludingNil(standardizedURL.user, standardizedRedirectURL.user)
|
||||
&& OIDIsEqualIncludingNil(standardizedURL.password, standardizedRedirectURL.password)
|
||||
&& OIDIsEqualIncludingNil(standardizedURL.host, standardizedRedirectURL.host)
|
||||
&& OIDIsEqualIncludingNil(standardizedURL.port, standardizedRedirectURL.port)
|
||||
&& OIDIsEqualIncludingNil(standardizedURL.path, standardizedRedirectURL.path);
|
||||
}
|
||||
|
||||
- (BOOL)shouldHandleURL:(NSURL *)URL {
|
||||
return [[self class] URL:URL matchesRedirectionURL:_request.redirectURL];
|
||||
}
|
||||
|
||||
- (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL {
|
||||
// rejects URLs that don't match redirect (these may be completely unrelated to the authorization)
|
||||
if (![self shouldHandleURL:URL]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
AppAuthRequestTrace(@"Authorization Response: %@", URL);
|
||||
|
||||
// checks for an invalid state
|
||||
if (!_pendingauthorizationFlowCallback) {
|
||||
[NSException raise:OIDOAuthExceptionInvalidAuthorizationFlow
|
||||
format:@"%@", OIDOAuthExceptionInvalidAuthorizationFlow, nil];
|
||||
}
|
||||
|
||||
OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] initWithURL:URL];
|
||||
|
||||
NSError *error;
|
||||
OIDAuthorizationResponse *response = nil;
|
||||
|
||||
// checks for an OAuth error response as per RFC6749 Section 4.1.2.1
|
||||
if (query.dictionaryValue[OIDOAuthErrorFieldError]) {
|
||||
error = [OIDErrorUtilities OAuthErrorWithDomain:OIDOAuthAuthorizationErrorDomain
|
||||
OAuthResponse:query.dictionaryValue
|
||||
underlyingError:nil];
|
||||
}
|
||||
|
||||
// no error, should be a valid OAuth 2.0 response
|
||||
if (!error) {
|
||||
response = [[OIDAuthorizationResponse alloc] initWithRequest:_request
|
||||
parameters:query.dictionaryValue];
|
||||
|
||||
// verifies that the state in the response matches the state in the request, or both are nil
|
||||
if (!OIDIsEqualIncludingNil(_request.state, response.state)) {
|
||||
NSMutableDictionary *userInfo = [query.dictionaryValue mutableCopy];
|
||||
userInfo[NSLocalizedDescriptionKey] =
|
||||
[NSString stringWithFormat:@"State mismatch, expecting %@ but got %@ in authorization "
|
||||
"response %@",
|
||||
_request.state,
|
||||
response.state,
|
||||
response];
|
||||
response = nil;
|
||||
error = [NSError errorWithDomain:OIDOAuthAuthorizationErrorDomain
|
||||
code:OIDErrorCodeOAuthAuthorizationClientError
|
||||
userInfo:userInfo];
|
||||
}
|
||||
}
|
||||
|
||||
[_externalUserAgent dismissExternalUserAgentAnimated:YES completion:^{
|
||||
[self didFinishWithResponse:response error:error];
|
||||
}];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)failExternalUserAgentFlowWithError:(NSError *)error {
|
||||
[self didFinishWithResponse:nil error:error];
|
||||
}
|
||||
|
||||
/*! @brief Invokes the pending callback and performs cleanup.
|
||||
@param response The authorization response, if any to return to the callback.
|
||||
@param error The error, if any, to return to the callback.
|
||||
*/
|
||||
- (void)didFinishWithResponse:(nullable OIDAuthorizationResponse *)response
|
||||
error:(nullable NSError *)error {
|
||||
OIDAuthorizationCallback callback = _pendingauthorizationFlowCallback;
|
||||
_pendingauthorizationFlowCallback = nil;
|
||||
_externalUserAgent = nil;
|
||||
if (callback) {
|
||||
callback(response, error);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface OIDEndSessionImplementation : NSObject<OIDExternalUserAgentSession> {
|
||||
// private variables
|
||||
OIDEndSessionRequest *_request;
|
||||
id<OIDExternalUserAgent> _externalUserAgent;
|
||||
OIDEndSessionCallback _pendingEndSessionCallback;
|
||||
}
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithRequest:(OIDEndSessionRequest *)request
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
@end
|
||||
|
||||
|
||||
@implementation OIDEndSessionImplementation
|
||||
|
||||
- (instancetype)initWithRequest:(OIDEndSessionRequest *)request {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_request = [request copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)presentAuthorizationWithExternalUserAgent:(id<OIDExternalUserAgent>)externalUserAgent
|
||||
callback:(OIDEndSessionCallback)authorizationFlowCallback {
|
||||
_externalUserAgent = externalUserAgent;
|
||||
_pendingEndSessionCallback = authorizationFlowCallback;
|
||||
BOOL authorizationFlowStarted =
|
||||
[_externalUserAgent presentExternalUserAgentRequest:_request session:self];
|
||||
if (!authorizationFlowStarted) {
|
||||
NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeSafariOpenError
|
||||
underlyingError:nil
|
||||
description:@"Unable to open Safari."];
|
||||
[self didFinishWithResponse:nil error:safariError];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
[self cancelWithCompletion:nil];
|
||||
}
|
||||
|
||||
- (void)cancelWithCompletion:(nullable void (^)(void))completion {
|
||||
[_externalUserAgent dismissExternalUserAgentAnimated:YES completion:^{
|
||||
NSError *error = [OIDErrorUtilities
|
||||
errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow
|
||||
underlyingError:nil
|
||||
description:nil];
|
||||
[self didFinishWithResponse:nil error:error];
|
||||
if (completion) completion();
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)shouldHandleURL:(NSURL *)URL {
|
||||
// The logic of when to handle the URL is the same as for authorization requests: should match
|
||||
// down to the path component.
|
||||
return [[OIDAuthorizationSession class] URL:URL
|
||||
matchesRedirectionURL:_request.postLogoutRedirectURL];
|
||||
}
|
||||
|
||||
- (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL {
|
||||
// rejects URLs that don't match redirect (these may be completely unrelated to the authorization)
|
||||
if (![self shouldHandleURL:URL]) {
|
||||
return NO;
|
||||
}
|
||||
// checks for an invalid state
|
||||
if (!_pendingEndSessionCallback) {
|
||||
[NSException raise:OIDOAuthExceptionInvalidAuthorizationFlow
|
||||
format:@"%@", OIDOAuthExceptionInvalidAuthorizationFlow, nil];
|
||||
}
|
||||
|
||||
|
||||
NSError *error;
|
||||
OIDEndSessionResponse *response = nil;
|
||||
|
||||
OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] initWithURL:URL];
|
||||
response = [[OIDEndSessionResponse alloc] initWithRequest:_request
|
||||
parameters:query.dictionaryValue];
|
||||
|
||||
// verifies that the state in the response matches the state in the request, or both are nil
|
||||
if (!OIDIsEqualIncludingNil(_request.state, response.state)) {
|
||||
NSMutableDictionary *userInfo = [query.dictionaryValue mutableCopy];
|
||||
userInfo[NSLocalizedDescriptionKey] =
|
||||
[NSString stringWithFormat:@"State mismatch, expecting %@ but got %@ in authorization "
|
||||
"response %@",
|
||||
_request.state,
|
||||
response.state,
|
||||
response];
|
||||
response = nil;
|
||||
error = [NSError errorWithDomain:OIDOAuthAuthorizationErrorDomain
|
||||
code:OIDErrorCodeOAuthAuthorizationClientError
|
||||
userInfo:userInfo];
|
||||
}
|
||||
|
||||
[_externalUserAgent dismissExternalUserAgentAnimated:YES completion:^{
|
||||
[self didFinishWithResponse:response error:error];
|
||||
}];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)failExternalUserAgentFlowWithError:(NSError *)error {
|
||||
[self didFinishWithResponse:nil error:error];
|
||||
}
|
||||
|
||||
/*! @brief Invokes the pending callback and performs cleanup.
|
||||
@param response The authorization response, if any to return to the callback.
|
||||
@param error The error, if any, to return to the callback.
|
||||
*/
|
||||
- (void)didFinishWithResponse:(nullable OIDEndSessionResponse *)response
|
||||
error:(nullable NSError *)error {
|
||||
OIDEndSessionCallback callback = _pendingEndSessionCallback;
|
||||
_pendingEndSessionCallback = nil;
|
||||
_externalUserAgent = nil;
|
||||
if (callback) {
|
||||
callback(response, error);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation OIDAuthorizationService
|
||||
|
||||
+ (void)discoverServiceConfigurationForIssuer:(NSURL *)issuerURL
|
||||
completion:(OIDDiscoveryCallback)completion {
|
||||
NSURL *fullDiscoveryURL =
|
||||
[issuerURL URLByAppendingPathComponent:kOpenIDConfigurationWellKnownPath];
|
||||
|
||||
[[self class] discoverServiceConfigurationForDiscoveryURL:fullDiscoveryURL
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
+ (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL
|
||||
completion:(OIDDiscoveryCallback)completion {
|
||||
|
||||
NSURLSession *session = [OIDURLSessionProvider session];
|
||||
NSURLSessionDataTask *task =
|
||||
[session dataTaskWithURL:discoveryURL
|
||||
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
// If we got any sort of error, just report it.
|
||||
if (error || !data) {
|
||||
NSString *errorDescription =
|
||||
[NSString stringWithFormat:@"Connection error fetching discovery document '%@': %@.",
|
||||
discoveryURL,
|
||||
error.localizedDescription];
|
||||
error = [OIDErrorUtilities errorWithCode:OIDErrorCodeNetworkError
|
||||
underlyingError:error
|
||||
description:errorDescription];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(nil, error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *)response;
|
||||
|
||||
// Check for non-200 status codes.
|
||||
// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse
|
||||
if (urlResponse.statusCode != 200) {
|
||||
NSError *URLResponseError = [OIDErrorUtilities HTTPErrorWithHTTPResponse:urlResponse
|
||||
data:data];
|
||||
NSString *errorDescription =
|
||||
[NSString stringWithFormat:@"Non-200 HTTP response (%d) fetching discovery document "
|
||||
"'%@'.",
|
||||
(int)urlResponse.statusCode,
|
||||
discoveryURL];
|
||||
error = [OIDErrorUtilities errorWithCode:OIDErrorCodeNetworkError
|
||||
underlyingError:URLResponseError
|
||||
description:errorDescription];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(nil, error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct an OIDServiceDiscovery with the received JSON.
|
||||
OIDServiceDiscovery *discovery =
|
||||
[[OIDServiceDiscovery alloc] initWithJSONData:data error:&error];
|
||||
if (error || !discovery) {
|
||||
NSString *errorDescription =
|
||||
[NSString stringWithFormat:@"JSON error parsing document at '%@': %@",
|
||||
discoveryURL,
|
||||
error.localizedDescription];
|
||||
error = [OIDErrorUtilities errorWithCode:OIDErrorCodeNetworkError
|
||||
underlyingError:error
|
||||
description:errorDescription];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(nil, error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Create our service configuration with the discovery document and return it.
|
||||
OIDServiceConfiguration *configuration =
|
||||
[[OIDServiceConfiguration alloc] initWithDiscoveryDocument:discovery];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(configuration, nil);
|
||||
});
|
||||
}];
|
||||
[task resume];
|
||||
}
|
||||
|
||||
#pragma mark - Authorization Endpoint
|
||||
|
||||
+ (id<OIDExternalUserAgentSession>) presentAuthorizationRequest:(OIDAuthorizationRequest *)request
|
||||
externalUserAgent:(id<OIDExternalUserAgent>)externalUserAgent
|
||||
callback:(OIDAuthorizationCallback)callback {
|
||||
|
||||
AppAuthRequestTrace(@"Authorization Request: %@", request);
|
||||
|
||||
OIDAuthorizationSession *flowSession = [[OIDAuthorizationSession alloc] initWithRequest:request];
|
||||
[flowSession presentAuthorizationWithExternalUserAgent:externalUserAgent callback:callback];
|
||||
return flowSession;
|
||||
}
|
||||
|
||||
+ (id<OIDExternalUserAgentSession>)
|
||||
presentEndSessionRequest:(OIDEndSessionRequest *)request
|
||||
externalUserAgent:(id<OIDExternalUserAgent>)externalUserAgent
|
||||
callback:(OIDEndSessionCallback)callback {
|
||||
OIDEndSessionImplementation *flowSession =
|
||||
[[OIDEndSessionImplementation alloc] initWithRequest:request];
|
||||
[flowSession presentAuthorizationWithExternalUserAgent:externalUserAgent callback:callback];
|
||||
return flowSession;
|
||||
}
|
||||
|
||||
#pragma mark - Token Endpoint
|
||||
|
||||
+ (void)performTokenRequest:(OIDTokenRequest *)request callback:(OIDTokenCallback)callback {
|
||||
[[self class] performTokenRequest:request
|
||||
originalAuthorizationResponse:nil
|
||||
callback:callback];
|
||||
}
|
||||
|
||||
+ (void)performTokenRequest:(OIDTokenRequest *)request
|
||||
originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse
|
||||
callback:(OIDTokenCallback)callback {
|
||||
|
||||
NSURLRequest *URLRequest = [request URLRequest];
|
||||
|
||||
AppAuthRequestTrace(@"Token Request: %@\nHeaders:%@\nHTTPBody: %@",
|
||||
URLRequest.URL,
|
||||
URLRequest.allHTTPHeaderFields,
|
||||
[[NSString alloc] initWithData:URLRequest.HTTPBody
|
||||
encoding:NSUTF8StringEncoding]);
|
||||
|
||||
NSURLSession *session = [OIDURLSessionProvider session];
|
||||
[[session dataTaskWithRequest:URLRequest
|
||||
completionHandler:^(NSData *_Nullable data,
|
||||
NSURLResponse *_Nullable response,
|
||||
NSError *_Nullable error) {
|
||||
if (error) {
|
||||
// A network error or server error occurred.
|
||||
NSString *errorDescription =
|
||||
[NSString stringWithFormat:@"Connection error making token request to '%@': %@.",
|
||||
URLRequest.URL,
|
||||
error.localizedDescription];
|
||||
NSError *returnedError =
|
||||
[OIDErrorUtilities errorWithCode:OIDErrorCodeNetworkError
|
||||
underlyingError:error
|
||||
description:errorDescription];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(nil, returnedError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
NSHTTPURLResponse *HTTPURLResponse = (NSHTTPURLResponse *)response;
|
||||
NSInteger statusCode = HTTPURLResponse.statusCode;
|
||||
AppAuthRequestTrace(@"Token Response: HTTP Status %d\nHTTPBody: %@",
|
||||
(int)statusCode,
|
||||
[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
|
||||
if (statusCode != 200) {
|
||||
// A server error occurred.
|
||||
NSError *serverError =
|
||||
[OIDErrorUtilities HTTPErrorWithHTTPResponse:HTTPURLResponse data:data];
|
||||
|
||||
// HTTP 4xx may indicate an RFC6749 Section 5.2 error response, attempts to parse as such.
|
||||
if (statusCode >= 400 && statusCode < 500) {
|
||||
NSError *jsonDeserializationError;
|
||||
NSDictionary<NSString *, NSObject<NSCopying> *> *json =
|
||||
[NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonDeserializationError];
|
||||
|
||||
// If the HTTP 4xx response parses as JSON and has an 'error' key, it's an OAuth error.
|
||||
// These errors are special as they indicate a problem with the authorization grant.
|
||||
if (json[OIDOAuthErrorFieldError]) {
|
||||
NSError *oauthError =
|
||||
[OIDErrorUtilities OAuthErrorWithDomain:OIDOAuthTokenErrorDomain
|
||||
OAuthResponse:json
|
||||
underlyingError:serverError];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(nil, oauthError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Status code indicates this is an error, but not an RFC6749 Section 5.2 error.
|
||||
NSString *errorDescription =
|
||||
[NSString stringWithFormat:@"Non-200 HTTP response (%d) making token request to '%@'.",
|
||||
(int)statusCode,
|
||||
URLRequest.URL];
|
||||
NSError *returnedError =
|
||||
[OIDErrorUtilities errorWithCode:OIDErrorCodeServerError
|
||||
underlyingError:serverError
|
||||
description:errorDescription];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(nil, returnedError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
NSError *jsonDeserializationError;
|
||||
NSDictionary<NSString *, NSObject<NSCopying> *> *json =
|
||||
[NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonDeserializationError];
|
||||
if (jsonDeserializationError) {
|
||||
// A problem occurred deserializing the response/JSON.
|
||||
NSString *errorDescription =
|
||||
[NSString stringWithFormat:@"JSON error parsing token response: %@",
|
||||
jsonDeserializationError.localizedDescription];
|
||||
NSError *returnedError =
|
||||
[OIDErrorUtilities errorWithCode:OIDErrorCodeJSONDeserializationError
|
||||
underlyingError:jsonDeserializationError
|
||||
description:errorDescription];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(nil, returnedError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
OIDTokenResponse *tokenResponse =
|
||||
[[OIDTokenResponse alloc] initWithRequest:request parameters:json];
|
||||
if (!tokenResponse) {
|
||||
// A problem occurred constructing the token response from the JSON.
|
||||
NSError *returnedError =
|
||||
[OIDErrorUtilities errorWithCode:OIDErrorCodeTokenResponseConstructionError
|
||||
underlyingError:jsonDeserializationError
|
||||
description:@"Token response invalid."];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(nil, returnedError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If an ID Token is included in the response, validates the ID Token following the rules
|
||||
// in OpenID Connect Core Section 3.1.3.7 for features that AppAuth directly supports
|
||||
// (which excludes rules #1, #4, #5, #7, #8, #12, and #13). Regarding rule #6, ID Tokens
|
||||
// received by this class are received via direct communication between the Client and the Token
|
||||
// Endpoint, thus we are exercising the option to rely only on the TLS validation. AppAuth
|
||||
// has a zero dependencies policy, and verifying the JWT signature would add a dependency.
|
||||
// Users of the library are welcome to perform the JWT signature verification themselves should
|
||||
// they wish.
|
||||
if (tokenResponse.idToken) {
|
||||
OIDIDToken *idToken = [[OIDIDToken alloc] initWithIDTokenString:tokenResponse.idToken];
|
||||
if (!idToken) {
|
||||
NSError *invalidIDToken =
|
||||
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenParsingError
|
||||
underlyingError:nil
|
||||
description:@"ID Token parsing failed"];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(nil, invalidIDToken);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// OpenID Connect Core Section 3.1.3.7. rule #1
|
||||
// Not supported: AppAuth does not support JWT encryption.
|
||||
|
||||
// OpenID Connect Core Section 3.1.3.7. rule #2
|
||||
// Validates that the issuer in the ID Token matches that of the discovery document.
|
||||
NSURL *issuer = tokenResponse.request.configuration.issuer;
|
||||
if (issuer && ![idToken.issuer isEqual:issuer]) {
|
||||
NSError *invalidIDToken =
|
||||
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError
|
||||
underlyingError:nil
|
||||
description:@"Issuer mismatch"];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(nil, invalidIDToken);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// OpenID Connect Core Section 3.1.3.7. rule #3 & Section 2 azp Claim
|
||||
// Validates that the aud (audience) Claim contains the client ID, or that the azp
|
||||
// (authorized party) Claim matches the client ID.
|
||||
NSString *clientID = tokenResponse.request.clientID;
|
||||
if (![idToken.audience containsObject:clientID] &&
|
||||
![idToken.claims[@"azp"] isEqualToString:clientID]) {
|
||||
NSError *invalidIDToken =
|
||||
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError
|
||||
underlyingError:nil
|
||||
description:@"Audience mismatch"];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(nil, invalidIDToken);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// OpenID Connect Core Section 3.1.3.7. rules #4 & #5
|
||||
// Not supported.
|
||||
|
||||
// OpenID Connect Core Section 3.1.3.7. rule #6
|
||||
// As noted above, AppAuth only supports the code flow which results in direct communication
|
||||
// of the ID Token from the Token Endpoint to the Client, and we are exercising the option to
|
||||
// use TSL server validation instead of checking the token signature. Users may additionally
|
||||
// check the token signature should they wish.
|
||||
|
||||
// OpenID Connect Core Section 3.1.3.7. rules #7 & #8
|
||||
// Not applicable. See rule #6.
|
||||
|
||||
// OpenID Connect Core Section 3.1.3.7. rule #9
|
||||
// Validates that the current time is before the expiry time.
|
||||
NSTimeInterval expiresAtDifference = [idToken.expiresAt timeIntervalSinceNow];
|
||||
if (expiresAtDifference < 0) {
|
||||
NSError *invalidIDToken =
|
||||
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError
|
||||
underlyingError:nil
|
||||
description:@"ID Token expired"];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(nil, invalidIDToken);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// OpenID Connect Core Section 3.1.3.7. rule #10
|
||||
// Validates that the issued at time is not more than +/- 10 minutes on the current time.
|
||||
NSTimeInterval issuedAtDifference = [idToken.issuedAt timeIntervalSinceNow];
|
||||
if (fabs(issuedAtDifference) > kOIDAuthorizationSessionIATMaxSkew) {
|
||||
NSString *message =
|
||||
[NSString stringWithFormat:@"Issued at time is more than %d seconds before or after "
|
||||
"the current time",
|
||||
kOIDAuthorizationSessionIATMaxSkew];
|
||||
NSError *invalidIDToken =
|
||||
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError
|
||||
underlyingError:nil
|
||||
description:message];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(nil, invalidIDToken);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Only relevant for the authorization_code response type
|
||||
if ([tokenResponse.request.grantType isEqual:OIDGrantTypeAuthorizationCode]) {
|
||||
// OpenID Connect Core Section 3.1.3.7. rule #11
|
||||
// Validates the nonce.
|
||||
NSString *nonce = authorizationResponse.request.nonce;
|
||||
if (nonce && ![idToken.nonce isEqual:nonce]) {
|
||||
NSError *invalidIDToken =
|
||||
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError
|
||||
underlyingError:nil
|
||||
description:@"Nonce mismatch"];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(nil, invalidIDToken);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// OpenID Connect Core Section 3.1.3.7. rules #12
|
||||
// ACR is not directly supported by AppAuth.
|
||||
|
||||
// OpenID Connect Core Section 3.1.3.7. rules #12
|
||||
// max_age is not directly supported by AppAuth.
|
||||
}
|
||||
|
||||
// Success
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(tokenResponse, nil);
|
||||
});
|
||||
}] resume];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Registration Endpoint
|
||||
|
||||
+ (void)performRegistrationRequest:(OIDRegistrationRequest *)request
|
||||
completion:(OIDRegistrationCompletion)completion {
|
||||
NSURLRequest *URLRequest = [request URLRequest];
|
||||
if (!URLRequest) {
|
||||
// A problem occurred deserializing the response/JSON.
|
||||
NSError *returnedError = [OIDErrorUtilities errorWithCode:OIDErrorCodeJSONSerializationError
|
||||
underlyingError:nil
|
||||
description:@"The registration request could not "
|
||||
"be serialized as JSON."];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(nil, returnedError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
NSURLSession *session = [OIDURLSessionProvider session];
|
||||
[[session dataTaskWithRequest:URLRequest
|
||||
completionHandler:^(NSData *_Nullable data,
|
||||
NSURLResponse *_Nullable response,
|
||||
NSError *_Nullable error) {
|
||||
if (error) {
|
||||
// A network error or server error occurred.
|
||||
NSString *errorDescription =
|
||||
[NSString stringWithFormat:@"Connection error making registration request to '%@': %@.",
|
||||
URLRequest.URL,
|
||||
error.localizedDescription];
|
||||
NSError *returnedError = [OIDErrorUtilities errorWithCode:OIDErrorCodeNetworkError
|
||||
underlyingError:error
|
||||
description:errorDescription];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(nil, returnedError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
NSHTTPURLResponse *HTTPURLResponse = (NSHTTPURLResponse *) response;
|
||||
|
||||
if (HTTPURLResponse.statusCode != 201 && HTTPURLResponse.statusCode != 200) {
|
||||
// A server error occurred.
|
||||
NSError *serverError = [OIDErrorUtilities HTTPErrorWithHTTPResponse:HTTPURLResponse
|
||||
data:data];
|
||||
|
||||
// HTTP 400 may indicate an OpenID Connect Dynamic Client Registration 1.0 Section 3.3 error
|
||||
// response, checks for that
|
||||
if (HTTPURLResponse.statusCode == 400) {
|
||||
NSError *jsonDeserializationError;
|
||||
NSDictionary<NSString *, NSObject <NSCopying> *> *json =
|
||||
[NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonDeserializationError];
|
||||
|
||||
// if the HTTP 400 response parses as JSON and has an 'error' key, it's an OAuth error
|
||||
// these errors are special as they indicate a problem with the authorization grant
|
||||
if (json[OIDOAuthErrorFieldError]) {
|
||||
NSError *oauthError =
|
||||
[OIDErrorUtilities OAuthErrorWithDomain:OIDOAuthRegistrationErrorDomain
|
||||
OAuthResponse:json
|
||||
underlyingError:serverError];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(nil, oauthError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// not an OAuth error, just a generic server error
|
||||
NSString *errorDescription =
|
||||
[NSString stringWithFormat:@"Non-200/201 HTTP response (%d) making registration request "
|
||||
"to '%@'.",
|
||||
(int)HTTPURLResponse.statusCode,
|
||||
URLRequest.URL];
|
||||
NSError *returnedError = [OIDErrorUtilities errorWithCode:OIDErrorCodeServerError
|
||||
underlyingError:serverError
|
||||
description:errorDescription];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(nil, returnedError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
NSError *jsonDeserializationError;
|
||||
NSDictionary<NSString *, NSObject <NSCopying> *> *json =
|
||||
[NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonDeserializationError];
|
||||
if (jsonDeserializationError) {
|
||||
// A problem occurred deserializing the response/JSON.
|
||||
NSString *errorDescription =
|
||||
[NSString stringWithFormat:@"JSON error parsing registration response: %@",
|
||||
jsonDeserializationError.localizedDescription];
|
||||
NSError *returnedError = [OIDErrorUtilities errorWithCode:OIDErrorCodeJSONDeserializationError
|
||||
underlyingError:jsonDeserializationError
|
||||
description:errorDescription];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(nil, returnedError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
OIDRegistrationResponse *registrationResponse =
|
||||
[[OIDRegistrationResponse alloc] initWithRequest:request
|
||||
parameters:json];
|
||||
if (!registrationResponse) {
|
||||
// A problem occurred constructing the registration response from the JSON.
|
||||
NSError *returnedError =
|
||||
[OIDErrorUtilities errorWithCode:OIDErrorCodeRegistrationResponseConstructionError
|
||||
underlyingError:nil
|
||||
description:@"Registration response invalid."];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(nil, returnedError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Success
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(registrationResponse, nil);
|
||||
});
|
||||
}] resume];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,51 @@
|
||||
/*! @file OIDClientMetadataParameters.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2016 The AppAuth for iOS 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Parameter name for the token endpoint authentication method.
|
||||
*/
|
||||
extern NSString *const OIDTokenEndpointAuthenticationMethodParam;
|
||||
|
||||
/*! @brief Parameter name for the application type.
|
||||
*/
|
||||
extern NSString *const OIDApplicationTypeParam;
|
||||
|
||||
/*! @brief Parameter name for the redirect URI values.
|
||||
*/
|
||||
extern NSString *const OIDRedirectURIsParam;
|
||||
|
||||
/*! @brief Parameter name for the response type values.
|
||||
*/
|
||||
extern NSString *const OIDResponseTypesParam;
|
||||
|
||||
/*! @brief Parameter name for the grant type values.
|
||||
*/
|
||||
extern NSString *const OIDGrantTypesParam;
|
||||
|
||||
/*! @brief Parameter name for the subject type.
|
||||
*/
|
||||
extern NSString *const OIDSubjectTypeParam;
|
||||
|
||||
/*! @brief Application type that indicates this client is a native (not a web) application.
|
||||
*/
|
||||
extern NSString *const OIDApplicationTypeNative;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,33 @@
|
||||
/*! @file OIDClientMetadataParameters.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2016 The AppAuth for iOS 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 "OIDClientMetadataParameters.h"
|
||||
|
||||
NSString *const OIDTokenEndpointAuthenticationMethodParam = @"token_endpoint_auth_method";
|
||||
|
||||
NSString *const OIDApplicationTypeParam = @"application_type";
|
||||
|
||||
NSString *const OIDRedirectURIsParam = @"redirect_uris";
|
||||
|
||||
NSString *const OIDResponseTypesParam = @"response_types";
|
||||
|
||||
NSString *const OIDGrantTypesParam = @"grant_types";
|
||||
|
||||
NSString *const OIDSubjectTypeParam = @"subject_type";
|
||||
|
||||
NSString *const OIDApplicationTypeNative = @"native";
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*! @file OIDDefines.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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.
|
||||
*/
|
||||
|
||||
/*! @def OIDIsEqualIncludingNil(x, y)
|
||||
@brief Returns YES if x and y are equal by reference or value.
|
||||
@discussion NOTE: parameters may be evaluated multiple times. Be careful if using this check
|
||||
with expressions - especially if the expressions have side effects.
|
||||
@param x An object.
|
||||
@param y An object.
|
||||
*/
|
||||
#define OIDIsEqualIncludingNil(x, y) (((x) == (y)) || [(x) isEqual:(y)])
|
||||
|
||||
/*! @def OID_UNAVAILABLE_USE_INITIALIZER(designatedInitializer)
|
||||
@brief Provides a template implementation for init-family methods which have been marked as
|
||||
NS_UNAVILABLE. Stops the compiler from giving a warning when it's the super class'
|
||||
designated initializer, and gives callers useful feedback telling them what the
|
||||
new designated initializer is.
|
||||
@remarks Takes a SEL as a parameter instead of a string so that we get compiler warnings if the
|
||||
designated intializer's signature changes.
|
||||
@param designatedInitializer A SEL referencing the designated initializer.
|
||||
*/
|
||||
#define OID_UNAVAILABLE_USE_INITIALIZER(designatedInitializer) { \
|
||||
NSString *reason = [NSString stringWithFormat:@"Called: %@\nDesignated Initializer:%@", \
|
||||
NSStringFromSelector(_cmd), \
|
||||
NSStringFromSelector(designatedInitializer)]; \
|
||||
@throw [NSException exceptionWithName:@"Attempt to call unavailable initializer." \
|
||||
reason:reason \
|
||||
userInfo:nil]; \
|
||||
}
|
||||
|
||||
#ifdef _APPAUTHTRACE
|
||||
# define AppAuthRequestTrace(fmt, ...) NSLog(fmt, ##__VA_ARGS__);
|
||||
#else // _APPAUTHTRACE
|
||||
# define AppAuthRequestTrace(...)
|
||||
#endif // _APPAUTHTRACE
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*! @file OIDEndSessionRequest.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>
|
||||
|
||||
#import "OIDExternalUserAgentRequest.h"
|
||||
|
||||
@class OIDServiceConfiguration;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OIDEndSessionRequest : NSObject
|
||||
<NSCopying, NSSecureCoding, OIDExternalUserAgentRequest>
|
||||
|
||||
/*! @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 The client's redirect URI.
|
||||
@remarks post_logout_redirect_uri
|
||||
@see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *postLogoutRedirectURL;
|
||||
|
||||
/*! @brief Previously issued ID Token passed to the end session endpoint as a hint about the End-User's current authenticated
|
||||
session with the Client
|
||||
@remarks id_token_hint
|
||||
@see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *idTokenHint;
|
||||
|
||||
/*! @brief An opaque value used by the client to maintain state between the request and callback.
|
||||
@remarks state
|
||||
@discussion If this value is not explicitly set, this library will automatically add state and
|
||||
perform appropriate validation of the state in the authorization response. It is recommended
|
||||
that the default implementation of this parameter be used wherever possible. Typically used
|
||||
to prevent CSRF attacks, as recommended in RFC6819 Section 5.3.5.
|
||||
@see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *state;
|
||||
|
||||
/*! @brief The client's additional authorization parameters.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDictionary<NSString *, NSString *> *additionalParameters;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use @c initWithConfiguration:clientId:scopes:redirectURL:additionalParameters:.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Creates an authorization request with opinionated defaults (a secure @c state).
|
||||
@param configuration The service's configuration.
|
||||
@param idTokenHint The previously issued ID Token
|
||||
@param postLogoutRedirectURL The client's post-logout redirect URI.
|
||||
callback.
|
||||
@param additionalParameters The client's additional authorization parameters.
|
||||
*/
|
||||
- (instancetype)
|
||||
initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
idTokenHint:(NSString *)idTokenHint
|
||||
postLogoutRedirectURL:(NSURL *)postLogoutRedirectURL
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters;
|
||||
|
||||
/*! @brief Designated initializer.
|
||||
@param configuration The service's configuration.
|
||||
@param idTokenHint The previously issued ID Token
|
||||
@param postLogoutRedirectURL The client's post-logout redirect URI.
|
||||
@param state An opaque value used by the client to maintain state between the request and
|
||||
callback.
|
||||
@param additionalParameters The client's additional authorization parameters.
|
||||
*/
|
||||
- (instancetype)
|
||||
initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
idTokenHint:(NSString *)idTokenHint
|
||||
postLogoutRedirectURL:(NSURL *)postLogoutRedirectURL
|
||||
state:(NSString *)state
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/*! @brief Constructs the request URI by adding the request parameters to the query component of the
|
||||
authorization endpoint URI using the "application/x-www-form-urlencoded" format.
|
||||
@return A URL representing the authorization request.
|
||||
@see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout
|
||||
*/
|
||||
- (NSURL *)endSessionRequestURL;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
/*! @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 "OIDEndSessionRequest.h"
|
||||
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDTokenUtilities.h"
|
||||
#import "OIDServiceConfiguration.h"
|
||||
#import "OIDServiceDiscovery.h"
|
||||
#import "OIDURLQueryComponent.h"
|
||||
|
||||
/*! @brief The key for the @c configuration property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kConfigurationKey = @"configuration";
|
||||
|
||||
/*! @brief Key used to encode the @c state property for @c NSSecureCoding, and on the URL request.
|
||||
*/
|
||||
static NSString *const kStateKey = @"state";
|
||||
|
||||
/*! @brief Key used to encode the @c postLogoutRedirectURL property for @c NSSecureCoding, and on the URL request.
|
||||
*/
|
||||
static NSString *const kPostLogoutRedirectURLKey = @"post_logout_redirect_uri";
|
||||
|
||||
/*! @brief Key used to encode the @c idTokenHint property for @c NSSecureCoding, and on the URL request.
|
||||
*/
|
||||
static NSString *const kIdTokenHintKey = @"id_token_hint";
|
||||
|
||||
/*! @brief Key used to encode the @c additionalParameters property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kAdditionalParametersKey = @"additionalParameters";
|
||||
|
||||
/*! @brief Number of random bytes generated for the @state.
|
||||
*/
|
||||
static NSUInteger const kStateSizeBytes = 32;
|
||||
|
||||
/*! @brief Assertion text for missing end_session_endpoint.
|
||||
*/
|
||||
static NSString *const OIDMissingEndSessionEndpointMessage =
|
||||
@"The service configuration is missing an end_session_endpoint.";
|
||||
|
||||
@implementation OIDEndSessionRequest
|
||||
|
||||
- (instancetype)init
|
||||
OID_UNAVAILABLE_USE_INITIALIZER(
|
||||
@selector(initWithConfiguration:
|
||||
idTokenHint:
|
||||
postLogoutRedirectURL:
|
||||
additionalParameters:)
|
||||
)
|
||||
|
||||
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
idTokenHint:(NSString *)idTokenHint
|
||||
postLogoutRedirectURL:(NSURL *)postLogoutRedirectURL
|
||||
state:(NSString *)state
|
||||
additionalParameters:(NSDictionary<NSString *,NSString *> *)additionalParameters
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_configuration = [configuration copy];
|
||||
_idTokenHint = [idTokenHint copy];
|
||||
_postLogoutRedirectURL = [postLogoutRedirectURL copy];
|
||||
_state = [state copy];
|
||||
_additionalParameters =
|
||||
[[NSDictionary alloc] initWithDictionary:additionalParameters copyItems:YES];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
idTokenHint:(NSString *)idTokenHint
|
||||
postLogoutRedirectURL:(NSURL *)postLogoutRedirectURL
|
||||
additionalParameters:(NSDictionary<NSString *,NSString *> *)additionalParameters
|
||||
{
|
||||
return [self initWithConfiguration:configuration
|
||||
idTokenHint:idTokenHint
|
||||
postLogoutRedirectURL:postLogoutRedirectURL
|
||||
state:[[self class] generateState]
|
||||
additionalParameters:additionalParameters];
|
||||
}
|
||||
#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 *idTokenHint = [aDecoder decodeObjectOfClass:[NSString class] forKey:kIdTokenHintKey];
|
||||
NSURL *postLogoutRedirectURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kPostLogoutRedirectURLKey];
|
||||
NSString *state = [aDecoder decodeObjectOfClass:[NSString class] forKey:kStateKey];
|
||||
NSSet *additionalParameterCodingClasses = [NSSet setWithArray:@[
|
||||
[NSDictionary class],
|
||||
[NSString class]
|
||||
]];
|
||||
NSDictionary *additionalParameters = [aDecoder decodeObjectOfClasses:additionalParameterCodingClasses
|
||||
forKey:kAdditionalParametersKey];
|
||||
|
||||
self = [self initWithConfiguration:configuration
|
||||
idTokenHint:idTokenHint
|
||||
postLogoutRedirectURL:postLogoutRedirectURL
|
||||
state:state
|
||||
additionalParameters:additionalParameters];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:_configuration forKey:kConfigurationKey];
|
||||
[aCoder encodeObject:_idTokenHint forKey:kIdTokenHintKey];
|
||||
[aCoder encodeObject:_postLogoutRedirectURL forKey:kPostLogoutRedirectURLKey];
|
||||
[aCoder encodeObject:_state forKey:kStateKey];
|
||||
[aCoder encodeObject:_additionalParameters forKey:kAdditionalParametersKey];
|
||||
}
|
||||
|
||||
#pragma mark - NSObject overrides
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"<%@: %p, request: %@>",
|
||||
NSStringFromClass([self class]),
|
||||
(void *)self,
|
||||
self.endSessionRequestURL];
|
||||
}
|
||||
|
||||
+ (nullable NSString *)generateState {
|
||||
return [OIDTokenUtilities randomURLSafeStringWithSize:kStateSizeBytes];
|
||||
}
|
||||
|
||||
#pragma mark - OIDExternalUserAgentRequest
|
||||
|
||||
- (NSURL*)externalUserAgentRequestURL {
|
||||
return [self endSessionRequestURL];
|
||||
}
|
||||
|
||||
- (NSString *)redirectScheme {
|
||||
return [_postLogoutRedirectURL scheme];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (NSURL *)endSessionRequestURL {
|
||||
OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init];
|
||||
|
||||
// Add any additional parameters the client has specified.
|
||||
[query addParameters:_additionalParameters];
|
||||
|
||||
// Add optional parameters, as applicable.
|
||||
if (_idTokenHint) {
|
||||
[query addParameter:kIdTokenHintKey value:_idTokenHint];
|
||||
}
|
||||
|
||||
if (_postLogoutRedirectURL) {
|
||||
[query addParameter:kPostLogoutRedirectURLKey value:_postLogoutRedirectURL.absoluteString];
|
||||
}
|
||||
|
||||
if (_state) {
|
||||
[query addParameter:kStateKey value:_state];
|
||||
}
|
||||
|
||||
NSAssert(_configuration.endSessionEndpoint, OIDMissingEndSessionEndpointMessage);
|
||||
|
||||
// Construct the URL
|
||||
return [query URLByReplacingQueryInURL:_configuration.endSessionEndpoint];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,64 @@
|
||||
/*! @file OIDEndSessionResponse.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 OIDEndSessionRequest;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Represents the response to an End Session request.
|
||||
@see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout
|
||||
*/
|
||||
|
||||
@interface OIDEndSessionResponse : NSObject <NSCopying, NSSecureCoding>
|
||||
|
||||
/*! @brief The request which was serviced.
|
||||
*/
|
||||
@property(nonatomic, readonly) OIDEndSessionRequest *request;
|
||||
|
||||
/*! @brief REQUIRED if the "state" parameter was present in the client end-session request. The
|
||||
exact value received from the client.
|
||||
@remarks state
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *state;
|
||||
|
||||
/*! @brief Additional parameters returned from the end session endpoint.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSDictionary<NSString *, NSObject<NSCopying> *> *additionalParameters;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use initWithParameters:.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Designated initializer.
|
||||
@param request The serviced request.
|
||||
@param parameters The decoded parameters returned from the End Session Endpoint.
|
||||
@remarks Known parameters are extracted from the @c parameters parameter and the normative
|
||||
properties are populated. Non-normative parameters are placed in the
|
||||
@c #additionalParameters dictionary.
|
||||
*/
|
||||
- (instancetype)initWithRequest:(OIDEndSessionRequest *)request
|
||||
parameters:(NSDictionary<NSString *, NSObject<NSCopying> *> *)parameters
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*! @file OIDEndSessionResponse.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 "OIDEndSessionResponse.h"
|
||||
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDEndSessionRequest.h"
|
||||
#import "OIDFieldMapping.h"
|
||||
|
||||
/*! @brief The key for the @c state property in the incoming parameters and for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kStateKey = @"state";
|
||||
|
||||
/*! @brief Key used to encode the @c request property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kRequestKey = @"request";
|
||||
|
||||
/*! @brief Key used to encode the @c additionalParameters property for
|
||||
@c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kAdditionalParametersKey = @"additionalParameters";
|
||||
|
||||
@implementation OIDEndSessionResponse
|
||||
|
||||
#pragma mark - Initializers
|
||||
|
||||
- (instancetype)init
|
||||
OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:))
|
||||
|
||||
- (instancetype)initWithRequest:(OIDEndSessionRequest *)request
|
||||
parameters:(NSDictionary<NSString *,NSObject<NSCopying> *> *)parameters {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_request = [request copy];
|
||||
NSDictionary<NSString *, NSObject<NSCopying> *> *additionalParameters =
|
||||
[OIDFieldMapping remainingParametersWithMap:[[self class] fieldMap]
|
||||
parameters:parameters
|
||||
instance:self];
|
||||
_additionalParameters = additionalParameters;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/*! @brief Returns a mapping of incoming parameters to instance variables.
|
||||
@return A mapping of incoming parameters to instance variables.
|
||||
*/
|
||||
+ (NSDictionary<NSString *, OIDFieldMapping *> *)fieldMap {
|
||||
static NSMutableDictionary<NSString *, OIDFieldMapping *> *fieldMap;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
fieldMap = [NSMutableDictionary dictionary];
|
||||
fieldMap[kStateKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_state" type:[NSString class]];
|
||||
});
|
||||
return fieldMap;
|
||||
}
|
||||
|
||||
#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 {
|
||||
OIDEndSessionRequest *request =
|
||||
[aDecoder decodeObjectOfClass:[OIDEndSessionRequest class] forKey:kRequestKey];
|
||||
self = [self initWithRequest:request parameters:@{ }];
|
||||
if (self) {
|
||||
[OIDFieldMapping decodeWithCoder:aDecoder map:[[self class] fieldMap] instance:self];
|
||||
_additionalParameters = [aDecoder decodeObjectOfClasses:[OIDFieldMapping JSONTypes]
|
||||
forKey:kAdditionalParametersKey];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:_request forKey:kRequestKey];
|
||||
[OIDFieldMapping encodeWithCoder:aCoder map:[[self class] fieldMap] instance:self];
|
||||
[aCoder encodeObject:_additionalParameters forKey:kAdditionalParametersKey];
|
||||
}
|
||||
|
||||
#pragma mark - NSObject overrides
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"<%@: %p, state: \"%@\", "
|
||||
"additionalParameters: %@, request: %@>",
|
||||
NSStringFromClass([self class]),
|
||||
(void *)self,
|
||||
_state,
|
||||
_additionalParameters,
|
||||
_request];
|
||||
}
|
||||
@end
|
||||
+393
@@ -0,0 +1,393 @@
|
||||
/*! @file OIDError.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief The error domain for all NSErrors returned from the AppAuth library.
|
||||
*/
|
||||
extern NSString *const OIDGeneralErrorDomain;
|
||||
|
||||
/*! @brief The error domain for OAuth specific errors on the authorization endpoint.
|
||||
@discussion This error domain is used when the server responds to an authorization request
|
||||
with an explicit OAuth error, as defined by RFC6749 Section 4.1.2.1. If the authorization
|
||||
response is invalid and not explicitly an error response, another error domain will be used.
|
||||
The error response parameter dictionary is available in the
|
||||
\NSError_userInfo dictionary using the @c ::OIDOAuthErrorResponseErrorKey key.
|
||||
The \NSError_code will be one of the @c ::OIDErrorCodeOAuthAuthorization enum values.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
extern NSString *const OIDOAuthAuthorizationErrorDomain;
|
||||
|
||||
/*! @brief The error domain for OAuth specific errors on the token endpoint.
|
||||
@discussion This error domain is used when the server responds with HTTP 400 and an OAuth error,
|
||||
as defined RFC6749 Section 5.2. If an HTTP 400 response does not parse as an OAuth error
|
||||
(i.e. no 'error' field is present or the JSON is invalid), another error domain will be
|
||||
used. The entire OAuth error response dictionary is available in the \NSError_userInfo
|
||||
dictionary using the @c ::OIDOAuthErrorResponseErrorKey key. Unlike transient network
|
||||
errors, errors in this domain invalidate the authentication state, and either indicate a
|
||||
client error or require user interaction (i.e. reauthentication) to resolve.
|
||||
The \NSError_code will be one of the @c ::OIDErrorCodeOAuthToken enum values.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
extern NSString *const OIDOAuthTokenErrorDomain;
|
||||
|
||||
/*! @brief The error domain for dynamic client registration errors.
|
||||
@discussion This error domain is used when the server responds with HTTP 400 and an OAuth error,
|
||||
as defined in OpenID Connect Dynamic Client Registration 1.0 Section 3.3. If an HTTP 400
|
||||
response does not parse as an OAuth error (i.e. no 'error' field is present or the JSON is
|
||||
invalid), another error domain will be used. The entire OAuth error response dictionary is
|
||||
available in the \NSError_userInfo dictionary using the @c ::OIDOAuthErrorResponseErrorKey
|
||||
key. Unlike transient network errors, errors in this domain invalidate the authentication
|
||||
state, and indicates a client error.
|
||||
The \NSError_code will be one of the @c ::OIDErrorCodeOAuthToken enum values.
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError
|
||||
*/
|
||||
extern NSString *const OIDOAuthRegistrationErrorDomain;
|
||||
|
||||
/*! @brief The error domain for authorization errors encountered out of band on the resource server.
|
||||
*/
|
||||
extern NSString *const OIDResourceServerAuthorizationErrorDomain;
|
||||
|
||||
/*! @brief An error domain representing received HTTP errors.
|
||||
*/
|
||||
extern NSString *const OIDHTTPErrorDomain;
|
||||
|
||||
/*! @brief An error key for the original OAuth error response (if any).
|
||||
*/
|
||||
extern NSString *const OIDOAuthErrorResponseErrorKey;
|
||||
|
||||
/*! @brief The key of the 'error' response field in a RFC6749 Section 5.2 response.
|
||||
@remark error
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
extern NSString *const OIDOAuthErrorFieldError;
|
||||
|
||||
/*! @brief The key of the 'error_description' response field in a RFC6749 Section 5.2 response.
|
||||
@remark error_description
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
extern NSString *const OIDOAuthErrorFieldErrorDescription;
|
||||
|
||||
/*! @brief The key of the 'error_uri' response field in a RFC6749 Section 5.2 response.
|
||||
@remark error_uri
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
extern NSString *const OIDOAuthErrorFieldErrorURI;
|
||||
|
||||
/*! @brief The various error codes returned from the AppAuth library.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, OIDErrorCode) {
|
||||
/*! @brief Indicates a problem parsing an OpenID Connect Service Discovery document.
|
||||
*/
|
||||
OIDErrorCodeInvalidDiscoveryDocument = -2,
|
||||
|
||||
/*! @brief Indicates the user manually canceled the OAuth authorization code flow.
|
||||
*/
|
||||
OIDErrorCodeUserCanceledAuthorizationFlow = -3,
|
||||
|
||||
/*! @brief Indicates an OAuth authorization flow was programmatically cancelled.
|
||||
*/
|
||||
OIDErrorCodeProgramCanceledAuthorizationFlow = -4,
|
||||
|
||||
/*! @brief Indicates a network error or server error occurred.
|
||||
*/
|
||||
OIDErrorCodeNetworkError = -5,
|
||||
|
||||
/*! @brief Indicates a server error occurred.
|
||||
*/
|
||||
OIDErrorCodeServerError = -6,
|
||||
|
||||
/*! @brief Indicates a problem occurred deserializing the response/JSON.
|
||||
*/
|
||||
OIDErrorCodeJSONDeserializationError = -7,
|
||||
|
||||
/*! @brief Indicates a problem occurred constructing the token response from the JSON.
|
||||
*/
|
||||
OIDErrorCodeTokenResponseConstructionError = -8,
|
||||
|
||||
/*! @brief @c UIApplication.openURL: returned NO when attempting to open the authorization
|
||||
request in mobile Safari.
|
||||
*/
|
||||
OIDErrorCodeSafariOpenError = -9,
|
||||
|
||||
/*! @brief @c NSWorkspace.openURL returned NO when attempting to open the authorization
|
||||
request in the default browser.
|
||||
*/
|
||||
OIDErrorCodeBrowserOpenError = -10,
|
||||
|
||||
/*! @brief Indicates a problem when trying to refresh the tokens.
|
||||
*/
|
||||
OIDErrorCodeTokenRefreshError = -11,
|
||||
|
||||
/*! @brief Indicates a problem occurred constructing the registration response from the JSON.
|
||||
*/
|
||||
OIDErrorCodeRegistrationResponseConstructionError = -12,
|
||||
|
||||
/*! @brief Indicates a problem occurred deserializing the response/JSON.
|
||||
*/
|
||||
OIDErrorCodeJSONSerializationError = -13,
|
||||
|
||||
/*! @brief The ID Token did not parse.
|
||||
*/
|
||||
OIDErrorCodeIDTokenParsingError = -14,
|
||||
|
||||
/*! @brief The ID Token did not pass validation (e.g. issuer, audience checks).
|
||||
*/
|
||||
OIDErrorCodeIDTokenFailedValidationError = -15,
|
||||
};
|
||||
|
||||
/*! @brief Enum of all possible OAuth error codes as defined by RFC6749
|
||||
@discussion Used by @c ::OIDErrorCodeOAuthAuthorization and @c ::OIDErrorCodeOAuthToken
|
||||
which define endpoint-specific subsets of OAuth codes. Those enum types are down-castable
|
||||
to this one.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-11.4
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, OIDErrorCodeOAuth) {
|
||||
|
||||
/*! @remarks invalid_request
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthInvalidRequest = -2,
|
||||
|
||||
/*! @remarks unauthorized_client
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthUnauthorizedClient = -3,
|
||||
|
||||
/*! @remarks access_denied
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthAccessDenied = -4,
|
||||
|
||||
/*! @remarks unsupported_response_type
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthUnsupportedResponseType = -5,
|
||||
|
||||
/*! @remarks invalid_scope
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthInvalidScope = -6,
|
||||
|
||||
/*! @remarks server_error
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthServerError = -7,
|
||||
|
||||
/*! @remarks temporarily_unavailable
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthTemporarilyUnavailable = -8,
|
||||
|
||||
/*! @remarks invalid_client
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthInvalidClient = -9,
|
||||
|
||||
/*! @remarks invalid_grant
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthInvalidGrant = -10,
|
||||
|
||||
/*! @remarks unsupported_grant_type
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthUnsupportedGrantType = -11,
|
||||
|
||||
/*! @remarks invalid_redirect_uri
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError
|
||||
*/
|
||||
OIDErrorCodeOAuthInvalidRedirectURI = -12,
|
||||
|
||||
/*! @remarks invalid_client_metadata
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError
|
||||
*/
|
||||
OIDErrorCodeOAuthInvalidClientMetadata = -13,
|
||||
|
||||
/*! @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
|
||||
authorization error.
|
||||
*/
|
||||
OIDErrorCodeOAuthClientError = -0xEFFF,
|
||||
|
||||
/*! @brief An OAuth error not known to this library
|
||||
@discussion Indicates an OAuth error as per RFC6749, but the error code was not in our
|
||||
list. It could be a custom error code, or one from an OAuth extension. See the "error" key
|
||||
of the \NSError_userInfo property. Such errors are assumed to invalidate the
|
||||
authentication state
|
||||
*/
|
||||
OIDErrorCodeOAuthOther = -0xF000,
|
||||
};
|
||||
|
||||
/*! @brief The error codes for the @c ::OIDOAuthAuthorizationErrorDomain error domain
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, OIDErrorCodeOAuthAuthorization) {
|
||||
/*! @remarks invalid_request
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthAuthorizationInvalidRequest = OIDErrorCodeOAuthInvalidRequest,
|
||||
|
||||
/*! @remarks unauthorized_client
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthAuthorizationUnauthorizedClient = OIDErrorCodeOAuthUnauthorizedClient,
|
||||
|
||||
/*! @remarks access_denied
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthAuthorizationAccessDenied =
|
||||
OIDErrorCodeOAuthAccessDenied,
|
||||
|
||||
/*! @remarks unsupported_response_type
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthAuthorizationUnsupportedResponseType =
|
||||
OIDErrorCodeOAuthUnsupportedResponseType,
|
||||
|
||||
/*! @brief Indicates a network error or server error occurred.
|
||||
@remarks invalid_scope
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthAuthorizationAuthorizationInvalidScope = OIDErrorCodeOAuthInvalidScope,
|
||||
|
||||
/*! @brief Indicates a server error occurred.
|
||||
@remarks server_error
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthAuthorizationServerError = OIDErrorCodeOAuthServerError,
|
||||
|
||||
/*! @remarks temporarily_unavailable
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthAuthorizationTemporarilyUnavailable = OIDErrorCodeOAuthTemporarilyUnavailable,
|
||||
|
||||
/*! @brief An authorization error occurring on the client rather than the server. For example,
|
||||
due to a state mismatch or client misconfiguration. Should be treated as an unrecoverable
|
||||
authorization error.
|
||||
*/
|
||||
OIDErrorCodeOAuthAuthorizationClientError = OIDErrorCodeOAuthClientError,
|
||||
|
||||
/*! @brief An authorization OAuth error not known to this library
|
||||
@discussion this indicates an OAuth error as per RFC6749, but the error code was not in our
|
||||
list. It could be a custom error code, or one from an OAuth extension. See the "error" key
|
||||
of the \NSError_userInfo property. We assume such errors are not transient.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
*/
|
||||
OIDErrorCodeOAuthAuthorizationOther = OIDErrorCodeOAuthOther,
|
||||
};
|
||||
|
||||
|
||||
/*! @brief The error codes for the @c ::OIDOAuthTokenErrorDomain error domain
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, OIDErrorCodeOAuthToken) {
|
||||
/*! @remarks invalid_request
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthTokenInvalidRequest = OIDErrorCodeOAuthInvalidRequest,
|
||||
|
||||
/*! @remarks invalid_client
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthTokenInvalidClient = OIDErrorCodeOAuthInvalidClient,
|
||||
|
||||
/*! @remarks invalid_grant
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthTokenInvalidGrant = OIDErrorCodeOAuthInvalidGrant,
|
||||
|
||||
/*! @remarks unauthorized_client
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthTokenUnauthorizedClient = OIDErrorCodeOAuthUnauthorizedClient,
|
||||
|
||||
/*! @remarks unsupported_grant_type
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthTokenUnsupportedGrantType = OIDErrorCodeOAuthUnsupportedGrantType,
|
||||
|
||||
/*! @remarks invalid_scope
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthTokenInvalidScope = OIDErrorCodeOAuthInvalidScope,
|
||||
|
||||
/*! @brief An unrecoverable token error occurring on the client rather than the server.
|
||||
*/
|
||||
OIDErrorCodeOAuthTokenClientError = OIDErrorCodeOAuthClientError,
|
||||
|
||||
/*! @brief A token endpoint OAuth error not known to this library
|
||||
@discussion this indicates an OAuth error as per RFC6749, but the error code was not in our
|
||||
list. It could be a custom error code, or one from an OAuth extension. See the "error" key
|
||||
of the \NSError_userInfo property. We assume such errors are not transient.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthTokenOther = OIDErrorCodeOAuthOther,
|
||||
};
|
||||
|
||||
/*! @brief The error codes for the @c ::OIDOAuthRegistrationErrorDomain error domain
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, OIDErrorCodeOAuthRegistration) {
|
||||
/*! @remarks invalid_request
|
||||
@see http://tools.ietf.org/html/rfc6750#section-3.1
|
||||
*/
|
||||
OIDErrorCodeOAuthRegistrationInvalidRequest = OIDErrorCodeOAuthInvalidRequest,
|
||||
|
||||
/*! @remarks invalid_redirect_uri
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError
|
||||
*/
|
||||
OIDErrorCodeOAuthRegistrationInvalidRedirectURI = OIDErrorCodeOAuthInvalidRedirectURI,
|
||||
|
||||
/*! @remarks invalid_client_metadata
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError
|
||||
*/
|
||||
OIDErrorCodeOAuthRegistrationInvalidClientMetadata = OIDErrorCodeOAuthInvalidClientMetadata,
|
||||
|
||||
/*! @brief An unrecoverable token error occurring on the client rather than the server.
|
||||
*/
|
||||
OIDErrorCodeOAuthRegistrationClientError = OIDErrorCodeOAuthClientError,
|
||||
|
||||
/*! @brief A registration endpoint OAuth error not known to this library
|
||||
@discussion this indicates an OAuth error, but the error code was not in our
|
||||
list. It could be a custom error code, or one from an OAuth extension. See the "error" key
|
||||
of the \NSError_userInfo property. We assume such errors are not transient.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
OIDErrorCodeOAuthRegistrationOther = OIDErrorCodeOAuthOther,
|
||||
};
|
||||
|
||||
|
||||
/*! @brief The exception text for the exception which occurs when a
|
||||
@c OIDExternalUserAgentSession receives a message after it has already completed.
|
||||
*/
|
||||
extern NSString *const OIDOAuthExceptionInvalidAuthorizationFlow;
|
||||
|
||||
/*! @brief The text for the exception which occurs when a Token Request is constructed
|
||||
with a null redirectURL for a grant_type that requires a nonnull Redirect
|
||||
*/
|
||||
extern NSString *const OIDOAuthExceptionInvalidTokenRequestNullRedirectURL;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*! @file OIDError.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDError.h"
|
||||
|
||||
NSString *const OIDGeneralErrorDomain = @"org.openid.appauth.general";
|
||||
|
||||
NSString *const OIDOAuthTokenErrorDomain = @"org.openid.appauth.oauth_token";
|
||||
|
||||
NSString *const OIDOAuthAuthorizationErrorDomain = @"org.openid.appauth.oauth_authorization";
|
||||
|
||||
NSString *const OIDOAuthRegistrationErrorDomain = @"org.openid.appauth.oauth_registration";
|
||||
|
||||
NSString *const OIDResourceServerAuthorizationErrorDomain = @"org.openid.appauth.resourceserver";
|
||||
|
||||
NSString *const OIDHTTPErrorDomain = @"org.openid.appauth.remote-http";
|
||||
|
||||
NSString *const OIDOAuthExceptionInvalidAuthorizationFlow = @"An OAuth redirect was sent to a "
|
||||
"OIDExternalUserAgentSession after it already completed.";
|
||||
|
||||
NSString *const OIDOAuthExceptionInvalidTokenRequestNullRedirectURL = @"A OIDTokenRequest was "
|
||||
"created with a grant_type that requires a redirectURL, but a null redirectURL was given";
|
||||
|
||||
NSString *const OIDOAuthErrorResponseErrorKey = @"OIDOAuthErrorResponseErrorKey";
|
||||
|
||||
NSString *const OIDOAuthErrorFieldError = @"error";
|
||||
|
||||
NSString *const OIDOAuthErrorFieldErrorDescription = @"error_description";
|
||||
|
||||
NSString *const OIDOAuthErrorFieldErrorURI = @"error_uri";
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*! @file OIDErrorUtilities.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
#import "OIDError.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Convenience methods for creating standardized \NSError instances.
|
||||
*/
|
||||
@interface OIDErrorUtilities : NSObject
|
||||
|
||||
/*! @brief Creates a standard \NSError from an @c ::OIDErrorCode and custom user info.
|
||||
Automatically populates the localized error description.
|
||||
@param code The error code.
|
||||
@param underlyingError The underlying error which occurred, if applicable.
|
||||
@param description A custom description, if applicable.
|
||||
@return An \NSError representing the error code.
|
||||
*/
|
||||
+ (NSError *)errorWithCode:(OIDErrorCode)code
|
||||
underlyingError:(nullable NSError *)underlyingError
|
||||
description:(nullable NSString *)description;
|
||||
|
||||
/*! @brief Creates a standard \NSError from an @c ::OIDErrorCode and custom user info.
|
||||
Automatically populates the localized error description.
|
||||
@param OAuthErrorDomain The OAuth error domain. Must be @c ::OIDOAuthAuthorizationErrorDomain or
|
||||
@c ::OIDOAuthTokenErrorDomain.
|
||||
@param errorResponse The dictionary from an OAuth error response (as per RFC6749 Section 5.2).
|
||||
@param underlyingError The underlying error which occurred, if applicable.
|
||||
@return An \NSError representing the OAuth error.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
+ (NSError *)OAuthErrorWithDomain:(NSString *)OAuthErrorDomain
|
||||
OAuthResponse:(NSDictionary *)errorResponse
|
||||
underlyingError:(nullable NSError *)underlyingError;
|
||||
|
||||
/*! @brief Creates a \NSError indicating that the resource server responded with an authorization
|
||||
error.
|
||||
@param code Your error code.
|
||||
@param errorResponse The resource server error response, if any.
|
||||
@param underlyingError The underlying error which occurred, if applicable.
|
||||
@return An \NSError representing the authorization error from the resource server.
|
||||
*/
|
||||
+ (NSError *)resourceServerAuthorizationErrorWithCode:(NSInteger)code
|
||||
errorResponse:(nullable NSDictionary *)errorResponse
|
||||
underlyingError:(nullable NSError *)underlyingError;
|
||||
|
||||
|
||||
/*! @brief Creates a standard \NSError from an \NSHTTPURLResponse. Automatically
|
||||
populates the localized error description with the response data associated with the
|
||||
\NSHTTPURLResponse, if available.
|
||||
@param HTTPURLResponse The response which indicates an error occurred.
|
||||
@param data The response data associated with the response which should be converted to an
|
||||
@c NSString assuming a UTF-8 encoding, if available.
|
||||
@return An \NSError representing the error.
|
||||
*/
|
||||
+ (NSError *)HTTPErrorWithHTTPResponse:(NSHTTPURLResponse *)HTTPURLResponse
|
||||
data:(nullable NSData *)data;
|
||||
|
||||
/*! @brief Raises an exception with the given name as both the name, and the message.
|
||||
@param name The name of the exception.
|
||||
*/
|
||||
+ (void)raiseException:(NSString *)name;
|
||||
|
||||
/*! @brief Raises an exception with the given name and message.
|
||||
@param name The name of the exception.
|
||||
@param message The message of the exception.
|
||||
*/
|
||||
+ (void)raiseException:(NSString *)name message:(NSString *)message;
|
||||
|
||||
/*! @brief Converts an OAuth error code into an @c ::OIDErrorCodeOAuth error code.
|
||||
@param errorCode The OAuth error code.
|
||||
@discussion Returns @c ::OIDErrorCodeOAuthOther if the string is not in AppAuth's list.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
+ (OIDErrorCodeOAuth)OAuthErrorCodeFromString:(NSString *)errorCode;
|
||||
|
||||
/*! @brief Returns true if the given error domain is an OAuth error domain.
|
||||
@param errorDomain The error domain to test.
|
||||
@discussion An OAuth error domain is used for errors returned per RFC6749 sections 4.1.2.1 and
|
||||
5.2. Other errors, such as network errors can also occur but they will not have an OAuth
|
||||
error domain.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
*/
|
||||
+ (BOOL)isOAuthErrorDomain:(NSString*)errorDomain;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
/*! @file OIDErrorUtilities.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDErrorUtilities.h"
|
||||
|
||||
@implementation OIDErrorUtilities
|
||||
|
||||
+ (NSError *)errorWithCode:(OIDErrorCode)code
|
||||
underlyingError:(NSError *)underlyingError
|
||||
description:(NSString *)description {
|
||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||
if (underlyingError) {
|
||||
userInfo[NSUnderlyingErrorKey] = underlyingError;
|
||||
}
|
||||
if (description) {
|
||||
userInfo[NSLocalizedDescriptionKey] = description;
|
||||
}
|
||||
// TODO: Populate localized description based on code.
|
||||
NSError *error = [NSError errorWithDomain:OIDGeneralErrorDomain
|
||||
code:code
|
||||
userInfo:userInfo];
|
||||
return error;
|
||||
}
|
||||
|
||||
+ (BOOL)isOAuthErrorDomain:(NSString *)errorDomain {
|
||||
return errorDomain == OIDOAuthRegistrationErrorDomain
|
||||
|| errorDomain == OIDOAuthAuthorizationErrorDomain
|
||||
|| errorDomain == OIDOAuthTokenErrorDomain;
|
||||
}
|
||||
|
||||
+ (NSError *)resourceServerAuthorizationErrorWithCode:(NSInteger)code
|
||||
errorResponse:(nullable NSDictionary *)errorResponse
|
||||
underlyingError:(nullable NSError *)underlyingError {
|
||||
// builds the userInfo dictionary with the full OAuth response and other information
|
||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||
if (errorResponse) {
|
||||
userInfo[OIDOAuthErrorResponseErrorKey] = errorResponse;
|
||||
}
|
||||
if (underlyingError) {
|
||||
userInfo[NSUnderlyingErrorKey] = underlyingError;
|
||||
}
|
||||
NSError *error = [NSError errorWithDomain:OIDResourceServerAuthorizationErrorDomain
|
||||
code:code
|
||||
userInfo:userInfo];
|
||||
return error;
|
||||
}
|
||||
|
||||
+ (NSError *)OAuthErrorWithDomain:(NSString *)oAuthErrorDomain
|
||||
OAuthResponse:(NSDictionary *)errorResponse
|
||||
underlyingError:(NSError *)underlyingError {
|
||||
// not a valid OAuth error
|
||||
if (![self isOAuthErrorDomain:oAuthErrorDomain]
|
||||
|| !errorResponse
|
||||
|| !errorResponse[OIDOAuthErrorFieldError]
|
||||
|| ![errorResponse[OIDOAuthErrorFieldError] isKindOfClass:[NSString class]]) {
|
||||
return [[self class] errorWithCode:OIDErrorCodeNetworkError
|
||||
underlyingError:underlyingError
|
||||
description:underlyingError.localizedDescription];
|
||||
}
|
||||
|
||||
// builds the userInfo dictionary with the full OAuth response and other information
|
||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||
userInfo[OIDOAuthErrorResponseErrorKey] = errorResponse;
|
||||
if (underlyingError) {
|
||||
userInfo[NSUnderlyingErrorKey] = underlyingError;
|
||||
}
|
||||
|
||||
NSString *oauthErrorCodeString = errorResponse[OIDOAuthErrorFieldError];
|
||||
NSString *oauthErrorMessage = nil;
|
||||
if ([errorResponse[OIDOAuthErrorFieldErrorDescription] isKindOfClass:[NSString class]]) {
|
||||
oauthErrorMessage = errorResponse[OIDOAuthErrorFieldErrorDescription];
|
||||
} else {
|
||||
oauthErrorMessage = [errorResponse[OIDOAuthErrorFieldErrorDescription] description];
|
||||
}
|
||||
NSString *oauthErrorURI = nil;
|
||||
if ([errorResponse[OIDOAuthErrorFieldErrorURI] isKindOfClass:[NSString class]]) {
|
||||
oauthErrorURI = errorResponse[OIDOAuthErrorFieldErrorURI];
|
||||
} else {
|
||||
oauthErrorURI = [errorResponse[OIDOAuthErrorFieldErrorURI] description];
|
||||
}
|
||||
|
||||
// builds the error description, using the information supplied by the server if possible
|
||||
NSMutableString *description = [NSMutableString string];
|
||||
[description appendString:oauthErrorCodeString];
|
||||
if (oauthErrorMessage) {
|
||||
[description appendString:@": "];
|
||||
[description appendString:oauthErrorMessage];
|
||||
}
|
||||
if (oauthErrorURI) {
|
||||
if ([description length] > 0) {
|
||||
[description appendString:@" - "];
|
||||
}
|
||||
[description appendString:oauthErrorURI];
|
||||
}
|
||||
if ([description length] == 0) {
|
||||
// backup description
|
||||
[description appendFormat:@"OAuth error: %@ - https://tools.ietf.org/html/rfc6749#section-5.2",
|
||||
oauthErrorCodeString];
|
||||
}
|
||||
userInfo[NSLocalizedDescriptionKey] = description;
|
||||
|
||||
// looks up the error code based on the "error" response param
|
||||
OIDErrorCodeOAuth code = [[self class] OAuthErrorCodeFromString:oauthErrorCodeString];
|
||||
|
||||
NSError *error = [NSError errorWithDomain:oAuthErrorDomain
|
||||
code:code
|
||||
userInfo:userInfo];
|
||||
return error;
|
||||
}
|
||||
|
||||
+ (NSError *)HTTPErrorWithHTTPResponse:(NSHTTPURLResponse *)HTTPURLResponse
|
||||
data:(nullable NSData *)data {
|
||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||
if (data) {
|
||||
NSString *serverResponse =
|
||||
[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
if (serverResponse) {
|
||||
userInfo[NSLocalizedDescriptionKey] = serverResponse;
|
||||
}
|
||||
}
|
||||
NSError *serverError =
|
||||
[NSError errorWithDomain:OIDHTTPErrorDomain
|
||||
code:HTTPURLResponse.statusCode
|
||||
userInfo:userInfo];
|
||||
return serverError;
|
||||
}
|
||||
|
||||
+ (OIDErrorCodeOAuth)OAuthErrorCodeFromString:(NSString *)errorCode {
|
||||
NSDictionary *errorCodes = @{
|
||||
@"invalid_request": @(OIDErrorCodeOAuthInvalidRequest),
|
||||
@"unauthorized_client": @(OIDErrorCodeOAuthUnauthorizedClient),
|
||||
@"access_denied": @(OIDErrorCodeOAuthAccessDenied),
|
||||
@"unsupported_response_type": @(OIDErrorCodeOAuthUnsupportedResponseType),
|
||||
@"invalid_scope": @(OIDErrorCodeOAuthInvalidScope),
|
||||
@"server_error": @(OIDErrorCodeOAuthServerError),
|
||||
@"temporarily_unavailable": @(OIDErrorCodeOAuthTemporarilyUnavailable),
|
||||
@"invalid_client": @(OIDErrorCodeOAuthInvalidClient),
|
||||
@"invalid_grant": @(OIDErrorCodeOAuthInvalidGrant),
|
||||
@"unsupported_grant_type": @(OIDErrorCodeOAuthUnsupportedGrantType),
|
||||
};
|
||||
NSNumber *code = errorCodes[errorCode];
|
||||
if (code) {
|
||||
return [code integerValue];
|
||||
} else {
|
||||
return OIDErrorCodeOAuthOther;
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)raiseException:(NSString *)name {
|
||||
[[self class] raiseException:name message:name];
|
||||
}
|
||||
|
||||
+ (void)raiseException:(NSString *)name message:(NSString *)message {
|
||||
[NSException raise:name format:@"%@", message];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,53 @@
|
||||
/*! @file OIDExternalUserAgent.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2016 Google Inc. 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>
|
||||
|
||||
@protocol OIDExternalUserAgentSession;
|
||||
@protocol OIDExternalUserAgentRequest;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @protocol OIDExternalUserAgent
|
||||
@brief An external user-agent UI that presents displays the request to the user. Clients may
|
||||
provide custom implementations of an external user-agent to customize the way the requests
|
||||
are presented to the end user.
|
||||
*/
|
||||
@protocol OIDExternalUserAgent<NSObject>
|
||||
|
||||
/*! @brief Presents the request in the external user-agent.
|
||||
@param request The request to be presented in the external user-agent.
|
||||
@param session The @c OIDExternalUserAgentSession instance that initiates presenting the UI.
|
||||
Concrete implementations of a @c OIDExternalUserAgent may call
|
||||
resumeExternalUserAgentFlowWithURL or failExternalUserAgentFlowWithError on session to either
|
||||
resume or fail the request.
|
||||
@return YES If the request UI was successfully presented to the user.
|
||||
*/
|
||||
- (BOOL)presentExternalUserAgentRequest:(id<OIDExternalUserAgentRequest> )request
|
||||
session:(id<OIDExternalUserAgentSession>)session;
|
||||
|
||||
/*! @brief Dimisses the external user-agent and calls completion when the dismiss operation ends.
|
||||
@param animated Whether or not the dismiss operation should be animated.
|
||||
@remarks Has no effect if no UI is presented.
|
||||
@param completion The block to be called when the dismiss operations ends
|
||||
*/
|
||||
- (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(void))completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,37 @@
|
||||
/*! @file OIDExternalUserAgent.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.
|
||||
*/
|
||||
|
||||
/*! @protocol OIDExternalUserAgent
|
||||
@brief An interface that any external user-agent request may implement to use the
|
||||
@c OIDExternalUserAgent flow.
|
||||
*/
|
||||
@protocol OIDExternalUserAgentRequest
|
||||
|
||||
/*! @brief Method to create and return the complete request URL instance.
|
||||
@return A @c NSURL instance which contains the URL to be opened in an external UI (i.e. browser)
|
||||
*/
|
||||
- (NSURL*)externalUserAgentRequestURL;
|
||||
|
||||
/*! @brief If this external user-agent request has a redirect URL, this should return its scheme.
|
||||
Since some external requests have optional callbacks (such as the end session endpoint), the
|
||||
return value of this method is nullable.
|
||||
@return A @c NSString instance that contains the scheme of a callback url, or nil if there is
|
||||
no callback url for this request.
|
||||
*/
|
||||
- (NSString*)redirectScheme;
|
||||
@end
|
||||
@@ -0,0 +1,65 @@
|
||||
/*! @file OIDExternalUserAgentSession.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.
|
||||
*/
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Represents an in-flight external user-agent session.
|
||||
*/
|
||||
@protocol OIDExternalUserAgentSession <NSObject>
|
||||
|
||||
/*! @brief Cancels the code flow session, invoking the request's callback with a cancelled error.
|
||||
@remarks Has no effect if called more than once, or after a
|
||||
@c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message was received.
|
||||
Will cause an error with code: @c ::OIDErrorCodeProgramCanceledAuthorizationFlow to be
|
||||
passed to the @c callback block passed to
|
||||
@c OIDAuthorizationService.presentAuthorizationRequest:presentingViewController:callback:
|
||||
*/
|
||||
- (void)cancel;
|
||||
|
||||
/*! @brief Cancels the code flow session, invoking the request's callback with a cancelled error.
|
||||
@remarks Has no effect if called more than once, or after a
|
||||
@c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message was received.
|
||||
Will cause an error with code: @c ::OIDErrorCodeProgramCanceledAuthorizationFlow to be
|
||||
passed to the @c callback block passed to
|
||||
@c OIDAuthorizationService.presentAuthorizationRequest:presentingViewController:callback:
|
||||
@param completion The block to be called when the cancel operation ends
|
||||
*/
|
||||
- (void)cancelWithCompletion:(nullable void (^)(void))completion;
|
||||
|
||||
/*! @brief Clients should call this method with the result of the external user-agent code flow if
|
||||
it becomes available.
|
||||
@param URL The redirect URL invoked by the server.
|
||||
@discussion When the URL represented a valid response, implementations should clean up any
|
||||
left-over UI state from the request, for example by closing the
|
||||
\SFSafariViewController or loopback HTTP listener if those were used. The completion block
|
||||
of the pending request should then be invoked.
|
||||
@remarks Has no effect if called more than once, or after a @c cancel message was received.
|
||||
@return YES if the passed URL matches the expected redirect URL and was consumed, NO otherwise.
|
||||
*/
|
||||
- (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL;
|
||||
|
||||
/*! @brief @c OIDExternalUserAgent or clients should call this method when the
|
||||
external user-agent flow failed with a non-OAuth error.
|
||||
@param error The error that is the reason for the failure of this external flow.
|
||||
@remarks Has no effect if called more than once, or after a @c cancel message was received.
|
||||
*/
|
||||
- (void)failExternalUserAgentFlowWithError:(NSError *)error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/*! @file OIDFieldMapping.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Represents a function which transforms incoming source values into instance variable
|
||||
values.
|
||||
*/
|
||||
typedef _Nullable id(^OIDFieldMappingConversionFunction)(NSObject *_Nullable value);
|
||||
|
||||
/*! @brief Describes the mapping of a key/value pair to an iVar with an optional conversion
|
||||
function.
|
||||
*/
|
||||
@interface OIDFieldMapping : NSObject
|
||||
|
||||
/*! @brief The name of the instance variable the field should be mapped to.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *name;
|
||||
|
||||
/*! @brief The type of the instance variable.
|
||||
*/
|
||||
@property(nonatomic, readonly) Class expectedType;
|
||||
|
||||
/*! @brief An optional conversion function which specifies a transform from the incoming data to the
|
||||
instance variable value.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) OIDFieldMappingConversionFunction conversion;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use initWithName:type:conversion:.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief The designated initializer.
|
||||
@param name The name of the instance variable the field should be mapped to.
|
||||
@param type The type of the instance variable.
|
||||
@param conversion An optional conversion function which specifies a transform from the incoming
|
||||
data to the instance variable value. Used during the process performed by
|
||||
@c OIDFieldMapping.remainingParametersWithMap:parameters:instance: but not during
|
||||
encoding/decoding, since the encoded and decoded values should already be of the type
|
||||
specified by the @c type parameter.
|
||||
*/
|
||||
- (instancetype)initWithName:(NSString *)name
|
||||
type:(Class)type
|
||||
conversion:(nullable OIDFieldMappingConversionFunction)conversion
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/*! @brief A convenience initializer.
|
||||
@param name The name of the instance variable the field should be mapped to.
|
||||
@param type The type of the instance variable.
|
||||
*/
|
||||
- (instancetype)initWithName:(NSString *)name
|
||||
type:(Class)type;
|
||||
|
||||
/*! @brief Performs a mapping of key/value pairs in an incoming parameters dictionary to instance
|
||||
variables, returning a dictionary of parameter key/values which didn't map to instance
|
||||
variables.
|
||||
@param map A mapping of incoming keys to instance variables.
|
||||
@param parameters Incoming key value pairs to map to an instance's variables.
|
||||
@param instance The instance whose variables should be set based on the mapping.
|
||||
@return A dictionary of parameter key/values which didn't map to instance variables.
|
||||
*/
|
||||
+ (NSDictionary<NSString *, NSObject<NSCopying> *> *)remainingParametersWithMap:
|
||||
(NSDictionary<NSString *, OIDFieldMapping *> *)map
|
||||
parameters:(NSDictionary<NSString *, NSObject<NSCopying> *> *)parameters
|
||||
instance:(id)instance;
|
||||
|
||||
/*! @brief This helper method for @c NSCoding implementations performs a serialization of fields
|
||||
defined in a field mapping.
|
||||
@param aCoder An @c NSCoder instance to serialize instance variable values to.
|
||||
@param map A mapping of keys to instance variables.
|
||||
@param instance The instance whose variables should be serialized based on the mapping.
|
||||
*/
|
||||
+ (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
map:(NSDictionary<NSString *, OIDFieldMapping *> *)map
|
||||
instance:(id)instance;
|
||||
|
||||
/*! @brief This helper method for @c NSCoding implementations performs a deserialization of
|
||||
fields defined in a field mapping.
|
||||
@param aCoder An @c NSCoder instance from which to deserialize instance variable values from.
|
||||
@param map A mapping of keys to instance variables.
|
||||
@param instance The instance whose variables should be deserialized based on the mapping.
|
||||
*/
|
||||
+ (void)decodeWithCoder:(NSCoder *)aCoder
|
||||
map:(NSDictionary<NSString *, OIDFieldMapping *> *)map
|
||||
instance:(id)instance;
|
||||
|
||||
/*! @brief Returns an @c NSSet of classes suitable for deserializing JSON content in an
|
||||
@c NSSecureCoding context.
|
||||
*/
|
||||
+ (NSSet *)JSONTypes;
|
||||
|
||||
/*! @brief Returns a function for converting an @c NSString to an @c NSURL.
|
||||
*/
|
||||
+ (OIDFieldMappingConversionFunction)URLConversion;
|
||||
|
||||
/*! @brief Returns a function for converting an @c NSNumber number of seconds from now to an
|
||||
@c NSDate.
|
||||
*/
|
||||
+ (OIDFieldMappingConversionFunction)dateSinceNowConversion;
|
||||
|
||||
/*! @brief Returns a function for converting an @c NSNumber representing a unix time stamp to an
|
||||
@c NSDate.
|
||||
*/
|
||||
+ (OIDFieldMappingConversionFunction)dateEpochConversion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*! @file OIDFieldMapping.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDFieldMapping.h"
|
||||
|
||||
#import "OIDDefines.h"
|
||||
|
||||
@implementation OIDFieldMapping
|
||||
|
||||
- (nonnull instancetype)init
|
||||
OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithName:type:conversion:))
|
||||
|
||||
- (instancetype)initWithName:(NSString *)name
|
||||
type:(Class)type {
|
||||
return [self initWithName:name type:type conversion:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithName:(NSString *)name
|
||||
type:(Class)type
|
||||
conversion:(nullable OIDFieldMappingConversionFunction)conversion {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_name = [name copy];
|
||||
_expectedType = type;
|
||||
_conversion = conversion;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, NSObject<NSCopying> *> *)remainingParametersWithMap:
|
||||
(NSDictionary<NSString *, OIDFieldMapping *> *)map
|
||||
parameters:(NSDictionary<NSString *, NSObject<NSCopying> *> *)parameters
|
||||
instance:(id)instance {
|
||||
NSMutableDictionary *additionalParameters = [NSMutableDictionary dictionary];
|
||||
for (NSString *key in parameters) {
|
||||
NSObject<NSCopying> *value = [parameters[key] copy];
|
||||
OIDFieldMapping *mapping = map[key];
|
||||
// If the field doesn't appear in the mapping, we add it to the additional parameters
|
||||
// dictionary.
|
||||
if (!mapping) {
|
||||
additionalParameters[key] = value;
|
||||
continue;
|
||||
}
|
||||
// If the field mapping specifies a conversion function, apply the conversion to the value.
|
||||
if (mapping.conversion) {
|
||||
value = mapping.conversion(value);
|
||||
}
|
||||
// Check the type of the value and make sure it matches the type we expected. If it doesn't we
|
||||
// add the value to the additional parameters dictionary but don't assign the instance variable.
|
||||
if (![value isKindOfClass:mapping.expectedType]) {
|
||||
additionalParameters[key] = value;
|
||||
continue;
|
||||
}
|
||||
// Assign the instance variable.
|
||||
[instance setValue:value forKey:mapping.name];
|
||||
}
|
||||
return additionalParameters;
|
||||
}
|
||||
|
||||
+ (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
map:(NSDictionary<NSString *, OIDFieldMapping *> *)map
|
||||
instance:(id)instance {
|
||||
for (NSString *key in map) {
|
||||
id value = [instance valueForKey:map[key].name];
|
||||
[aCoder encodeObject:value forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)decodeWithCoder:(NSCoder *)aCoder
|
||||
map:(NSDictionary<NSString *, OIDFieldMapping *> *)map
|
||||
instance:(id)instance {
|
||||
for (NSString *key in map) {
|
||||
OIDFieldMapping *mapping = map[key];
|
||||
id value = [aCoder decodeObjectOfClass:mapping.expectedType forKey:key];
|
||||
[instance setValue:value forKey:mapping.name];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSSet *)JSONTypes {
|
||||
return [NSSet setWithArray:@[
|
||||
[NSDictionary class],
|
||||
[NSArray class],
|
||||
[NSString class],
|
||||
[NSNumber class]
|
||||
]];
|
||||
}
|
||||
|
||||
+ (OIDFieldMappingConversionFunction)URLConversion {
|
||||
return ^id _Nullable(NSObject *_Nullable value) {
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
return [NSURL URLWithString:(NSString *)value];
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
|
||||
+ (OIDFieldMappingConversionFunction)dateSinceNowConversion {
|
||||
return ^id _Nullable(NSObject *_Nullable value) {
|
||||
if (![value isKindOfClass:[NSNumber class]]) {
|
||||
return value;
|
||||
}
|
||||
NSNumber *valueAsNumber = (NSNumber *)value;
|
||||
return [NSDate dateWithTimeIntervalSinceNow:[valueAsNumber longLongValue]];
|
||||
};
|
||||
}
|
||||
|
||||
+ (OIDFieldMappingConversionFunction)dateEpochConversion {
|
||||
return ^id _Nullable(NSObject *_Nullable value) {
|
||||
if (![value isKindOfClass:[NSNumber class]]) {
|
||||
return value;
|
||||
}
|
||||
NSNumber *valueAsNumber = (NSNumber *) value;
|
||||
return [NSDate dateWithTimeIntervalSince1970:[valueAsNumber longLongValue]];
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*! @file OIDGrantTypes.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
/*! @brief For exchanging an authorization code for an access token.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||
*/
|
||||
extern NSString *const OIDGrantTypeAuthorizationCode;
|
||||
|
||||
/*! @brief For refreshing an access token with a refresh token.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-6
|
||||
*/
|
||||
extern NSString *const OIDGrantTypeRefreshToken;
|
||||
|
||||
/*! @brief For obtaining an access token with a username and password.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.3.2
|
||||
*/
|
||||
extern NSString *const OIDGrantTypePassword;
|
||||
|
||||
/*! @brief For obtaining an access token from the token endpoint using client credentials.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.2.1
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.4.2
|
||||
*/
|
||||
extern NSString *const OIDGrantTypeClientCredentials;
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*! @file OIDGrantTypes.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDGrantTypes.h"
|
||||
|
||||
NSString *const OIDGrantTypeAuthorizationCode = @"authorization_code";
|
||||
|
||||
NSString *const OIDGrantTypeRefreshToken = @"refresh_token";
|
||||
|
||||
NSString *const OIDGrantTypePassword = @"password";
|
||||
|
||||
NSString *const OIDGrantTypeClientCredentials = @"client_credentials";
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*! @file OIDIDToken.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2017 Google Inc. 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief A convenience class that parses an ID Token and extracts the claims _but does not_
|
||||
verify its signature. AppAuth only supports the OpenID Code flow, meaning ID Tokens
|
||||
received by AppAuth are sent from the token endpoint on a TLS protected channel,
|
||||
offering some assurances as to the origin of the token. You may wish to additionally
|
||||
verify the ID Token signature using a JWT signature verification library of your
|
||||
choosing.
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
@see https://tools.ietf.org/html/rfc7519
|
||||
@see https://jwt.io/
|
||||
*/
|
||||
@interface OIDIDToken : NSObject
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use @c initWithAuthorizationResponse:.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Parses the given ID Token string.
|
||||
@param idToken The ID Token spring.
|
||||
*/
|
||||
- (nullable instancetype)initWithIDTokenString:(NSString *)idToken;
|
||||
|
||||
/*! @brief The header JWT values.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSDictionary *header;
|
||||
|
||||
/*! @brief All ID Token claims.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSDictionary *claims;
|
||||
|
||||
/*! @brief Issuer Identifier for the Issuer of the response.
|
||||
@remarks iss
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL *issuer;
|
||||
|
||||
/*! @brief Subject Identifier.
|
||||
@remarks sub
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *subject;
|
||||
|
||||
/*! @brief Audience(s) that this ID Token is intended for.
|
||||
@remarks aud
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray *audience;
|
||||
|
||||
/*! @brief Expiration time on or after which the ID Token MUST NOT be accepted for processing.
|
||||
@remarks exp
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
*/
|
||||
@property(nonatomic, readonly) NSDate *expiresAt;
|
||||
|
||||
/*! @brief Time at which the JWT was issued.
|
||||
@remarks iat
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
*/
|
||||
@property(nonatomic, readonly) NSDate *issuedAt;
|
||||
|
||||
/*! @brief String value used to associate a Client session with an ID Token, and to mitigate replay
|
||||
attacks.
|
||||
@remarks nonce
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *nonce;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/*! @file OIDIDToken.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2017 Google Inc. 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 "OIDIDToken.h"
|
||||
|
||||
/*! Field keys associated with an ID Token. */
|
||||
static NSString *const kIssKey = @"iss";
|
||||
static NSString *const kSubKey = @"sub";
|
||||
static NSString *const kAudKey = @"aud";
|
||||
static NSString *const kExpKey = @"exp";
|
||||
static NSString *const kIatKey = @"iat";
|
||||
static NSString *const kNonceKey = @"nonce";
|
||||
|
||||
#import "OIDFieldMapping.h"
|
||||
|
||||
@implementation OIDIDToken
|
||||
|
||||
- (instancetype)initWithIDTokenString:(NSString *)idToken {
|
||||
self = [super init];
|
||||
NSArray *sections = [idToken componentsSeparatedByString:@"."];
|
||||
|
||||
// The header and claims sections are required.
|
||||
if (sections.count <= 1) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_header = [[self class] parseJWTSection:sections[0]];
|
||||
_claims = [[self class] parseJWTSection:sections[1]];
|
||||
if (!_header || !_claims) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
[OIDFieldMapping remainingParametersWithMap:[[self class] fieldMap]
|
||||
parameters:_claims
|
||||
instance:self];
|
||||
|
||||
// Required fields.
|
||||
if (!_issuer || !_audience || !_subject || !_expiresAt || !_issuedAt) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*! @brief Returns a mapping of incoming parameters to instance variables.
|
||||
@return A mapping of incoming parameters to instance variables.
|
||||
*/
|
||||
+ (NSDictionary<NSString *, OIDFieldMapping *> *)fieldMap {
|
||||
static NSMutableDictionary<NSString *, OIDFieldMapping *> *fieldMap;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
fieldMap = [NSMutableDictionary dictionary];
|
||||
|
||||
fieldMap[kIssKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_issuer"
|
||||
type:[NSURL class]
|
||||
conversion:[OIDFieldMapping URLConversion]];
|
||||
fieldMap[kSubKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_subject" type:[NSString class]];
|
||||
fieldMap[kAudKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_audience"
|
||||
type:[NSArray class]
|
||||
conversion:^id _Nullable(NSObject *_Nullable value) {
|
||||
if ([value isKindOfClass:[NSArray class]]) {
|
||||
return value;
|
||||
}
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
return @[value];
|
||||
}
|
||||
return nil;
|
||||
}];
|
||||
fieldMap[kExpKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_expiresAt"
|
||||
type:[NSDate class]
|
||||
conversion:^id _Nullable(NSObject *_Nullable value) {
|
||||
if (![value isKindOfClass:[NSNumber class]]) {
|
||||
return value;
|
||||
}
|
||||
NSNumber *valueAsNumber = (NSNumber *)value;
|
||||
return [NSDate dateWithTimeIntervalSince1970:valueAsNumber.longLongValue];
|
||||
}];
|
||||
fieldMap[kIatKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_issuedAt"
|
||||
type:[NSDate class]
|
||||
conversion:^id _Nullable(NSObject *_Nullable value) {
|
||||
if (![value isKindOfClass:[NSNumber class]]) {
|
||||
return value;
|
||||
}
|
||||
NSNumber *valueAsNumber = (NSNumber *)value;
|
||||
return [NSDate dateWithTimeIntervalSince1970:valueAsNumber.longLongValue];
|
||||
}];
|
||||
fieldMap[kNonceKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_nonce" type:[NSString class]];
|
||||
});
|
||||
return fieldMap;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)parseJWTSection:(NSString *)sectionString {
|
||||
NSData *decodedData = [[self class] base64urlNoPaddingDecode:sectionString];
|
||||
|
||||
// Parses JSON.
|
||||
NSError *error;
|
||||
id object = [NSJSONSerialization JSONObjectWithData:decodedData options:0 error:&error];
|
||||
if (error) {
|
||||
NSLog(@"Error %@ parsing token payload %@", error, sectionString);
|
||||
}
|
||||
if ([object isKindOfClass:[NSDictionary class]]) {
|
||||
return (NSDictionary *)object;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSData *)base64urlNoPaddingDecode:(NSString *)base64urlNoPaddingString {
|
||||
NSMutableString *body = [base64urlNoPaddingString mutableCopy];
|
||||
|
||||
// Converts base64url to base64.
|
||||
NSRange range = NSMakeRange(0, base64urlNoPaddingString.length);
|
||||
[body replaceOccurrencesOfString:@"-" withString:@"+" options:NSLiteralSearch range:range];
|
||||
[body replaceOccurrencesOfString:@"_" withString:@"/" options:NSLiteralSearch range:range];
|
||||
|
||||
// Converts base64 no padding to base64 with padding
|
||||
while (body.length % 4 != 0) {
|
||||
[body appendString:@"="];
|
||||
}
|
||||
|
||||
// Decodes base64 string.
|
||||
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:body options:0];
|
||||
return decodedData;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/*! @file OIDRegistrationRequest.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2016 The AppAuth for iOS 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 OIDAuthorizationResponse;
|
||||
@class OIDServiceConfiguration;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Represents a registration request.
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest
|
||||
*/
|
||||
@interface OIDRegistrationRequest : 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 The initial access token to access the Client Registration Endpoint
|
||||
(if required by the OpenID Provider).
|
||||
@remarks OAuth 2.0 Access Token optionally issued by an Authorization Server granting
|
||||
access to its Client Registration Endpoint. This token (if required) is
|
||||
provisioned out of band.
|
||||
@see Section 3 of OpenID Connect Dynamic Client Registration 1.0
|
||||
https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *initialAccessToken;
|
||||
|
||||
/*! @brief The application type to register, will always be 'native'.
|
||||
@remarks application_type
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *applicationType;
|
||||
|
||||
/*! @brief The client's redirect URI's.
|
||||
@remarks redirect_uris
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.1.2
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray<NSURL *> *redirectURIs;
|
||||
|
||||
/*! @brief The response types to register for usage by this client.
|
||||
@remarks response_types
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#Authentication
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *responseTypes;
|
||||
|
||||
/*! @brief The grant types to register for usage by this client.
|
||||
@remarks grant_types
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *grantTypes;
|
||||
|
||||
/*! @brief The subject type to to request.
|
||||
@remarks subject_type
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *subjectType;
|
||||
|
||||
/*! @brief The client authentication method to use at the token endpoint.
|
||||
@remarks token_endpoint_auth_method
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *tokenEndpointAuthenticationMethod;
|
||||
|
||||
/*! @brief The client's additional token request parameters.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDictionary<NSString *, NSString *> *additionalParameters;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use initWithConfiguration
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Create a Client Registration Request to an OpenID Provider that supports open Dynamic
|
||||
Registration.
|
||||
@param configuration The service's configuration.
|
||||
@param redirectURIs The redirect URIs to register for the client.
|
||||
@param responseTypes The response types to register for the client.
|
||||
@param grantTypes The grant types to register for the client.
|
||||
@param subjectType The subject type to register for the client.
|
||||
@param tokenEndpointAuthMethod The token endpoint authentication method to register for the
|
||||
client.
|
||||
@param additionalParameters The client's additional registration request parameters.
|
||||
*/
|
||||
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
redirectURIs:(NSArray<NSURL *> *)redirectURIs
|
||||
responseTypes:(nullable NSArray<NSString *> *)responseTypes
|
||||
grantTypes:(nullable NSArray<NSString *> *)grantTypes
|
||||
subjectType:(nullable NSString *)subjectType
|
||||
tokenEndpointAuthMethod:(nullable NSString *)tokenEndpointAuthMethod
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters;
|
||||
|
||||
/*! @brief Designated initializer.
|
||||
@param configuration The service's configuration.
|
||||
@param redirectURIs The redirect URIs to register for the client.
|
||||
@param responseTypes The response types to register for the client.
|
||||
@param grantTypes The grant types to register for the client.
|
||||
@param subjectType The subject type to register for the client.
|
||||
@param tokenEndpointAuthMethod The token endpoint authentication method to register for the
|
||||
client.
|
||||
@param initialAccessToken The initial access token to access the Client Registration Endpoint
|
||||
(if required by the OpenID Provider).
|
||||
@param additionalParameters The client's additional registration request parameters.
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration
|
||||
*/
|
||||
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
redirectURIs:(NSArray<NSURL *> *)redirectURIs
|
||||
responseTypes:(nullable NSArray<NSString *> *)responseTypes
|
||||
grantTypes:(nullable NSArray<NSString *> *)grantTypes
|
||||
subjectType:(nullable NSString *)subjectType
|
||||
tokenEndpointAuthMethod:(nullable NSString *)tokenEndpointAuthMethod
|
||||
initialAccessToken:(nullable NSString *)initialAccessToken
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/*! @brief Constructs an @c NSURLRequest representing the registration request.
|
||||
@return An @c NSURLRequest representing the registration request.
|
||||
*/
|
||||
- (NSURLRequest *)URLRequest;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,248 @@
|
||||
/*! @file OIDRegistrationRequest.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2016 The AppAuth for iOS 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 "OIDRegistrationRequest.h"
|
||||
|
||||
#import "OIDClientMetadataParameters.h"
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDServiceConfiguration.h"
|
||||
|
||||
/*! @brief The key for the @c configuration property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kConfigurationKey = @"configuration";
|
||||
|
||||
/*! @brief The key for the @c initialAccessToken property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kInitialAccessToken = @"initial_access_token";
|
||||
|
||||
/*! @brief Key used to encode the @c redirectURIs property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kRedirectURIsKey = @"redirect_uris";
|
||||
|
||||
/*! @brief The key for the @c responseTypes property for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kResponseTypesKey = @"response_types";
|
||||
|
||||
/*! @brief Key used to encode the @c grantType property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kGrantTypesKey = @"grant_types";
|
||||
|
||||
/*! @brief Key used to encode the @c subjectType property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kSubjectTypeKey = @"subject_type";
|
||||
|
||||
/*! @brief Key used to encode the @c additionalParameters property for
|
||||
@c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kAdditionalParametersKey = @"additionalParameters";
|
||||
|
||||
@implementation OIDRegistrationRequest
|
||||
|
||||
#pragma mark - Initializers
|
||||
|
||||
- (instancetype)init
|
||||
OID_UNAVAILABLE_USE_INITIALIZER(
|
||||
@selector(initWithConfiguration:
|
||||
redirectURIs:
|
||||
responseTypes:
|
||||
grantTypes:
|
||||
subjectType:
|
||||
tokenEndpointAuthMethod:
|
||||
additionalParameters:)
|
||||
)
|
||||
|
||||
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
redirectURIs:(NSArray<NSURL *> *)redirectURIs
|
||||
responseTypes:(nullable NSArray<NSString *> *)responseTypes
|
||||
grantTypes:(nullable NSArray<NSString *> *)grantTypes
|
||||
subjectType:(nullable NSString *)subjectType
|
||||
tokenEndpointAuthMethod:(nullable NSString *)tokenEndpointAuthenticationMethod
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters {
|
||||
return [self initWithConfiguration:configuration
|
||||
redirectURIs:redirectURIs
|
||||
responseTypes:responseTypes
|
||||
grantTypes:grantTypes
|
||||
subjectType:subjectType
|
||||
tokenEndpointAuthMethod:tokenEndpointAuthenticationMethod
|
||||
initialAccessToken:nil
|
||||
additionalParameters:additionalParameters];
|
||||
}
|
||||
|
||||
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
redirectURIs:(NSArray<NSURL *> *)redirectURIs
|
||||
responseTypes:(nullable NSArray<NSString *> *)responseTypes
|
||||
grantTypes:(nullable NSArray<NSString *> *)grantTypes
|
||||
subjectType:(nullable NSString *)subjectType
|
||||
tokenEndpointAuthMethod:(nullable NSString *)tokenEndpointAuthenticationMethod
|
||||
initialAccessToken:(nullable NSString *)initialAccessToken
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_configuration = [configuration copy];
|
||||
_initialAccessToken = [initialAccessToken copy];
|
||||
_redirectURIs = [redirectURIs copy];
|
||||
_responseTypes = [responseTypes copy];
|
||||
_grantTypes = [grantTypes copy];
|
||||
_subjectType = [subjectType copy];
|
||||
_tokenEndpointAuthenticationMethod = [tokenEndpointAuthenticationMethod copy];
|
||||
_additionalParameters =
|
||||
[[NSDictionary alloc] initWithDictionary:additionalParameters copyItems:YES];
|
||||
|
||||
_applicationType = OIDApplicationTypeNative;
|
||||
}
|
||||
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 *initialAccessToken = [aDecoder decodeObjectOfClass:[NSString class]
|
||||
forKey:kInitialAccessToken];
|
||||
NSArray<NSURL *> *redirectURIs = [aDecoder decodeObjectOfClass:[NSArray<NSURL *> class]
|
||||
forKey:kRedirectURIsKey];
|
||||
NSArray<NSString *> *responseTypes = [aDecoder decodeObjectOfClass:[NSArray<NSString *> class]
|
||||
forKey:kResponseTypesKey];
|
||||
NSArray<NSString *> *grantTypes = [aDecoder decodeObjectOfClass:[NSArray<NSString *> class]
|
||||
forKey:kGrantTypesKey];
|
||||
NSString *subjectType = [aDecoder decodeObjectOfClass:[NSString class]
|
||||
forKey:kSubjectTypeKey];
|
||||
NSString *tokenEndpointAuthenticationMethod =
|
||||
[aDecoder decodeObjectOfClass:[NSString class]
|
||||
forKey:OIDTokenEndpointAuthenticationMethodParam];
|
||||
NSSet *additionalParameterCodingClasses = [NSSet setWithArray:@[ [NSDictionary class],
|
||||
[NSString class] ]];
|
||||
NSDictionary *additionalParameters =
|
||||
[aDecoder decodeObjectOfClasses:additionalParameterCodingClasses
|
||||
forKey:kAdditionalParametersKey];
|
||||
self = [self initWithConfiguration:configuration
|
||||
redirectURIs:redirectURIs
|
||||
responseTypes:responseTypes
|
||||
grantTypes:grantTypes
|
||||
subjectType:subjectType
|
||||
tokenEndpointAuthMethod:tokenEndpointAuthenticationMethod
|
||||
initialAccessToken:initialAccessToken
|
||||
additionalParameters:additionalParameters];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:_configuration forKey:kConfigurationKey];
|
||||
[aCoder encodeObject:_initialAccessToken forKey:kInitialAccessToken];
|
||||
[aCoder encodeObject:_redirectURIs forKey:kRedirectURIsKey];
|
||||
[aCoder encodeObject:_responseTypes forKey:kResponseTypesKey];
|
||||
[aCoder encodeObject:_grantTypes forKey:kGrantTypesKey];
|
||||
[aCoder encodeObject:_subjectType forKey:kSubjectTypeKey];
|
||||
[aCoder encodeObject:_tokenEndpointAuthenticationMethod
|
||||
forKey:OIDTokenEndpointAuthenticationMethodParam];
|
||||
[aCoder encodeObject:_additionalParameters forKey:kAdditionalParametersKey];
|
||||
}
|
||||
|
||||
#pragma mark - NSObject overrides
|
||||
|
||||
- (NSString *)description {
|
||||
NSURLRequest *request = [self URLRequest];
|
||||
NSString *requestBody = [[NSString alloc] initWithData:request.HTTPBody
|
||||
encoding:NSUTF8StringEncoding];
|
||||
return [NSString stringWithFormat:@"<%@: %p, request: <URL: %@, HTTPBody: %@>>",
|
||||
NSStringFromClass([self class]),
|
||||
(void *)self,
|
||||
request.URL,
|
||||
requestBody];
|
||||
}
|
||||
|
||||
- (NSURLRequest *)URLRequest {
|
||||
static NSString *const kHTTPPost = @"POST";
|
||||
static NSString *const kBearer = @"Bearer";
|
||||
static NSString *const kHTTPContentTypeHeaderKey = @"Content-Type";
|
||||
static NSString *const kHTTPContentTypeHeaderValue = @"application/json";
|
||||
static NSString *const kHTTPAuthorizationHeaderKey = @"Authorization";
|
||||
|
||||
NSData *postBody = [self JSONString];
|
||||
if (!postBody) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSURL *registrationRequestURL = _configuration.registrationEndpoint;
|
||||
NSMutableURLRequest *URLRequest =
|
||||
[[NSURLRequest requestWithURL:registrationRequestURL] mutableCopy];
|
||||
URLRequest.HTTPMethod = kHTTPPost;
|
||||
[URLRequest setValue:kHTTPContentTypeHeaderValue forHTTPHeaderField:kHTTPContentTypeHeaderKey];
|
||||
if (_initialAccessToken) {
|
||||
NSString *value = [NSString stringWithFormat:@"%@ %@", kBearer, _initialAccessToken];
|
||||
[URLRequest setValue:value forHTTPHeaderField:kHTTPAuthorizationHeaderKey];
|
||||
}
|
||||
URLRequest.HTTPBody = postBody;
|
||||
return URLRequest;
|
||||
}
|
||||
|
||||
- (NSData *)JSONString {
|
||||
// Dictionary with several kay/value pairs and the above array of arrays
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
NSMutableArray<NSString *> *redirectURIStrings =
|
||||
[NSMutableArray arrayWithCapacity:[_redirectURIs count]];
|
||||
for (id obj in _redirectURIs) {
|
||||
[redirectURIStrings addObject:[obj absoluteString]];
|
||||
}
|
||||
dict[OIDRedirectURIsParam] = redirectURIStrings;
|
||||
dict[OIDApplicationTypeParam] = _applicationType;
|
||||
|
||||
if (_additionalParameters) {
|
||||
// Add any additional parameters first to allow them
|
||||
// to be overwritten by instance values
|
||||
[dict addEntriesFromDictionary:_additionalParameters];
|
||||
}
|
||||
if (_responseTypes) {
|
||||
dict[OIDResponseTypesParam] = _responseTypes;
|
||||
}
|
||||
if (_grantTypes) {
|
||||
dict[OIDGrantTypesParam] = _grantTypes;
|
||||
}
|
||||
if (_subjectType) {
|
||||
dict[OIDSubjectTypeParam] = _subjectType;
|
||||
}
|
||||
if (_tokenEndpointAuthenticationMethod) {
|
||||
dict[OIDTokenEndpointAuthenticationMethodParam] = _tokenEndpointAuthenticationMethod;
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
NSData *json = [NSJSONSerialization dataWithJSONObject:dict options:kNilOptions error:&error];
|
||||
if (json == nil || error != nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,126 @@
|
||||
/*! @file OIDRegistrationResponse.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2016 The AppAuth for iOS 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 OIDRegistrationRequest;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Parameter name for the client id.
|
||||
*/
|
||||
extern NSString *const OIDClientIDParam;
|
||||
|
||||
/*! @brief Parameter name for the client id issuance timestamp.
|
||||
*/
|
||||
extern NSString *const OIDClientIDIssuedAtParam;
|
||||
|
||||
/*! @brief Parameter name for the client secret.
|
||||
*/
|
||||
extern NSString *const OIDClientSecretParam;
|
||||
|
||||
/*! @brief Parameter name for the client secret expiration time.
|
||||
*/
|
||||
extern NSString *const OIDClientSecretExpirestAtParam;
|
||||
|
||||
/*! @brief Parameter name for the registration access token.
|
||||
*/
|
||||
extern NSString *const OIDRegistrationAccessTokenParam;
|
||||
|
||||
/*! @brief Parameter name for the client configuration URI.
|
||||
*/
|
||||
extern NSString *const OIDRegistrationClientURIParam;
|
||||
|
||||
/*! @brief Represents a registration response.
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse
|
||||
*/
|
||||
@interface OIDRegistrationResponse : NSObject <NSCopying, NSSecureCoding>
|
||||
|
||||
/*! @brief The request which was serviced.
|
||||
*/
|
||||
@property(nonatomic, readonly) OIDRegistrationRequest *request;
|
||||
|
||||
/*! @brief The registered client identifier.
|
||||
@remarks client_id
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.1
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *clientID;
|
||||
|
||||
/*! @brief Timestamp of when the client identifier was issued, if provided.
|
||||
@remarks client_id_issued_at
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDate *clientIDIssuedAt;
|
||||
|
||||
/*! @brief TThe client secret, which is part of the client credentials, if provided.
|
||||
@remarks client_secret
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *clientSecret;
|
||||
|
||||
/*! @brief Timestamp of when the client credentials expires, if provided.
|
||||
@remarks client_secret_expires_at
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDate *clientSecretExpiresAt;
|
||||
|
||||
/*! @brief Client registration access token that can be used for subsequent operations upon the
|
||||
client registration.
|
||||
@remarks registration_access_token
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *registrationAccessToken;
|
||||
|
||||
/*! @brief Location of the client configuration endpoint, if provided.
|
||||
@remarks registration_client_uri
|
||||
@see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *registrationClientURI;
|
||||
|
||||
/*! @brief Client authentication method to use at the token endpoint, if provided.
|
||||
@remarks token_endpoint_auth_method
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *tokenEndpointAuthenticationMethod;
|
||||
|
||||
/*! @brief Additional parameters returned from the token server.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDictionary<NSString *, NSObject <NSCopying> *>
|
||||
*additionalParameters;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use initWithRequest
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Designated initializer.
|
||||
@param request The serviced request.
|
||||
@param parameters The decoded parameters returned from the Authorization Server.
|
||||
@remarks Known parameters are extracted from the @c parameters parameter and the normative
|
||||
properties are populated. Non-normative parameters are placed in the
|
||||
@c #additionalParameters dictionary.
|
||||
*/
|
||||
- (instancetype)initWithRequest:(OIDRegistrationRequest *)request
|
||||
parameters:(NSDictionary<NSString *, NSObject <NSCopying> *> *)parameters
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,164 @@
|
||||
/*! @file OIDRegistrationResponse.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2016 The AppAuth for iOS 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 "OIDRegistrationResponse.h"
|
||||
|
||||
#import "OIDClientMetadataParameters.h"
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDFieldMapping.h"
|
||||
#import "OIDRegistrationRequest.h"
|
||||
#import "OIDTokenUtilities.h"
|
||||
|
||||
NSString *const OIDClientIDParam = @"client_id";
|
||||
NSString *const OIDClientIDIssuedAtParam = @"client_id_issued_at";
|
||||
NSString *const OIDClientSecretParam = @"client_secret";
|
||||
NSString *const OIDClientSecretExpirestAtParam = @"client_secret_expires_at";
|
||||
NSString *const OIDRegistrationAccessTokenParam = @"registration_access_token";
|
||||
NSString *const OIDRegistrationClientURIParam = @"registration_client_uri";
|
||||
|
||||
/*! @brief Key used to encode the @c request property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kRequestKey = @"request";
|
||||
|
||||
/*! @brief Key used to encode the @c additionalParameters property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kAdditionalParametersKey = @"additionalParameters";
|
||||
|
||||
@implementation OIDRegistrationResponse
|
||||
|
||||
/*! @brief Returns a mapping of incoming parameters to instance variables.
|
||||
@return A mapping of incoming parameters to instance variables.
|
||||
*/
|
||||
+ (NSDictionary<NSString *, OIDFieldMapping *> *)fieldMap {
|
||||
static NSMutableDictionary<NSString *, OIDFieldMapping *> *fieldMap;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
fieldMap = [NSMutableDictionary dictionary];
|
||||
fieldMap[OIDClientIDParam] = [[OIDFieldMapping alloc] initWithName:@"_clientID"
|
||||
type:[NSString class]];
|
||||
fieldMap[OIDClientIDIssuedAtParam] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_clientIDIssuedAt"
|
||||
type:[NSDate class]
|
||||
conversion:[OIDFieldMapping dateEpochConversion]];
|
||||
fieldMap[OIDClientSecretParam] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_clientSecret"
|
||||
type:[NSString class]];
|
||||
fieldMap[OIDClientSecretExpirestAtParam] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_clientSecretExpiresAt"
|
||||
type:[NSDate class]
|
||||
conversion:[OIDFieldMapping dateEpochConversion]];
|
||||
fieldMap[OIDRegistrationAccessTokenParam] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_registrationAccessToken"
|
||||
type:[NSString class]];
|
||||
fieldMap[OIDRegistrationClientURIParam] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_registrationClientURI"
|
||||
type:[NSURL class]
|
||||
conversion:[OIDFieldMapping URLConversion]];
|
||||
fieldMap[OIDTokenEndpointAuthenticationMethodParam] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_tokenEndpointAuthenticationMethod"
|
||||
type:[NSString class]];
|
||||
});
|
||||
return fieldMap;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Initializers
|
||||
|
||||
- (nonnull instancetype)init
|
||||
OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:))
|
||||
|
||||
- (instancetype)initWithRequest:(OIDRegistrationRequest *)request
|
||||
parameters:(NSDictionary<NSString *, NSObject <NSCopying> *> *)parameters {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_request = [request copy];
|
||||
NSDictionary<NSString *, NSObject <NSCopying> *> *additionalParameters =
|
||||
[OIDFieldMapping remainingParametersWithMap:[[self class] fieldMap]
|
||||
parameters:parameters
|
||||
instance:self];
|
||||
_additionalParameters = additionalParameters;
|
||||
|
||||
if ((_clientSecret && !_clientSecretExpiresAt)
|
||||
|| (!!_registrationClientURI != !!_registrationAccessToken)) {
|
||||
// If client_secret is issued, client_secret_expires_at is REQUIRED,
|
||||
// and the response MUST contain "[...] both a Client Configuration Endpoint
|
||||
// and a Registration Access Token or neither of them"
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
OIDRegistrationRequest *request = [aDecoder decodeObjectOfClass:[OIDRegistrationRequest class]
|
||||
forKey:kRequestKey];
|
||||
self = [self initWithRequest:request
|
||||
parameters:@{}];
|
||||
if (self) {
|
||||
[OIDFieldMapping decodeWithCoder:aDecoder
|
||||
map:[[self class] fieldMap]
|
||||
instance:self];
|
||||
_additionalParameters = [aDecoder decodeObjectOfClasses:[OIDFieldMapping JSONTypes]
|
||||
forKey:kAdditionalParametersKey];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[OIDFieldMapping encodeWithCoder:aCoder map:[[self class] fieldMap] instance:self];
|
||||
[aCoder encodeObject:_request forKey:kRequestKey];
|
||||
[aCoder encodeObject:_additionalParameters forKey:kAdditionalParametersKey];
|
||||
}
|
||||
|
||||
#pragma mark - NSObject overrides
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"<%@: %p, clientID: \"%@\", clientIDIssuedAt: %@, "
|
||||
"clientSecret: %@, clientSecretExpiresAt: \"%@\", "
|
||||
"registrationAccessToken: \"%@\", "
|
||||
"registrationClientURI: \"%@\", "
|
||||
"additionalParameters: %@, request: %@>",
|
||||
NSStringFromClass([self class]),
|
||||
(void *)self,
|
||||
_clientID,
|
||||
_clientIDIssuedAt,
|
||||
[OIDTokenUtilities redact:_clientSecret],
|
||||
_clientSecretExpiresAt,
|
||||
[OIDTokenUtilities redact:_registrationAccessToken],
|
||||
_registrationClientURI,
|
||||
_additionalParameters,
|
||||
_request];
|
||||
}
|
||||
|
||||
@end
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*! @file OIDResponseTypes.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
/*! @brief A constant for the standard OAuth2 Response Type of 'code'.
|
||||
*/
|
||||
extern NSString *const OIDResponseTypeCode;
|
||||
|
||||
/*! @brief A constant for the standard OAuth2 Response Type of 'token'.
|
||||
*/
|
||||
extern NSString *const OIDResponseTypeToken;
|
||||
|
||||
/*! @brief A constant for the standard OAuth2 Response Type of 'id_token'.
|
||||
*/
|
||||
extern NSString *const OIDResponseTypeIDToken;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*! @file OIDResponseTypes.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDResponseTypes.h"
|
||||
|
||||
NSString *const OIDResponseTypeCode = @"code";
|
||||
|
||||
NSString *const OIDResponseTypeToken = @"token";
|
||||
|
||||
NSString *const OIDResponseTypeIDToken = @"id_token";
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*! @file OIDScopeUtilities.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Provides convenience methods for dealing with scope strings.
|
||||
*/
|
||||
@interface OIDScopeUtilities : NSObject
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. This class should not be initialized.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Converts an array of scope strings to a single scope string per the OAuth 2 spec.
|
||||
@param scopes An array of scope strings.
|
||||
@return A space-delimited string of scopes.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.3
|
||||
*/
|
||||
+ (NSString *)scopesWithArray:(NSArray<NSString *> *)scopes;
|
||||
|
||||
/*! @brief Converts an OAuth 2 spec-compliant scope string to an array of scopes.
|
||||
@param scopes An OAuth 2 spec-compliant scope string.
|
||||
@return An array of scope strings.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.3
|
||||
*/
|
||||
+ (NSArray<NSString *> *)scopesArrayWithString:(NSString *)scopes;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*! @file OIDScopeUtilities.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDScopeUtilities.h"
|
||||
|
||||
@implementation OIDScopeUtilities
|
||||
|
||||
/*! @brief A character set with the characters NOT allowed in a scope name.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.3
|
||||
*/
|
||||
+ (NSCharacterSet *)disallowedScopeCharacters {
|
||||
static NSCharacterSet *disallowedCharacters;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSMutableCharacterSet *allowedCharacters;
|
||||
allowedCharacters =
|
||||
[NSMutableCharacterSet characterSetWithRange:NSMakeRange(0x23, 0x5B - 0x23 + 1)];
|
||||
[allowedCharacters addCharactersInRange:NSMakeRange(0x5D, 0x7E - 0x5D + 1)];
|
||||
[allowedCharacters addCharactersInString:@"\x21"];
|
||||
disallowedCharacters = [allowedCharacters invertedSet];
|
||||
});
|
||||
return disallowedCharacters;
|
||||
}
|
||||
|
||||
+ (NSString *)scopesWithArray:(NSArray<NSString *> *)scopes {
|
||||
#if !defined(NS_BLOCK_ASSERTIONS)
|
||||
NSCharacterSet *disallowedCharacters = [self disallowedScopeCharacters];
|
||||
for (NSString *scope in scopes) {
|
||||
NSAssert(scope.length, @"Found illegal empty scope string.");
|
||||
NSAssert([scope rangeOfCharacterFromSet:disallowedCharacters].location == NSNotFound,
|
||||
@"Found illegal character in scope string.");
|
||||
}
|
||||
#endif // !defined(NS_BLOCK_ASSERTIONS)
|
||||
|
||||
NSString *scopeString = [scopes componentsJoinedByString:@" "];
|
||||
return scopeString;
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)scopesArrayWithString:(NSString *)scopes {
|
||||
return [scopes componentsSeparatedByString:@" "];
|
||||
}
|
||||
|
||||
@end
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*! @file OIDScopes.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
/*! @brief Scope that indicates this request is an OpenID Connect request.
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#AuthRequestValidation
|
||||
*/
|
||||
extern NSString *const OIDScopeOpenID;
|
||||
|
||||
/*! @brief This scope value requests access to the End-User's default profile Claims, which are:
|
||||
name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture,
|
||||
website, gender, birthdate, zoneinfo, locale, and updated_at.
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
|
||||
*/
|
||||
extern NSString *const OIDScopeProfile;
|
||||
|
||||
/*! @brief This scope value requests access to the email and email_verified Claims.
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
|
||||
*/
|
||||
extern NSString *const OIDScopeEmail;
|
||||
|
||||
/*! @brief This scope value requests access to the address Claim.
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
|
||||
*/
|
||||
extern NSString *const OIDScopeAddress;
|
||||
|
||||
/*! @brief This scope value requests access to the phone_number and phone_number_verified Claims.
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
|
||||
*/
|
||||
extern NSString *const OIDScopePhone;
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*! @file OIDScopes.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDScopes.h"
|
||||
|
||||
NSString *const OIDScopeOpenID = @"openid";
|
||||
|
||||
NSString *const OIDScopeProfile = @"profile";
|
||||
|
||||
NSString *const OIDScopeEmail = @"email";
|
||||
|
||||
NSString *const OIDScopeAddress = @"address";
|
||||
|
||||
NSString *const OIDScopePhone = @"phone";
|
||||
@@ -0,0 +1,118 @@
|
||||
/*! @file OIDServiceConfiguration.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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;
|
||||
@class OIDServiceDiscovery;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief The type of block called when a @c OIDServiceConfiguration has been created
|
||||
by loading a @c OIDServiceDiscovery from an @c NSURL.
|
||||
*/
|
||||
typedef void (^OIDServiceConfigurationCreated)
|
||||
(OIDServiceConfiguration *_Nullable serviceConfiguration,
|
||||
NSError *_Nullable error);
|
||||
|
||||
/*! @brief Represents the information needed to construct a @c OIDAuthorizationService.
|
||||
*/
|
||||
@interface OIDServiceConfiguration : NSObject <NSCopying, NSSecureCoding>
|
||||
|
||||
/*! @brief The authorization endpoint URI.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL *authorizationEndpoint;
|
||||
|
||||
/*! @brief The token exchange and refresh endpoint URI.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL *tokenEndpoint;
|
||||
|
||||
/*! @brief The OpenID Connect issuer.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *issuer;
|
||||
|
||||
/*! @brief The dynamic client registration endpoint URI.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *registrationEndpoint;
|
||||
|
||||
/*! @brief The end session logout endpoint URI.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *endSessionEndpoint;
|
||||
|
||||
/*! @brief The discovery document.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) OIDServiceDiscovery *discoveryDocument;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use @c initWithAuthorizationEndpoint:tokenEndpoint: or
|
||||
@c initWithDiscoveryDocument:.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @param authorizationEndpoint The authorization endpoint URI.
|
||||
@param tokenEndpoint The token exchange and refresh endpoint URI.
|
||||
*/
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint;
|
||||
|
||||
/*! @param authorizationEndpoint The authorization endpoint URI.
|
||||
@param tokenEndpoint The token exchange and refresh endpoint URI.
|
||||
@param registrationEndpoint The dynamic client registration endpoint URI.
|
||||
*/
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint
|
||||
registrationEndpoint:(nullable NSURL *)registrationEndpoint;
|
||||
|
||||
/*! @param authorizationEndpoint The authorization endpoint URI.
|
||||
@param tokenEndpoint The token exchange and refresh endpoint URI.
|
||||
@param issuer The OpenID Connect issuer.
|
||||
*/
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint
|
||||
issuer:(nullable NSURL *)issuer;
|
||||
|
||||
/*! @param authorizationEndpoint The authorization endpoint URI.
|
||||
@param tokenEndpoint The token exchange and refresh endpoint URI.
|
||||
@param issuer The OpenID Connect issuer.
|
||||
@param registrationEndpoint The dynamic client registration endpoint URI.
|
||||
*/
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint
|
||||
issuer:(nullable NSURL *)issuer
|
||||
registrationEndpoint:(nullable NSURL *)registrationEndpoint;
|
||||
|
||||
/*! @param authorizationEndpoint The authorization endpoint URI.
|
||||
@param tokenEndpoint The token exchange and refresh endpoint URI.
|
||||
@param issuer The OpenID Connect issuer.
|
||||
@param registrationEndpoint The dynamic client registration endpoint URI.
|
||||
@param endSessionEndpoint The end session endpoint (logout) URI.
|
||||
*/
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint
|
||||
issuer:(nullable NSURL *)issuer
|
||||
registrationEndpoint:(nullable NSURL *)registrationEndpoint
|
||||
endSessionEndpoint:(nullable NSURL *)endSessionEndpoint;
|
||||
|
||||
/*! @param discoveryDocument The discovery document from which to extract the required OAuth
|
||||
configuration.
|
||||
*/
|
||||
- (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *)discoveryDocument;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,223 @@
|
||||
/*! @file OIDServiceConfiguration.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDServiceConfiguration.h"
|
||||
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDErrorUtilities.h"
|
||||
#import "OIDServiceDiscovery.h"
|
||||
|
||||
/*! @brief The key for the @c authorizationEndpoint property.
|
||||
*/
|
||||
static NSString *const kAuthorizationEndpointKey = @"authorizationEndpoint";
|
||||
|
||||
/*! @brief The key for the @c tokenEndpoint property.
|
||||
*/
|
||||
static NSString *const kTokenEndpointKey = @"tokenEndpoint";
|
||||
|
||||
/*! @brief The key for the @c issuer property.
|
||||
*/
|
||||
static NSString *const kIssuerKey = @"issuer";
|
||||
|
||||
/*! @brief The key for the @c registrationEndpoint property.
|
||||
*/
|
||||
static NSString *const kRegistrationEndpointKey = @"registrationEndpoint";
|
||||
|
||||
/*! @brief The key for the @c endSessionEndpoint property.
|
||||
*/
|
||||
static NSString *const kEndSessionEndpointKey = @"endSessionEndpoint";
|
||||
|
||||
/*! @brief The key for the @c discoveryDocument property.
|
||||
*/
|
||||
static NSString *const kDiscoveryDocumentKey = @"discoveryDocument";
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OIDServiceConfiguration ()
|
||||
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint
|
||||
issuer:(nullable NSURL *)issuer
|
||||
registrationEndpoint:(nullable NSURL *)registrationEndpoint
|
||||
endSessionEndpoint:(nullable NSURL *)endSessionEndpoint
|
||||
discoveryDocument:(nullable OIDServiceDiscovery *)discoveryDocument
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OIDServiceConfiguration
|
||||
|
||||
- (instancetype)init
|
||||
OID_UNAVAILABLE_USE_INITIALIZER(@selector(
|
||||
initWithAuthorizationEndpoint:
|
||||
tokenEndpoint:)
|
||||
)
|
||||
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint
|
||||
issuer:(nullable NSURL *)issuer
|
||||
registrationEndpoint:(nullable NSURL *)registrationEndpoint
|
||||
endSessionEndpoint:(nullable OIDServiceDiscovery *)endSessionEndpoint
|
||||
discoveryDocument:(nullable OIDServiceDiscovery *)discoveryDocument {
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_authorizationEndpoint = [authorizationEndpoint copy];
|
||||
_tokenEndpoint = [tokenEndpoint copy];
|
||||
_issuer = [issuer copy];
|
||||
_registrationEndpoint = [registrationEndpoint copy];
|
||||
_endSessionEndpoint = [endSessionEndpoint copy];
|
||||
_discoveryDocument = [discoveryDocument copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint {
|
||||
return [self initWithAuthorizationEndpoint:authorizationEndpoint
|
||||
tokenEndpoint:tokenEndpoint
|
||||
issuer:nil
|
||||
registrationEndpoint:nil
|
||||
endSessionEndpoint:nil
|
||||
discoveryDocument:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint
|
||||
registrationEndpoint:(nullable NSURL *)registrationEndpoint {
|
||||
return [self initWithAuthorizationEndpoint:authorizationEndpoint
|
||||
tokenEndpoint:tokenEndpoint
|
||||
issuer:nil
|
||||
registrationEndpoint:registrationEndpoint
|
||||
endSessionEndpoint:nil
|
||||
discoveryDocument:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint
|
||||
issuer:(nullable NSURL *)issuer {
|
||||
return [self initWithAuthorizationEndpoint:authorizationEndpoint
|
||||
tokenEndpoint:tokenEndpoint
|
||||
issuer:issuer
|
||||
registrationEndpoint:nil
|
||||
endSessionEndpoint:nil
|
||||
discoveryDocument:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint
|
||||
issuer:(nullable NSURL *)issuer
|
||||
registrationEndpoint:(nullable NSURL *)registrationEndpoint {
|
||||
return [self initWithAuthorizationEndpoint:authorizationEndpoint
|
||||
tokenEndpoint:tokenEndpoint
|
||||
issuer:issuer
|
||||
registrationEndpoint:registrationEndpoint
|
||||
endSessionEndpoint:nil
|
||||
discoveryDocument:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint
|
||||
tokenEndpoint:(NSURL *)tokenEndpoint
|
||||
issuer:(nullable NSURL *)issuer
|
||||
registrationEndpoint:(nullable NSURL *)registrationEndpoint
|
||||
endSessionEndpoint:(nullable NSURL *)endSessionEndpoint {
|
||||
return [self initWithAuthorizationEndpoint:authorizationEndpoint
|
||||
tokenEndpoint:tokenEndpoint
|
||||
issuer:issuer
|
||||
registrationEndpoint:registrationEndpoint
|
||||
endSessionEndpoint:endSessionEndpoint
|
||||
discoveryDocument:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *) discoveryDocument {
|
||||
return [self initWithAuthorizationEndpoint:discoveryDocument.authorizationEndpoint
|
||||
tokenEndpoint:discoveryDocument.tokenEndpoint
|
||||
issuer:discoveryDocument.issuer
|
||||
registrationEndpoint:discoveryDocument.registrationEndpoint
|
||||
endSessionEndpoint:discoveryDocument.endSessionEndpoint
|
||||
discoveryDocument:discoveryDocument];
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
NSURL *authorizationEndpoint = [aDecoder decodeObjectOfClass:[NSURL class]
|
||||
forKey:kAuthorizationEndpointKey];
|
||||
NSURL *tokenEndpoint = [aDecoder decodeObjectOfClass:[NSURL class]
|
||||
forKey:kTokenEndpointKey];
|
||||
NSURL *issuer = [aDecoder decodeObjectOfClass:[NSURL class]
|
||||
forKey:kIssuerKey];
|
||||
NSURL *registrationEndpoint = [aDecoder decodeObjectOfClass:[NSURL class]
|
||||
forKey:kRegistrationEndpointKey];
|
||||
NSURL *endSessionEndpoint = [aDecoder decodeObjectOfClass:[NSURL class]
|
||||
forKey:kEndSessionEndpointKey];
|
||||
// We don't accept nil authorizationEndpoints or tokenEndpoints.
|
||||
if (!authorizationEndpoint || !tokenEndpoint) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
OIDServiceDiscovery *discoveryDocument = [aDecoder decodeObjectOfClass:[OIDServiceDiscovery class]
|
||||
forKey:kDiscoveryDocumentKey];
|
||||
|
||||
return [self initWithAuthorizationEndpoint:authorizationEndpoint
|
||||
tokenEndpoint:tokenEndpoint
|
||||
issuer:issuer
|
||||
registrationEndpoint:registrationEndpoint
|
||||
endSessionEndpoint:endSessionEndpoint
|
||||
discoveryDocument:discoveryDocument];
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:_authorizationEndpoint forKey:kAuthorizationEndpointKey];
|
||||
[aCoder encodeObject:_tokenEndpoint forKey:kTokenEndpointKey];
|
||||
[aCoder encodeObject:_issuer forKey:kIssuerKey];
|
||||
[aCoder encodeObject:_registrationEndpoint forKey:kRegistrationEndpointKey];
|
||||
[aCoder encodeObject:_discoveryDocument forKey:kDiscoveryDocumentKey];
|
||||
[aCoder encodeObject:_endSessionEndpoint forKey:kEndSessionEndpointKey];
|
||||
}
|
||||
|
||||
#pragma mark - description
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:
|
||||
@"OIDServiceConfiguration authorizationEndpoint: %@, tokenEndpoint: %@, "
|
||||
"registrationEndpoint: %@, endSessionEndpoint: %@, discoveryDocument: [%@]",
|
||||
_authorizationEndpoint,
|
||||
_tokenEndpoint,
|
||||
_registrationEndpoint,
|
||||
_endSessionEndpoint,
|
||||
_discoveryDocument];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+358
@@ -0,0 +1,358 @@
|
||||
/*! @file OIDServiceDiscovery.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Represents an OpenID Connect 1.0 Discovery Document
|
||||
@see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
*/
|
||||
@interface OIDServiceDiscovery : NSObject <NSCopying, NSSecureCoding>
|
||||
|
||||
/*! @brief The decoded OpenID Connect 1.0 Discovery Document as a dictionary.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSDictionary<NSString *, id> *discoveryDictionary;
|
||||
|
||||
/*! @brief REQUIRED. URL using the @c https scheme with no query or fragment component that the OP
|
||||
asserts as its Issuer Identifier. If Issuer discovery is supported, this value MUST be
|
||||
identical to the issuer value returned by WebFinger. This also MUST be identical to the
|
||||
@c iss Claim value in ID Tokens issued from this Issuer.
|
||||
@remarks issuer
|
||||
@seealso https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL *issuer;
|
||||
|
||||
/*! @brief REQUIRED. URL of the OP's OAuth 2.0 Authorization Endpoint.
|
||||
@remarks authorization_endpoint
|
||||
@seealso http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL *authorizationEndpoint;
|
||||
|
||||
/*! @brief URL of the OP's OAuth 2.0 Token Endpoint. This is REQUIRED unless only the Implicit Flow
|
||||
is used.
|
||||
@remarks token_endpoint
|
||||
@seealso http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL *tokenEndpoint;
|
||||
|
||||
/*! @brief RECOMMENDED. URL of the OP's UserInfo Endpoint. This URL MUST use the https scheme and
|
||||
MAY contain port, path, and query parameter components.
|
||||
@remarks userinfo_endpoint
|
||||
@seealso http://openid.net/specs/openid-connect-core-1_0.html#UserInfo
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *userinfoEndpoint;
|
||||
|
||||
/*! @brief REQUIRED. URL of the OP's JSON Web Key Set document. This contains the signing key(s) the
|
||||
RP uses to validate signatures from the OP. The JWK Set MAY also contain the Server's
|
||||
encryption key(s), which are used by RPs to encrypt requests to the Server. When both
|
||||
signing and encryption keys are made available, a use (Key Use) parameter value is REQUIRED
|
||||
for all keys in the referenced JWK Set to indicate each key's intended usage. Although some
|
||||
algorithms allow the same key to be used for both signatures and encryption, doing so is NOT
|
||||
RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509
|
||||
representations of keys provided. When used, the bare key values MUST still be present and
|
||||
MUST match those in the certificate.
|
||||
@remarks jwks_uri
|
||||
@seealso http://tools.ietf.org/html/rfc7517
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL *jwksURL;
|
||||
|
||||
/*! @brief RECOMMENDED. URL of the OP's Dynamic Client Registration Endpoint.
|
||||
@remarks registration_endpoint
|
||||
@seealso http://openid.net/specs/openid-connect-registration-1_0.html
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *registrationEndpoint;
|
||||
|
||||
/* @brief OPTIONAL. URL of the OP's RP-Initiated Logout endpoint.
|
||||
@remarks end_session_endpoint
|
||||
@seealso http://openid.net/specs/openid-connect-session-1_0.html#OPMetadata
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *endSessionEndpoint;
|
||||
|
||||
/*! @brief RECOMMENDED. JSON array containing a list of the OAuth 2.0 [RFC6749] scope values that
|
||||
this server supports. The server MUST support the openid scope value. Servers MAY choose not
|
||||
to advertise some supported scope values even when this parameter is used, although those
|
||||
defined in [OpenID.Core] SHOULD be listed, if supported.
|
||||
@remarks scopes_supported
|
||||
@seealso http://tools.ietf.org/html/rfc6749#section-3.3
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *scopesSupported;
|
||||
|
||||
/*! @brief REQUIRED. JSON array containing a list of the OAuth 2.0 @c response_type values that this
|
||||
OP supports. Dynamic OpenID Providers MUST support the @c code, @c id_token, and the token
|
||||
@c id_token Response Type values.
|
||||
@remarks response_types_supported
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray<NSString *> *responseTypesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the OAuth 2.0 @c response_mode values that this
|
||||
OP supports, as specified in OAuth 2.0 Multiple Response Type Encoding Practices. If
|
||||
omitted, the default for Dynamic OpenID Providers is @c ["query", "fragment"].
|
||||
@remarks response_modes_supported
|
||||
@seealso http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *responseModesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the OAuth 2.0 Grant Type values that this OP
|
||||
supports. Dynamic OpenID Providers MUST support the @c authorization_code and @c implicit
|
||||
Grant Type values and MAY support other Grant Types. If omitted, the default value is
|
||||
@c ["authorization_code", "implicit"].
|
||||
@remarks grant_types_supported
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *grantTypesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the Authentication Context Class References
|
||||
that this OP supports.
|
||||
@remarks acr_values_supported
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *acrValuesSupported;
|
||||
|
||||
/*! @brief REQUIRED. JSON array containing a list of the Subject Identifier types that this OP
|
||||
supports. Valid types include @c pairwise and @c public.
|
||||
@remarks subject_types_supported
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray<NSString *> *subjectTypesSupported;
|
||||
|
||||
/*! @brief REQUIRED. JSON array containing a list of the JWS signing algorithms (@c alg values)
|
||||
supported by the OP for the ID Token to encode the Claims in a JWT. The algorithm @c RS256
|
||||
MUST be included. The value @c none MAY be supported, but MUST NOT be used unless the
|
||||
Response Type used returns no ID Token from the Authorization Endpoint (such as when using
|
||||
the Authorization Code Flow).
|
||||
@remarks id_token_signing_alg_values_supported
|
||||
@seealso https://tools.ietf.org/html/rfc7519
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray<NSString *> *IDTokenSigningAlgorithmValuesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the JWE encryption algorithms (@c alg values)
|
||||
supported by the OP for the ID Token to encode the Claims in a JWT.
|
||||
@remarks id_token_encryption_alg_values_supported
|
||||
@seealso https://tools.ietf.org/html/rfc7519
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSArray<NSString *> *IDTokenEncryptionAlgorithmValuesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the JWE encryption algorithms (@c enc values)
|
||||
supported by the OP for the ID Token to encode the Claims in a JWT.
|
||||
@remarks id_token_encryption_enc_values_supported
|
||||
@seealso https://tools.ietf.org/html/rfc7519
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSArray<NSString *> *IDTokenEncryptionEncodingValuesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the JWS signing algorithms (@c alg values)
|
||||
supported by the UserInfo Endpoint to encode the Claims in a JWT. The value none MAY be
|
||||
included.
|
||||
@remarks userinfo_signing_alg_values_supported
|
||||
@seealso https://tools.ietf.org/html/rfc7515
|
||||
@seealso https://tools.ietf.org/html/rfc7518
|
||||
@seealso https://tools.ietf.org/html/rfc7519
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSArray<NSString *> *userinfoSigningAlgorithmValuesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the JWE encryption algorithms (alg values)
|
||||
supported by the UserInfo Endpoint to encode the Claims in a JWT.
|
||||
@remarks userinfo_encryption_alg_values_supported
|
||||
@seealso https://tools.ietf.org/html/rfc7516
|
||||
@seealso https://tools.ietf.org/html/rfc7518
|
||||
@seealso https://tools.ietf.org/html/rfc7519
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSArray<NSString *> *userinfoEncryptionAlgorithmValuesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the JWE encryption algorithms (@c enc values)
|
||||
supported by the UserInfo Endpoint to encode the Claims in a JWT.
|
||||
@remarks userinfo_encryption_enc_values_supported
|
||||
@seealso https://tools.ietf.org/html/rfc7519
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSArray<NSString *> *userinfoEncryptionEncodingValuesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the JWS signing algorithms (@c alg values)
|
||||
supported by the OP for Request Objects, which are described in Section 6.1 of OpenID
|
||||
Connect Core 1.0. These algorithms are used both when the Request Object is passed by value
|
||||
(using the request parameter) and when it is passed by reference (using the @c request_uri
|
||||
parameter). Servers SHOULD support @c none and @c RS256.
|
||||
@remarks request_object_signing_alg_values_supported
|
||||
@seealso http://openid.net/specs/openid-connect-core-1_0.html
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSArray<NSString *> *requestObjectSigningAlgorithmValuesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the JWE encryption algorithms (@c alg values)
|
||||
supported by the OP for Request Objects. These algorithms are used both when the Request
|
||||
Object is passed by value and when it is passed by reference.
|
||||
@remarks request_object_encryption_alg_values_supported
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSArray<NSString *> *requestObjectEncryptionAlgorithmValuesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the JWE encryption algorithms (@c enc values)
|
||||
supported by the OP for Request Objects. These algorithms are used both when the Request
|
||||
Object is passed by value and when it is passed by reference.
|
||||
@remarks request_object_encryption_enc_values_supported
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSArray<NSString *> *requestObjectEncryptionEncodingValuesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of Client Authentication methods supported by this
|
||||
Token Endpoint. The options are @c client_secret_post, @c client_secret_basic,
|
||||
@c client_secret_jwt, and @c private_key_jwt, as described in Section 9 of OpenID Connect
|
||||
Core 1.0. Other authentication methods MAY be defined by extensions. If omitted, the default
|
||||
is @c client_secret_basic -- the HTTP Basic Authentication Scheme specified in Section 2.3.1
|
||||
of OAuth 2.0.
|
||||
@remarks token_endpoint_auth_methods_supported
|
||||
@seealso http://openid.net/specs/openid-connect-core-1_0.html
|
||||
@seealso http://tools.ietf.org/html/rfc6749#section-2.3.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *tokenEndpointAuthMethodsSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the JWS signing algorithms (@c alg values)
|
||||
supported by the Token Endpoint for the signature on the JWT used to authenticate the Client
|
||||
at the Token Endpoint for the @c private_key_jwt and @c client_secret_jwt authentication
|
||||
methods. Servers SHOULD support @c RS256. The value @c none MUST NOT be used.
|
||||
@remarks token_endpoint_auth_signing_alg_values_supported
|
||||
@seealso https://tools.ietf.org/html/rfc7519
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSArray<NSString *> *tokenEndpointAuthSigningAlgorithmValuesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the @c display parameter values that the OpenID
|
||||
Provider supports. These values are described in Section 3.1.2.1 of OpenID Connect Core 1.0.
|
||||
@remarks display_values_supported
|
||||
@seealso http://openid.net/specs/openid-connect-core-1_0.html
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *displayValuesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. JSON array containing a list of the Claim Types that the OpenID Provider
|
||||
supports. These Claim Types are described in Section 5.6 of OpenID Connect Core 1.0. Values
|
||||
defined by this specification are @c normal, @c aggregated, and @c distributed. If omitted,
|
||||
the implementation supports only @c normal Claims.
|
||||
@remarks claim_types_supported
|
||||
@seealso http://openid.net/specs/openid-connect-core-1_0.html
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *claimTypesSupported;
|
||||
|
||||
/*! @brief RECOMMENDED. JSON array containing a list of the Claim Names of the Claims that the
|
||||
OpenID Provider MAY be able to supply values for. Note that for privacy or other reasons,
|
||||
this might not be an exhaustive list.
|
||||
@remarks claims_supported
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *claimsSupported;
|
||||
|
||||
/*! @brief OPTIONAL. URL of a page containing human-readable information that developers might want
|
||||
or need to know when using the OpenID Provider. In particular, if the OpenID Provider does
|
||||
not support Dynamic Client Registration, then information on how to register Clients needs
|
||||
to be provided in this documentation.
|
||||
@remarks service_documentation
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *serviceDocumentation;
|
||||
|
||||
/*! @brief OPTIONAL. Languages and scripts supported for values in Claims being returned,
|
||||
represented as a JSON array of BCP47 language tag values. Not all languages and scripts are
|
||||
necessarily supported for all Claim values.
|
||||
@remarks claims_locales_supported
|
||||
@seealso http://tools.ietf.org/html/rfc5646
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *claimsLocalesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. Languages and scripts supported for the user interface, represented as a JSON
|
||||
array of BCP47 language tag values.
|
||||
@remarks ui_locales_supported
|
||||
@seealso http://tools.ietf.org/html/rfc5646
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSArray<NSString *> *UILocalesSupported;
|
||||
|
||||
/*! @brief OPTIONAL. Boolean value specifying whether the OP supports use of the claims parameter,
|
||||
with @c true indicating support. If omitted, the default value is @c false.
|
||||
@remarks claims_parameter_supported
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL claimsParameterSupported;
|
||||
|
||||
/*! @brief OPTIONAL. Boolean value specifying whether the OP supports use of the request parameter,
|
||||
with @c true indicating support. If omitted, the default value is @c false.
|
||||
@remarks request_parameter_supported
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL requestParameterSupported;
|
||||
|
||||
/*! @brief OPTIONAL. Boolean value specifying whether the OP supports use of the @c request_uri
|
||||
parameter, with true indicating support. If omitted, the default value is @c true.
|
||||
@remarks request_uri_parameter_supported
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL requestURIParameterSupported;
|
||||
|
||||
/*! @brief OPTIONAL. Boolean value specifying whether the OP requires any @c request_uri values used
|
||||
to be pre-registered using the @c request_uris registration parameter. Pre-registration is
|
||||
REQUIRED when the value is @c true. If omitted, the default value is @c false.
|
||||
@remarks require_request_uri_registration
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL requireRequestURIRegistration;
|
||||
|
||||
/*! @brief OPTIONAL. URL that the OpenID Provider provides to the person registering the Client to
|
||||
read about the OP's requirements on how the Relying Party can use the data provided by the
|
||||
OP. The registration process SHOULD display this URL to the person registering the Client if
|
||||
it is given.
|
||||
@remarks op_policy_uri
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *OPPolicyURI;
|
||||
|
||||
/*! @brief OPTIONAL. URL that the OpenID Provider provides to the person registering the Client to
|
||||
read about OpenID Provider's terms of service. The registration process SHOULD display this
|
||||
URL to the person registering the Client if it is given.
|
||||
@remarks op_tos_uri
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *OPTosURI;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use @c initWithDictionary:error:, @c initWithJSON:error, or the
|
||||
@c serviceDiscoveryWithURL:callback: factory method.
|
||||
*/
|
||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Decodes a OpenID Connect Discovery 1.0 JSON document.
|
||||
@param serviceDiscoveryJSON An OpenID Connect Service Discovery document.
|
||||
@param error If a required field is missing from the dictionary, an error with domain
|
||||
@c ::OIDGeneralErrorDomain and code @c ::OIDErrorCodeInvalidDiscoveryDocument will be
|
||||
returned.
|
||||
*/
|
||||
- (nullable instancetype)initWithJSON:(NSString *)serviceDiscoveryJSON
|
||||
error:(NSError **_Nullable)error;
|
||||
|
||||
/*! @brief Decodes a OpenID Connect Discovery 1.0 JSON document.
|
||||
@param serviceDiscoveryJSONData An OpenID Connect Service Discovery document.
|
||||
@param error If a required field is missing from the dictionary, an error with domain
|
||||
@c ::OIDGeneralErrorDomain and code @c ::OIDErrorCodeInvalidDiscoveryDocument will be
|
||||
returned.
|
||||
*/
|
||||
- (nullable instancetype)initWithJSONData:(NSData *)serviceDiscoveryJSONData
|
||||
error:(NSError **_Nullable)error;
|
||||
|
||||
/*! @brief Designated initializer. The dictionary keys should match the keys defined in the OpenID
|
||||
Connect Discovery 1.0 standard for OpenID Provider Metadata.
|
||||
@param serviceDiscoveryDictionary A dictionary representing an OpenID Connect Service Discovery
|
||||
document.
|
||||
@param error If a required field is missing from the dictionary, an error with domain
|
||||
@c ::OIDGeneralErrorDomain and code @c ::OIDErrorCodeInvalidDiscoveryDocument will be
|
||||
returned.
|
||||
*/
|
||||
- (nullable instancetype)initWithDictionary:(NSDictionary *)serviceDiscoveryDictionary
|
||||
error:(NSError **_Nullable)error NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+362
@@ -0,0 +1,362 @@
|
||||
/*! @file OIDServiceDiscovery.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDServiceDiscovery.h"
|
||||
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDErrorUtilities.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! Field keys associated with an OpenID Connect Discovery Document. */
|
||||
static NSString *const kIssuerKey = @"issuer";
|
||||
static NSString *const kAuthorizationEndpointKey = @"authorization_endpoint";
|
||||
static NSString *const kTokenEndpointKey = @"token_endpoint";
|
||||
static NSString *const kUserinfoEndpointKey = @"userinfo_endpoint";
|
||||
static NSString *const kJWKSURLKey = @"jwks_uri";
|
||||
static NSString *const kRegistrationEndpointKey = @"registration_endpoint";
|
||||
static NSString *const kEndSessionEndpointKey = @"end_session_endpoint";
|
||||
static NSString *const kScopesSupportedKey = @"scopes_supported";
|
||||
static NSString *const kResponseTypesSupportedKey = @"response_types_supported";
|
||||
static NSString *const kResponseModesSupportedKey = @"response_modes_supported";
|
||||
static NSString *const kGrantTypesSupportedKey = @"grant_types_supported";
|
||||
static NSString *const kACRValuesSupportedKey = @"acr_values_supported";
|
||||
static NSString *const kSubjectTypesSupportedKey = @"subject_types_supported";
|
||||
static NSString *const kIDTokenSigningAlgorithmValuesSupportedKey =
|
||||
@"id_token_signing_alg_values_supported";
|
||||
static NSString *const kIDTokenEncryptionAlgorithmValuesSupportedKey =
|
||||
@"id_token_encryption_alg_values_supported";
|
||||
static NSString *const kIDTokenEncryptionEncodingValuesSupportedKey =
|
||||
@"id_token_encryption_enc_values_supported";
|
||||
static NSString *const kUserinfoSigningAlgorithmValuesSupportedKey =
|
||||
@"userinfo_signing_alg_values_supported";
|
||||
static NSString *const kUserinfoEncryptionAlgorithmValuesSupportedKey =
|
||||
@"userinfo_encryption_alg_values_supported";
|
||||
static NSString *const kUserinfoEncryptionEncodingValuesSupportedKey =
|
||||
@"userinfo_encryption_enc_values_supported";
|
||||
static NSString *const kRequestObjectSigningAlgorithmValuesSupportedKey =
|
||||
@"request_object_signing_alg_values_supported";
|
||||
static NSString *const kRequestObjectEncryptionAlgorithmValuesSupportedKey =
|
||||
@"request_object_encryption_alg_values_supported";
|
||||
static NSString *const kRequestObjectEncryptionEncodingValuesSupported =
|
||||
@"request_object_encryption_enc_values_supported";
|
||||
static NSString *const kTokenEndpointAuthMethodsSupportedKey =
|
||||
@"token_endpoint_auth_methods_supported";
|
||||
static NSString *const kTokenEndpointAuthSigningAlgorithmValuesSupportedKey =
|
||||
@"token_endpoint_auth_signing_alg_values_supported";
|
||||
static NSString *const kDisplayValuesSupportedKey = @"display_values_supported";
|
||||
static NSString *const kClaimTypesSupportedKey = @"claim_types_supported";
|
||||
static NSString *const kClaimsSupportedKey = @"claims_supported";
|
||||
static NSString *const kServiceDocumentationKey = @"service_documentation";
|
||||
static NSString *const kClaimsLocalesSupportedKey = @"claims_locales_supported";
|
||||
static NSString *const kUILocalesSupportedKey = @"ui_locales_supported";
|
||||
static NSString *const kClaimsParameterSupportedKey = @"claims_parameter_supported";
|
||||
static NSString *const kRequestParameterSupportedKey = @"request_parameter_supported";
|
||||
static NSString *const kRequestURIParameterSupportedKey = @"request_uri_parameter_supported";
|
||||
static NSString *const kRequireRequestURIRegistrationKey = @"require_request_uri_registration";
|
||||
static NSString *const kOPPolicyURIKey = @"op_policy_uri";
|
||||
static NSString *const kOPTosURIKey = @"op_tos_uri";
|
||||
|
||||
@implementation OIDServiceDiscovery {
|
||||
NSDictionary *_discoveryDictionary;
|
||||
}
|
||||
|
||||
- (nonnull instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithDictionary:error:))
|
||||
|
||||
- (nullable instancetype)initWithJSON:(NSString *)serviceDiscoveryJSON error:(NSError **)error {
|
||||
NSData *jsonData = [serviceDiscoveryJSON dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [self initWithJSONData:jsonData error:error];
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithJSONData:(NSData *)serviceDiscoveryJSONData
|
||||
error:(NSError **_Nullable)error {
|
||||
NSError *jsonError;
|
||||
NSDictionary *json =
|
||||
[NSJSONSerialization JSONObjectWithData:serviceDiscoveryJSONData options:0 error:&jsonError];
|
||||
if (!json || jsonError) {
|
||||
*error = [OIDErrorUtilities errorWithCode:OIDErrorCodeJSONDeserializationError
|
||||
underlyingError:jsonError
|
||||
description:jsonError.localizedDescription];
|
||||
return nil;
|
||||
}
|
||||
if (![json isKindOfClass:[NSDictionary class]]) {
|
||||
*error = [OIDErrorUtilities errorWithCode:OIDErrorCodeInvalidDiscoveryDocument
|
||||
underlyingError:nil
|
||||
description:@"Discovery document isn't a dictionary"];
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [self initWithDictionary:json error:error];
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithDictionary:(NSDictionary *)serviceDiscoveryDictionary
|
||||
error:(NSError **_Nullable)error {
|
||||
if (![[self class] dictionaryHasRequiredFields:serviceDiscoveryDictionary error:error]) {
|
||||
return nil;
|
||||
}
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_discoveryDictionary = [serviceDiscoveryDictionary copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
/*! @brief Checks to see if the specified dictionary contains the required fields.
|
||||
@discussion This test is not meant to provide semantic analysis of the document (eg. fields
|
||||
where the value @c none is not an allowed option would not cause this method to fail if
|
||||
their value was @c none.) We are just testing to make sure we can meet the nullability
|
||||
contract we promised in the header.
|
||||
*/
|
||||
+ (BOOL)dictionaryHasRequiredFields:(NSDictionary<NSString *, id> *)dictionary
|
||||
error:(NSError **_Nullable)error {
|
||||
static NSString *const kMissingFieldErrorText = @"Missing field: %@";
|
||||
static NSString *const kInvalidURLFieldErrorText = @"Invalid URL: %@";
|
||||
|
||||
NSArray *requiredFields = @[
|
||||
kIssuerKey,
|
||||
kAuthorizationEndpointKey,
|
||||
kTokenEndpointKey,
|
||||
kJWKSURLKey,
|
||||
kResponseTypesSupportedKey,
|
||||
kSubjectTypesSupportedKey,
|
||||
kIDTokenSigningAlgorithmValuesSupportedKey
|
||||
];
|
||||
|
||||
for (NSString *field in requiredFields) {
|
||||
if (!dictionary[field]) {
|
||||
if (error) {
|
||||
NSString *errorText = [NSString stringWithFormat:kMissingFieldErrorText, field];
|
||||
*error = [OIDErrorUtilities errorWithCode:OIDErrorCodeInvalidDiscoveryDocument
|
||||
underlyingError:nil
|
||||
description:errorText];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Check required URL fields are valid URLs.
|
||||
NSArray *requiredURLFields = @[
|
||||
kIssuerKey,
|
||||
kTokenEndpointKey,
|
||||
kJWKSURLKey
|
||||
];
|
||||
|
||||
for (NSString *field in requiredURLFields) {
|
||||
if (![NSURL URLWithString:dictionary[field]]) {
|
||||
if (error) {
|
||||
NSString *errorText = [NSString stringWithFormat:kInvalidURLFieldErrorText, field];
|
||||
*error = [OIDErrorUtilities errorWithCode:OIDErrorCodeInvalidDiscoveryDocument
|
||||
underlyingError:nil
|
||||
description:errorText];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
NSError *error;
|
||||
NSDictionary *dictionary = [[NSDictionary alloc] initWithCoder:aDecoder];
|
||||
self = [self initWithDictionary:dictionary error:&error];
|
||||
if (error) {
|
||||
return nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[_discoveryDictionary encodeWithCoder:aCoder];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (NSDictionary<NSString *, NSString *> *)discoveryDictionary {
|
||||
return _discoveryDictionary;
|
||||
}
|
||||
|
||||
- (NSURL *)issuer {
|
||||
return [NSURL URLWithString:_discoveryDictionary[kIssuerKey]];
|
||||
}
|
||||
|
||||
- (NSURL *)authorizationEndpoint {
|
||||
return [NSURL URLWithString:_discoveryDictionary[kAuthorizationEndpointKey]];
|
||||
}
|
||||
|
||||
- (NSURL *)tokenEndpoint {
|
||||
return [NSURL URLWithString:_discoveryDictionary[kTokenEndpointKey]];
|
||||
}
|
||||
|
||||
- (nullable NSURL *)userinfoEndpoint {
|
||||
return [NSURL URLWithString:_discoveryDictionary[kUserinfoEndpointKey]];
|
||||
}
|
||||
|
||||
- (NSURL *)jwksURL {
|
||||
return [NSURL URLWithString:_discoveryDictionary[kJWKSURLKey]];
|
||||
}
|
||||
|
||||
- (nullable NSURL *)registrationEndpoint {
|
||||
return [NSURL URLWithString:_discoveryDictionary[kRegistrationEndpointKey]];
|
||||
}
|
||||
|
||||
- (nullable NSURL *)endSessionEndpoint {
|
||||
return [NSURL URLWithString:_discoveryDictionary[kEndSessionEndpointKey]];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)scopesSupported {
|
||||
return _discoveryDictionary[kScopesSupportedKey];
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)responseTypesSupported {
|
||||
return _discoveryDictionary[kResponseTypesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)responseModesSupported {
|
||||
return _discoveryDictionary[kResponseModesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)grantTypesSupported {
|
||||
return _discoveryDictionary[kGrantTypesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)acrValuesSupported {
|
||||
return _discoveryDictionary[kACRValuesSupportedKey];
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)subjectTypesSupported {
|
||||
return _discoveryDictionary[kSubjectTypesSupportedKey];
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *) IDTokenSigningAlgorithmValuesSupported {
|
||||
return _discoveryDictionary[kIDTokenSigningAlgorithmValuesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)IDTokenEncryptionAlgorithmValuesSupported {
|
||||
return _discoveryDictionary[kIDTokenEncryptionAlgorithmValuesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)IDTokenEncryptionEncodingValuesSupported {
|
||||
return _discoveryDictionary[kIDTokenEncryptionEncodingValuesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)userinfoSigningAlgorithmValuesSupported {
|
||||
return _discoveryDictionary[kUserinfoSigningAlgorithmValuesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)userinfoEncryptionAlgorithmValuesSupported {
|
||||
return _discoveryDictionary[kUserinfoEncryptionAlgorithmValuesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)userinfoEncryptionEncodingValuesSupported {
|
||||
return _discoveryDictionary[kUserinfoEncryptionEncodingValuesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)requestObjectSigningAlgorithmValuesSupported {
|
||||
return _discoveryDictionary[kRequestObjectSigningAlgorithmValuesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *) requestObjectEncryptionAlgorithmValuesSupported {
|
||||
return _discoveryDictionary[kRequestObjectEncryptionAlgorithmValuesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *) requestObjectEncryptionEncodingValuesSupported {
|
||||
return _discoveryDictionary[kRequestObjectEncryptionEncodingValuesSupported];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)tokenEndpointAuthMethodsSupported {
|
||||
return _discoveryDictionary[kTokenEndpointAuthMethodsSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)tokenEndpointAuthSigningAlgorithmValuesSupported {
|
||||
return _discoveryDictionary[kTokenEndpointAuthSigningAlgorithmValuesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)displayValuesSupported {
|
||||
return _discoveryDictionary[kDisplayValuesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)claimTypesSupported {
|
||||
return _discoveryDictionary[kClaimTypesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)claimsSupported {
|
||||
return _discoveryDictionary[kClaimsSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSURL *)serviceDocumentation {
|
||||
return [NSURL URLWithString:_discoveryDictionary[kServiceDocumentationKey]];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)claimsLocalesSupported {
|
||||
return _discoveryDictionary[kClaimsLocalesSupportedKey];
|
||||
}
|
||||
|
||||
- (nullable NSArray<NSString *> *)UILocalesSupported {
|
||||
return _discoveryDictionary[kUILocalesSupportedKey];
|
||||
}
|
||||
|
||||
- (BOOL)claimsParameterSupported {
|
||||
return [_discoveryDictionary[kClaimsParameterSupportedKey] boolValue];
|
||||
}
|
||||
|
||||
- (BOOL)requestParameterSupported {
|
||||
return [_discoveryDictionary[kRequestParameterSupportedKey] boolValue];
|
||||
}
|
||||
|
||||
- (BOOL)requestURIParameterSupported {
|
||||
// Default is true/YES.
|
||||
if (!_discoveryDictionary[kRequestURIParameterSupportedKey]) {
|
||||
return YES;
|
||||
}
|
||||
return [_discoveryDictionary[kRequestURIParameterSupportedKey] boolValue];
|
||||
}
|
||||
|
||||
- (BOOL)requireRequestURIRegistration {
|
||||
return [_discoveryDictionary[kRequireRequestURIRegistrationKey] boolValue];
|
||||
}
|
||||
|
||||
- (nullable NSURL *)OPPolicyURI {
|
||||
return [NSURL URLWithString:_discoveryDictionary[kOPPolicyURIKey]];
|
||||
}
|
||||
|
||||
- (nullable NSURL *)OPTosURI {
|
||||
return [NSURL URLWithString:_discoveryDictionary[kOPTosURIKey]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
/*! @file OIDTokenRequest.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
// This file only declares string constants useful for constructing a @c OIDTokenRequest, so it is
|
||||
// imported here for convenience.
|
||||
#import "OIDGrantTypes.h"
|
||||
|
||||
@class OIDAuthorizationResponse;
|
||||
@class OIDServiceConfiguration;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Represents a token request.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.2
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||
*/
|
||||
@interface OIDTokenRequest : 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 The type of token being sent to the token endpoint, i.e. "authorization_code" for the
|
||||
authorization code exchange, or "refresh_token" for an access token refresh request.
|
||||
@remarks grant_type
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||
@see https://www.google.com/url?sa=D&q=https%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc6749%23section-6
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *grantType;
|
||||
|
||||
/*! @brief The authorization code received from the authorization server.
|
||||
@remarks code
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *authorizationCode;
|
||||
|
||||
/*! @brief The client's redirect URI.
|
||||
@remarks redirect_uri
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL *redirectURL;
|
||||
|
||||
/*! @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;
|
||||
|
||||
/*! @brief The value of the scope parameter is expressed as a list of space-delimited,
|
||||
case-sensitive strings.
|
||||
@remarks scope
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.3
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *scope;
|
||||
|
||||
/*! @brief The refresh token, which can be used to obtain new access tokens using the same
|
||||
authorization grant.
|
||||
@remarks refresh_token
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *refreshToken;
|
||||
|
||||
/*! @brief The PKCE code verifier used to redeem the authorization code.
|
||||
@remarks code_verifier
|
||||
@see https://tools.ietf.org/html/rfc7636#section-4.3
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *codeVerifier;
|
||||
|
||||
/*! @brief The client's additional token request parameters.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDictionary<NSString *, NSString *> *additionalParameters;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use
|
||||
initWithConfiguration:grantType:code:redirectURL:clientID:additionalParameters:.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @param configuration The service's configuration.
|
||||
@param grantType the type of token being sent to the token endpoint, i.e. "authorization_code"
|
||||
for the authorization code exchange, or "refresh_token" for an access token refresh request.
|
||||
@see OIDGrantTypes.h
|
||||
@param code The authorization code received from the authorization server.
|
||||
@param redirectURL The client's redirect URI.
|
||||
@param clientID The client identifier.
|
||||
@param clientSecret The client secret.
|
||||
@param scopes An array of scopes to combine into a single scope string per the OAuth2 spec.
|
||||
@param refreshToken The refresh token.
|
||||
@param codeVerifier The PKCE code verifier.
|
||||
@param additionalParameters The client's additional token request parameters.
|
||||
*/
|
||||
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
grantType:(NSString *)grantType
|
||||
authorizationCode:(nullable NSString *)code
|
||||
redirectURL:(nullable NSURL *)redirectURL
|
||||
clientID:(NSString *)clientID
|
||||
clientSecret:(nullable NSString *)clientSecret
|
||||
scopes:(nullable NSArray<NSString *> *)scopes
|
||||
refreshToken:(nullable NSString *)refreshToken
|
||||
codeVerifier:(nullable NSString *)codeVerifier
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters;
|
||||
|
||||
/*! @brief Designated initializer.
|
||||
@param configuration The service's configuration.
|
||||
@param grantType the type of token being sent to the token endpoint, i.e. "authorization_code"
|
||||
for the authorization code exchange, or "refresh_token" for an access token refresh request.
|
||||
@see OIDGrantTypes.h
|
||||
@param code The authorization code received from the authorization server.
|
||||
@param redirectURL The client's redirect URI.
|
||||
@param clientID The client identifier.
|
||||
@param clientSecret The client secret.
|
||||
@param scope The value of the scope parameter is expressed as a list of space-delimited,
|
||||
case-sensitive strings.
|
||||
@param refreshToken The refresh token.
|
||||
@param codeVerifier The PKCE code verifier.
|
||||
@param additionalParameters The client's additional token request parameters.
|
||||
*/
|
||||
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
grantType:(NSString *)grantType
|
||||
authorizationCode:(nullable NSString *)code
|
||||
redirectURL:(nullable NSURL *)redirectURL
|
||||
clientID:(NSString *)clientID
|
||||
clientSecret:(nullable NSString *)clientSecret
|
||||
scope:(nullable NSString *)scope
|
||||
refreshToken:(nullable NSString *)refreshToken
|
||||
codeVerifier:(nullable NSString *)codeVerifier
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/*! @brief Constructs an @c NSURLRequest representing the token request.
|
||||
@return An @c NSURLRequest representing the token request.
|
||||
*/
|
||||
- (NSURLRequest *)URLRequest;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
/*! @file OIDTokenRequest.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDTokenRequest.h"
|
||||
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDError.h"
|
||||
#import "OIDScopeUtilities.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 grantType property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kGrantTypeKey = @"grant_type";
|
||||
|
||||
/*! @brief The key for the @c authorizationCode property for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kAuthorizationCodeKey = @"code";
|
||||
|
||||
/*! @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 Key used to encode the @c redirectURL property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kRedirectURLKey = @"redirect_uri";
|
||||
|
||||
/*! @brief Key used to encode the @c scopes property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kScopeKey = @"scope";
|
||||
|
||||
/*! @brief Key used to encode the @c refreshToken property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kRefreshTokenKey = @"refresh_token";
|
||||
|
||||
/*! @brief Key used to encode the @c codeVerifier property for @c NSSecureCoding and to build the
|
||||
request URL.
|
||||
*/
|
||||
static NSString *const kCodeVerifierKey = @"code_verifier";
|
||||
|
||||
/*! @brief Key used to encode the @c additionalParameters property for
|
||||
@c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kAdditionalParametersKey = @"additionalParameters";
|
||||
|
||||
@implementation OIDTokenRequest
|
||||
|
||||
- (instancetype)init
|
||||
OID_UNAVAILABLE_USE_INITIALIZER(
|
||||
@selector(initWithConfiguration:
|
||||
grantType:
|
||||
authorizationCode:
|
||||
redirectURL:
|
||||
clientID:
|
||||
clientSecret:
|
||||
scope:
|
||||
refreshToken:
|
||||
codeVerifier:
|
||||
additionalParameters:)
|
||||
)
|
||||
|
||||
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
grantType:(NSString *)grantType
|
||||
authorizationCode:(nullable NSString *)code
|
||||
redirectURL:(nullable NSURL *)redirectURL
|
||||
clientID:(NSString *)clientID
|
||||
clientSecret:(nullable NSString *)clientSecret
|
||||
scopes:(nullable NSArray<NSString *> *)scopes
|
||||
refreshToken:(nullable NSString *)refreshToken
|
||||
codeVerifier:(nullable NSString *)codeVerifier
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters {
|
||||
return [self initWithConfiguration:configuration
|
||||
grantType:grantType
|
||||
authorizationCode:code
|
||||
redirectURL:redirectURL
|
||||
clientID:clientID
|
||||
clientSecret:clientSecret
|
||||
scope:[OIDScopeUtilities scopesWithArray:scopes]
|
||||
refreshToken:refreshToken
|
||||
codeVerifier:(NSString *)codeVerifier
|
||||
additionalParameters:additionalParameters];
|
||||
}
|
||||
|
||||
- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
|
||||
grantType:(NSString *)grantType
|
||||
authorizationCode:(nullable NSString *)code
|
||||
redirectURL:(nullable NSURL *)redirectURL
|
||||
clientID:(NSString *)clientID
|
||||
clientSecret:(nullable NSString *)clientSecret
|
||||
scope:(nullable NSString *)scope
|
||||
refreshToken:(nullable NSString *)refreshToken
|
||||
codeVerifier:(nullable NSString *)codeVerifier
|
||||
additionalParameters:(nullable NSDictionary<NSString *, NSString *> *)additionalParameters {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_configuration = [configuration copy];
|
||||
_grantType = [grantType copy];
|
||||
_authorizationCode = [code copy];
|
||||
_redirectURL = [redirectURL copy];
|
||||
_clientID = [clientID copy];
|
||||
_clientSecret = [clientSecret copy];
|
||||
_scope = [scope copy];
|
||||
_refreshToken = [refreshToken copy];
|
||||
_codeVerifier = [codeVerifier copy];
|
||||
_additionalParameters =
|
||||
[[NSDictionary alloc] initWithDictionary:additionalParameters copyItems:YES];
|
||||
|
||||
// Additional validation for the authorization_code grant type
|
||||
if ([_grantType isEqual:OIDGrantTypeAuthorizationCode]) {
|
||||
// redirect URI must not be nil
|
||||
if (!_redirectURL) {
|
||||
[NSException raise:OIDOAuthExceptionInvalidTokenRequestNullRedirectURL
|
||||
format:@"%@", OIDOAuthExceptionInvalidTokenRequestNullRedirectURL, nil];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
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 *grantType = [aDecoder decodeObjectOfClass:[NSString class] forKey:kGrantTypeKey];
|
||||
NSString *code = [aDecoder decodeObjectOfClass:[NSString class] forKey:kAuthorizationCodeKey];
|
||||
NSString *clientID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kClientIDKey];
|
||||
NSString *clientSecret = [aDecoder decodeObjectOfClass:[NSString class] forKey:kClientSecretKey];
|
||||
NSString *scope = [aDecoder decodeObjectOfClass:[NSString class] forKey:kScopeKey];
|
||||
NSString *refreshToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:kRefreshTokenKey];
|
||||
NSString *codeVerifier = [aDecoder decodeObjectOfClass:[NSString class] forKey:kCodeVerifierKey];
|
||||
NSURL *redirectURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kRedirectURLKey];
|
||||
NSSet *additionalParameterCodingClasses = [NSSet setWithArray:@[
|
||||
[NSDictionary class],
|
||||
[NSString class]
|
||||
]];
|
||||
NSDictionary *additionalParameters =
|
||||
[aDecoder decodeObjectOfClasses:additionalParameterCodingClasses
|
||||
forKey:kAdditionalParametersKey];
|
||||
self = [self initWithConfiguration:configuration
|
||||
grantType:grantType
|
||||
authorizationCode:code
|
||||
redirectURL:redirectURL
|
||||
clientID:clientID
|
||||
clientSecret:clientSecret
|
||||
scope:scope
|
||||
refreshToken:refreshToken
|
||||
codeVerifier:codeVerifier
|
||||
additionalParameters:additionalParameters];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:_configuration forKey:kConfigurationKey];
|
||||
[aCoder encodeObject:_grantType forKey:kGrantTypeKey];
|
||||
[aCoder encodeObject:_authorizationCode forKey:kAuthorizationCodeKey];
|
||||
[aCoder encodeObject:_clientID forKey:kClientIDKey];
|
||||
[aCoder encodeObject:_clientSecret forKey:kClientSecretKey];
|
||||
[aCoder encodeObject:_redirectURL forKey:kRedirectURLKey];
|
||||
[aCoder encodeObject:_scope forKey:kScopeKey];
|
||||
[aCoder encodeObject:_refreshToken forKey:kRefreshTokenKey];
|
||||
[aCoder encodeObject:_codeVerifier forKey:kCodeVerifierKey];
|
||||
[aCoder encodeObject:_additionalParameters forKey:kAdditionalParametersKey];
|
||||
}
|
||||
|
||||
#pragma mark - NSObject overrides
|
||||
|
||||
- (NSString *)description {
|
||||
NSURLRequest *request = self.URLRequest;
|
||||
NSString *requestBody =
|
||||
[[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding];
|
||||
return [NSString stringWithFormat:@"<%@: %p, request: <URL: %@, HTTPBody: %@>>",
|
||||
NSStringFromClass([self class]),
|
||||
(void *)self,
|
||||
request.URL,
|
||||
requestBody];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
/*! @brief Constructs the request URI.
|
||||
@return A URL representing the token request.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||
*/
|
||||
- (NSURL *)tokenRequestURL {
|
||||
return _configuration.tokenEndpoint;
|
||||
}
|
||||
|
||||
/*! @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 request URL.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||
*/
|
||||
- (OIDURLQueryComponent *)tokenRequestBody {
|
||||
OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init];
|
||||
|
||||
// Add parameters, as applicable.
|
||||
if (_grantType) {
|
||||
[query addParameter:kGrantTypeKey value:_grantType];
|
||||
}
|
||||
if (_scope) {
|
||||
[query addParameter:kScopeKey value:_scope];
|
||||
}
|
||||
if (_redirectURL) {
|
||||
[query addParameter:kRedirectURLKey value:_redirectURL.absoluteString];
|
||||
}
|
||||
if (_refreshToken) {
|
||||
[query addParameter:kRefreshTokenKey value:_refreshToken];
|
||||
}
|
||||
if (_authorizationCode) {
|
||||
[query addParameter:kAuthorizationCodeKey value:_authorizationCode];
|
||||
}
|
||||
if (_codeVerifier) {
|
||||
[query addParameter:kCodeVerifierKey value:_codeVerifier];
|
||||
}
|
||||
|
||||
// Add any additional parameters the client has specified.
|
||||
[query addParameters:_additionalParameters];
|
||||
|
||||
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 tokenRequestURL];
|
||||
NSMutableURLRequest *URLRequest = [[NSURLRequest requestWithURL:tokenRequestURL] mutableCopy];
|
||||
URLRequest.HTTPMethod = kHTTPPost;
|
||||
[URLRequest setValue:kHTTPContentTypeHeaderValue forHTTPHeaderField:kHTTPContentTypeHeaderKey];
|
||||
|
||||
OIDURLQueryComponent *bodyParameters = [self tokenRequestBody];
|
||||
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
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*! @file OIDTokenResponse.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 OIDTokenRequest;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Represents the response to an token request.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-3.2
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||
*/
|
||||
@interface OIDTokenResponse : NSObject <NSCopying, NSSecureCoding>
|
||||
|
||||
/*! @brief The request which was serviced.
|
||||
*/
|
||||
@property(nonatomic, readonly) OIDTokenRequest *request;
|
||||
|
||||
/*! @brief The access token generated by the authorization server.
|
||||
@remarks access_token
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.4
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *accessToken;
|
||||
|
||||
/*! @brief The approximate expiration date & time of the access token.
|
||||
@remarks expires_in
|
||||
@seealso OIDTokenResponse.accessToken
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.4
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDate *accessTokenExpirationDate;
|
||||
|
||||
/*! @brief Typically "Bearer" when present. Otherwise, another token_type value that the Client has
|
||||
negotiated with the Authorization Server.
|
||||
@remarks token_type
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.4
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *tokenType;
|
||||
|
||||
/*! @brief ID Token value associated with the authenticated session. Always present for the
|
||||
authorization code grant exchange when OpenID Connect is used, optional for responses to
|
||||
access token refresh requests. Note that AppAuth does NOT verify the JWT signature. Users
|
||||
of AppAuth are encouraged to verifying the JWT signature using the validation library of
|
||||
their choosing.
|
||||
@remarks id_token
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse
|
||||
@see http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
@see https://jwt.io
|
||||
@discussion @c OIDIDToken can be used to parse the ID Token and extract the claims. As noted,
|
||||
this class does not verify the JWT signature.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *idToken;
|
||||
|
||||
/*! @brief The refresh token, which can be used to obtain new access tokens using the same
|
||||
authorization grant
|
||||
@remarks refresh_token
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *refreshToken;
|
||||
|
||||
/*! @brief The scope of the access token. OPTIONAL, if identical to the scopes requested, otherwise,
|
||||
REQUIRED.
|
||||
@remarks scope
|
||||
@see https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString *scope;
|
||||
|
||||
/*! @brief Additional parameters returned from the token server.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable)
|
||||
NSDictionary<NSString *, NSObject<NSCopying> *> *additionalParameters;
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. Please use initWithParameters:.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Designated initializer.
|
||||
@param request The serviced request.
|
||||
@param parameters The decoded parameters returned from the Authorization Server.
|
||||
@remarks Known parameters are extracted from the @c parameters parameter and the normative
|
||||
properties are populated. Non-normative parameters are placed in the
|
||||
@c #additionalParameters dictionary.
|
||||
*/
|
||||
- (instancetype)initWithRequest:(OIDTokenRequest *)request
|
||||
parameters:(NSDictionary<NSString *, NSObject<NSCopying> *> *)parameters
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*! @file OIDTokenResponse.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDTokenResponse.h"
|
||||
|
||||
#import "OIDDefines.h"
|
||||
#import "OIDFieldMapping.h"
|
||||
#import "OIDTokenRequest.h"
|
||||
#import "OIDTokenUtilities.h"
|
||||
|
||||
/*! @brief Key used to encode the @c request property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kRequestKey = @"request";
|
||||
|
||||
/*! @brief The key for the @c accessToken property in the incoming parameters and for
|
||||
@c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kAccessTokenKey = @"access_token";
|
||||
|
||||
/*! @brief The key for the @c accessTokenExpirationDate property in the incoming parameters and for
|
||||
@c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kExpiresInKey = @"expires_in";
|
||||
|
||||
/*! @brief The key for the @c tokenType property in the incoming parameters and for
|
||||
@c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kTokenTypeKey = @"token_type";
|
||||
|
||||
/*! @brief The key for the @c idToken property in the incoming parameters and for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kIDTokenKey = @"id_token";
|
||||
|
||||
/*! @brief The key for the @c refreshToken property in the incoming parameters and for
|
||||
@c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kRefreshTokenKey = @"refresh_token";
|
||||
|
||||
/*! @brief The key for the @c scope property in the incoming parameters and for @c NSSecureCoding.
|
||||
*/
|
||||
static NSString *const kScopeKey = @"scope";
|
||||
|
||||
/*! @brief Key used to encode the @c additionalParameters property for @c NSSecureCoding
|
||||
*/
|
||||
static NSString *const kAdditionalParametersKey = @"additionalParameters";
|
||||
|
||||
@implementation OIDTokenResponse
|
||||
|
||||
/*! @brief Returns a mapping of incoming parameters to instance variables.
|
||||
@return A mapping of incoming parameters to instance variables.
|
||||
*/
|
||||
+ (NSDictionary<NSString *, OIDFieldMapping *> *)fieldMap {
|
||||
static NSMutableDictionary<NSString *, OIDFieldMapping *> *fieldMap;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
fieldMap = [NSMutableDictionary dictionary];
|
||||
fieldMap[kAccessTokenKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_accessToken" type:[NSString class]];
|
||||
fieldMap[kExpiresInKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_accessTokenExpirationDate"
|
||||
type:[NSDate class]
|
||||
conversion:[OIDFieldMapping dateSinceNowConversion]];
|
||||
fieldMap[kTokenTypeKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_tokenType" type:[NSString class]];
|
||||
fieldMap[kIDTokenKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_idToken" type:[NSString class]];
|
||||
fieldMap[kRefreshTokenKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_refreshToken" type:[NSString class]];
|
||||
fieldMap[kScopeKey] =
|
||||
[[OIDFieldMapping alloc] initWithName:@"_scope" type:[NSString class]];
|
||||
});
|
||||
return fieldMap;
|
||||
}
|
||||
|
||||
#pragma mark - Initializers
|
||||
|
||||
- (instancetype)init
|
||||
OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:))
|
||||
|
||||
- (instancetype)initWithRequest:(OIDTokenRequest *)request
|
||||
parameters:(NSDictionary<NSString *, NSObject<NSCopying> *> *)parameters {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_request = [request copy];
|
||||
NSDictionary<NSString *, NSObject<NSCopying> *> *additionalParameters =
|
||||
[OIDFieldMapping remainingParametersWithMap:[[self class] fieldMap]
|
||||
parameters:parameters
|
||||
instance:self];
|
||||
_additionalParameters = additionalParameters;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
OIDTokenRequest *request =
|
||||
[aDecoder decodeObjectOfClass:[OIDTokenRequest class] forKey:kRequestKey];
|
||||
self = [self initWithRequest:request parameters:@{ }];
|
||||
if (self) {
|
||||
[OIDFieldMapping decodeWithCoder:aDecoder map:[[self class] fieldMap] instance:self];
|
||||
_additionalParameters = [aDecoder decodeObjectOfClasses:[OIDFieldMapping JSONTypes]
|
||||
forKey:kAdditionalParametersKey];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[OIDFieldMapping encodeWithCoder:aCoder map:[[self class] fieldMap] instance:self];
|
||||
[aCoder encodeObject:_request forKey:kRequestKey];
|
||||
[aCoder encodeObject:_additionalParameters forKey:kAdditionalParametersKey];
|
||||
}
|
||||
|
||||
#pragma mark - NSObject overrides
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"<%@: %p, accessToken: \"%@\", accessTokenExpirationDate: %@, "
|
||||
"tokenType: %@, idToken: \"%@\", refreshToken: \"%@\", "
|
||||
"scope: \"%@\", additionalParameters: %@, request: %@>",
|
||||
NSStringFromClass([self class]),
|
||||
(void *)self,
|
||||
[OIDTokenUtilities redact:_accessToken],
|
||||
_accessTokenExpirationDate,
|
||||
_tokenType,
|
||||
[OIDTokenUtilities redact:_idToken],
|
||||
[OIDTokenUtilities redact:_refreshToken],
|
||||
_scope,
|
||||
_additionalParameters,
|
||||
_request];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@end
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*! @file OIDTokenUtilities.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief Provides data encoding/decoding methods, random string generators, etc.
|
||||
*/
|
||||
@interface OIDTokenUtilities : NSObject
|
||||
|
||||
/*! @internal
|
||||
@brief Unavailable. This class should not be initialized.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/*! @brief Base64url-nopadding encodes the given data.
|
||||
@param data The input data.
|
||||
@return The base64url encoded data as a NSString.
|
||||
@discussion Base64url-nopadding is used in several identity specs such as PKCE and
|
||||
OpenID Connect.
|
||||
*/
|
||||
+ (NSString *)encodeBase64urlNoPadding:(NSData *)data;
|
||||
|
||||
/*! @brief Generates a URL-safe string of random data.
|
||||
@param size The number of random bytes to encode. NB. the length of the output string will be
|
||||
greater than the number of random bytes, due to the URL-safe encoding.
|
||||
@return Random data encoded with base64url.
|
||||
*/
|
||||
+ (nullable NSString *)randomURLSafeStringWithSize:(NSUInteger)size;
|
||||
|
||||
/*! @brief SHA256 hashes the input string.
|
||||
@param inputString The input string.
|
||||
@return The SHA256 data.
|
||||
*/
|
||||
+ (NSData *)sha256:(NSString *)inputString;
|
||||
|
||||
/*! @brief Truncated intput string after first 6 characters followed by ellipses
|
||||
@param inputString The input string.
|
||||
@return Truncated string.
|
||||
*/
|
||||
+ (nullable NSString *)redact:(nullable NSString *)inputString;
|
||||
|
||||
/*! @brief Form url encode the input string by applying application/x-www-form-urlencoded algorithm
|
||||
@param inputString The input string.
|
||||
@return The encoded string.
|
||||
*/
|
||||
+ (NSString*)formUrlEncode:(NSString*)inputString;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*! @file OIDTokenUtilities.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDTokenUtilities.h"
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
/*! @brief String representing the set of characters that are allowed as is for the
|
||||
application/x-www-form-urlencoded encoding algorithm.
|
||||
*/
|
||||
static NSString *const kFormUrlEncodedAllowedCharacters =
|
||||
@" *-._0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
@implementation OIDTokenUtilities
|
||||
|
||||
+ (NSString *)encodeBase64urlNoPadding:(NSData *)data {
|
||||
NSString *base64string = [data base64EncodedStringWithOptions:0];
|
||||
// converts base64 to base64url
|
||||
base64string = [base64string stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
|
||||
base64string = [base64string stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
|
||||
// strips padding
|
||||
base64string = [base64string stringByReplacingOccurrencesOfString:@"=" withString:@""];
|
||||
return base64string;
|
||||
}
|
||||
|
||||
+ (nullable NSString *)randomURLSafeStringWithSize:(NSUInteger)size {
|
||||
NSMutableData *randomData = [NSMutableData dataWithLength:size];
|
||||
int result = SecRandomCopyBytes(kSecRandomDefault, randomData.length, randomData.mutableBytes);
|
||||
if (result != 0) {
|
||||
return nil;
|
||||
}
|
||||
return [[self class] encodeBase64urlNoPadding:randomData];
|
||||
}
|
||||
|
||||
+ (NSData *)sha256:(NSString *)inputString {
|
||||
NSData *verifierData = [inputString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSMutableData *sha256Verifier = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
|
||||
CC_SHA256(verifierData.bytes, (CC_LONG)verifierData.length, sha256Verifier.mutableBytes);
|
||||
return sha256Verifier;
|
||||
}
|
||||
|
||||
+ (NSString *)redact:(NSString *)inputString {
|
||||
if (inputString == nil) {
|
||||
return nil;
|
||||
}
|
||||
switch(inputString.length){
|
||||
case 0:
|
||||
return @"";
|
||||
case 1 ... 8:
|
||||
return @"[redacted]";
|
||||
case 9:
|
||||
default:
|
||||
return [[inputString substringToIndex:6] stringByAppendingString:@"...[redacted]"];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString*)formUrlEncode:(NSString*)inputString {
|
||||
// https://www.w3.org/TR/html5/sec-forms.html#application-x-www-form-urlencoded-encoding-algorithm
|
||||
// Following the spec from the above link, application/x-www-form-urlencoded percent encode all
|
||||
// the characters except *-._A-Za-z0-9
|
||||
// Space character is replaced by + in the resulting bytes sequence
|
||||
if (inputString.length == 0) {
|
||||
return inputString;
|
||||
}
|
||||
NSCharacterSet *allowedCharacters =
|
||||
[NSCharacterSet characterSetWithCharactersInString:kFormUrlEncodedAllowedCharacters];
|
||||
// Percent encode all characters not present in the provided set.
|
||||
NSString *encodedString =
|
||||
[inputString stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];
|
||||
// Replace occurences of space by '+' character
|
||||
return [encodedString stringByReplacingOccurrencesOfString:@" " withString:@"+"];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,93 @@
|
||||
/*! @file OIDURLQueryComponent.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 OIDAuthorizationRequest;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief If set to YES, will force the iOS 7-only code for @c OIDURLQueryComponent to be used,
|
||||
even on non-iOS 7 devices and simulators. Useful for testing the iOS 7 code paths on the
|
||||
simulator. Defaults to NO.
|
||||
*/
|
||||
extern BOOL gOIDURLQueryComponentForceIOS7Handling;
|
||||
|
||||
/*! @brief A utility class for creating and parsing URL query components encoded with the
|
||||
application/x-www-form-urlencoded format.
|
||||
@description Supports application/x-www-form-urlencoded encoding and decoding, specifically
|
||||
'+' is replaced with space before percent decoding. For encoding, simply percent encodes
|
||||
space, as this is valid application/x-www-form-urlencoded.
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.2
|
||||
@see https://tools.ietf.org/html/rfc6749#section-4.1.3
|
||||
@see https://tools.ietf.org/html/rfc6749#appendix-B
|
||||
@see https://url.spec.whatwg.org/#urlencoded-parsing
|
||||
*/
|
||||
@interface OIDURLQueryComponent : NSObject
|
||||
|
||||
/*! @brief The parameter names in the query.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray<NSString *> *parameters;
|
||||
|
||||
/*! @brief The parameters represented as a dictionary.
|
||||
@remarks All values are @c NSString except for parameters which contain multiple values, in
|
||||
which case the value is an @c NSArray<NSString *> *.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSDictionary<NSString *, NSObject<NSCopying> *> *dictionaryValue;
|
||||
|
||||
/*! @brief Creates an @c OIDURLQueryComponent by parsing the query string in a URL.
|
||||
@param URL The URL from which to extract a query component.
|
||||
*/
|
||||
- (nullable instancetype)initWithURL:(NSURL *)URL;
|
||||
|
||||
/*! @brief The value (or values) for a named parameter in the query.
|
||||
@param parameter The parameter name. Case sensitive.
|
||||
@return The value (or values) for a named parameter in the query.
|
||||
*/
|
||||
- (NSArray<NSString *> *)valuesForParameter:(NSString *)parameter;
|
||||
|
||||
/*! @brief Adds a parameter value to the query.
|
||||
@param parameter The name of the parameter. Case sensitive.
|
||||
@param value The value to add.
|
||||
*/
|
||||
- (void)addParameter:(NSString *)parameter value:(NSString *)value;
|
||||
|
||||
/*! @brief Adds multiple parameters with associated values to the query.
|
||||
@param parameters The parameter name value pairs to add to the query.
|
||||
*/
|
||||
- (void)addParameters:(NSDictionary<NSString *, NSString *> *)parameters;
|
||||
|
||||
/*! @param URL The URL to add the query component to.
|
||||
@return The original URL with the query component replaced by the parameters from this query.
|
||||
*/
|
||||
- (NSURL *)URLByReplacingQueryInURL:(NSURL *)URL;
|
||||
|
||||
/*! @brief Builds an x-www-form-urlencoded string representing the parameters.
|
||||
@return The x-www-form-urlencoded string representing the parameters.
|
||||
*/
|
||||
- (NSString *)URLEncodedParameters;
|
||||
|
||||
/*! @brief A NSMutableCharacterSet containing allowed characters in URL parameter values (that is
|
||||
the "value" part of "?key=value"). This has less allowed characters than
|
||||
@c URLQueryAllowedCharacterSet, as the query component includes both the key & value.
|
||||
*/
|
||||
+ (NSMutableCharacterSet *)URLParamValueAllowedCharacters;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
/*! @file OIDURLQueryComponent.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDURLQueryComponent.h"
|
||||
|
||||
BOOL gOIDURLQueryComponentForceIOS7Handling = NO;
|
||||
|
||||
/*! @brief String representing the set of characters that are valid for the URL query
|
||||
(per @ NSCharacterSet.URLQueryAllowedCharacterSet), but are disallowed in URL query
|
||||
parameters and values.
|
||||
*/
|
||||
static NSString *const kQueryStringParamAdditionalDisallowedCharacters = @"=&+";
|
||||
|
||||
@implementation OIDURLQueryComponent {
|
||||
/*! @brief A dictionary of parameter names and values representing the contents of the query.
|
||||
*/
|
||||
NSMutableDictionary<NSString *, NSMutableArray<NSString *> *> *_parameters;
|
||||
}
|
||||
|
||||
- (nullable instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_parameters = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithURL:(NSURL *)URL {
|
||||
self = [self init];
|
||||
if (self) {
|
||||
if (@available(iOS 8.0, macOS 10.10, *)) {
|
||||
// If NSURLQueryItem is available, use it for deconstructing the new URL. (iOS 8+)
|
||||
if (!gOIDURLQueryComponentForceIOS7Handling) {
|
||||
NSURLComponents *components =
|
||||
[NSURLComponents componentsWithURL:URL resolvingAgainstBaseURL:NO];
|
||||
// As OAuth uses application/x-www-form-urlencoded encoding, interprets '+' as a space
|
||||
// in addition to regular percent decoding. https://url.spec.whatwg.org/#urlencoded-parsing
|
||||
components.percentEncodedQuery =
|
||||
[components.percentEncodedQuery stringByReplacingOccurrencesOfString:@"+"
|
||||
withString:@"%20"];
|
||||
// NB. @c queryItems are already percent decoded
|
||||
NSArray<NSURLQueryItem *> *queryItems = components.queryItems;
|
||||
for (NSURLQueryItem *queryItem in queryItems) {
|
||||
[self addParameter:queryItem.name value:queryItem.value];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback for iOS 7
|
||||
NSString *query = URL.query;
|
||||
// As OAuth uses application/x-www-form-urlencoded encoding, interprets '+' as a space
|
||||
// in addition to regular percent decoding. https://url.spec.whatwg.org/#urlencoded-parsing
|
||||
query = [query stringByReplacingOccurrencesOfString:@"+" withString:@"%20"];
|
||||
|
||||
NSArray<NSString *> *queryParts = [query componentsSeparatedByString:@"&"];
|
||||
for (NSString *queryPart in queryParts) {
|
||||
NSRange equalsRange = [queryPart rangeOfString:@"="];
|
||||
if (equalsRange.location == NSNotFound) {
|
||||
continue;
|
||||
}
|
||||
NSString *name = [queryPart substringToIndex:equalsRange.location];
|
||||
name = name.stringByRemovingPercentEncoding;
|
||||
NSString *value = [queryPart substringFromIndex:equalsRange.location + equalsRange.length];
|
||||
value = value.stringByRemovingPercentEncoding;
|
||||
[self addParameter:name value:value];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)parameters {
|
||||
return _parameters.allKeys;
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, NSObject<NSCopying> *> *)dictionaryValue {
|
||||
// This method will flatten arrays in our @c _parameters' values if only one value exists.
|
||||
NSMutableDictionary<NSString *, NSObject<NSCopying> *> *values = [NSMutableDictionary dictionary];
|
||||
for (NSString *parameter in _parameters.allKeys) {
|
||||
NSArray<NSString *> *value = _parameters[parameter];
|
||||
if (value.count == 1) {
|
||||
values[parameter] = [value.firstObject copy];
|
||||
} else {
|
||||
values[parameter] = [value copy];
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)valuesForParameter:(NSString *)parameter {
|
||||
return _parameters[parameter];
|
||||
}
|
||||
|
||||
- (void)addParameter:(NSString *)parameter value:(NSString *)value {
|
||||
NSMutableArray<NSString *> *parameterValues = _parameters[parameter];
|
||||
if (!parameterValues) {
|
||||
parameterValues = [NSMutableArray array];
|
||||
_parameters[parameter] = parameterValues;
|
||||
}
|
||||
[parameterValues addObject:value];
|
||||
}
|
||||
|
||||
- (void)addParameters:(NSDictionary<NSString *, NSString *> *)parameters {
|
||||
for (NSString *parameterName in parameters.allKeys) {
|
||||
[self addParameter:parameterName value:parameters[parameterName]];
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Builds a query items array that can be set to @c NSURLComponents.queryItems
|
||||
@discussion The parameter names and values are NOT URL encoded.
|
||||
@return An array of unencoded @c NSURLQueryItem objects.
|
||||
*/
|
||||
- (NSMutableArray<NSURLQueryItem *> *)queryItems NS_AVAILABLE(10.10, 8.0) {
|
||||
NSMutableArray<NSURLQueryItem *> *queryParameters = [NSMutableArray array];
|
||||
for (NSString *parameterName in _parameters.allKeys) {
|
||||
NSArray<NSString *> *values = _parameters[parameterName];
|
||||
for (NSString *value in values) {
|
||||
NSURLQueryItem *item = [NSURLQueryItem queryItemWithName:parameterName value:value];
|
||||
[queryParameters addObject:item];
|
||||
}
|
||||
}
|
||||
return queryParameters;
|
||||
}
|
||||
|
||||
+ (NSMutableCharacterSet *)URLParamValueAllowedCharacters {
|
||||
// Starts with the standard URL-allowed character set.
|
||||
NSMutableCharacterSet *allowedParamCharacters =
|
||||
[[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
|
||||
// Removes additional characters we don't want to see in the query component.
|
||||
[allowedParamCharacters removeCharactersInString:kQueryStringParamAdditionalDisallowedCharacters];
|
||||
return allowedParamCharacters;
|
||||
}
|
||||
|
||||
/*! @brief Builds a query string that can be set to @c NSURLComponents.percentEncodedQuery
|
||||
@discussion This string is percent encoded, and shouldn't be used with
|
||||
@c NSURLComponents.query.
|
||||
@return An percentage encoded query string.
|
||||
*/
|
||||
- (NSString *)percentEncodedQueryString {
|
||||
NSMutableArray<NSString *> *parameterizedValues = [NSMutableArray array];
|
||||
|
||||
// Starts with the standard URL-allowed character set.
|
||||
NSMutableCharacterSet *allowedParamCharacters = [[self class] URLParamValueAllowedCharacters];
|
||||
|
||||
for (NSString *parameterName in _parameters.allKeys) {
|
||||
NSString *encodedParameterName =
|
||||
[parameterName stringByAddingPercentEncodingWithAllowedCharacters:allowedParamCharacters];
|
||||
|
||||
NSArray<NSString *> *values = _parameters[parameterName];
|
||||
for (NSString *value in values) {
|
||||
NSString *encodedValue =
|
||||
[value stringByAddingPercentEncodingWithAllowedCharacters:allowedParamCharacters];
|
||||
NSString *parameterizedValue =
|
||||
[NSString stringWithFormat:@"%@=%@", encodedParameterName, encodedValue];
|
||||
[parameterizedValues addObject:parameterizedValue];
|
||||
}
|
||||
}
|
||||
|
||||
NSString *queryString = [parameterizedValues componentsJoinedByString:@"&"];
|
||||
return queryString;
|
||||
}
|
||||
|
||||
- (NSString *)URLEncodedParameters {
|
||||
// If NSURLQueryItem is available, uses it for constructing the encoded parameters. (iOS 8+)
|
||||
if (@available(iOS 8.0, macOS 10.10, *)) {
|
||||
if (!gOIDURLQueryComponentForceIOS7Handling) {
|
||||
NSURLComponents *components = [[NSURLComponents alloc] init];
|
||||
components.queryItems = [self queryItems];
|
||||
NSString *encodedQuery = components.percentEncodedQuery;
|
||||
// NSURLComponents.percentEncodedQuery creates a validly escaped URL query component, but
|
||||
// doesn't encode the '+' leading to potential ambiguity with application/x-www-form-urlencoded
|
||||
// encoding. Percent encodes '+' to avoid this ambiguity.
|
||||
encodedQuery = [encodedQuery stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"];
|
||||
return encodedQuery;
|
||||
}
|
||||
}
|
||||
|
||||
// else, falls back to building query string manually (iOS 7)
|
||||
return [self percentEncodedQueryString];
|
||||
}
|
||||
|
||||
- (NSURL *)URLByReplacingQueryInURL:(NSURL *)URL {
|
||||
NSURLComponents *components =
|
||||
[NSURLComponents componentsWithURL:URL resolvingAgainstBaseURL:NO];
|
||||
|
||||
// Replaces encodedQuery component
|
||||
NSString *queryString = [self URLEncodedParameters];
|
||||
components.percentEncodedQuery = queryString;
|
||||
|
||||
NSURL *URLWithParameters = components.URL;
|
||||
return URLWithParameters;
|
||||
}
|
||||
|
||||
#pragma mark - NSObject overrides
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"<%@: %p, parameters: %@>",
|
||||
NSStringFromClass([self class]),
|
||||
(void *)self,
|
||||
_parameters];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,40 @@
|
||||
/*! @file OIDURLSessionProvider.h
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*! @brief A NSURLSession provider that allows clients to provide custom implementation
|
||||
for NSURLSession
|
||||
*/
|
||||
@interface OIDURLSessionProvider : NSObject
|
||||
|
||||
/*! @brief Obtains the current @c NSURLSession; using the +[NSURLSession sharedSession] if
|
||||
no custom implementation is provided.
|
||||
@return NSURLSession object to be used for making network requests.
|
||||
*/
|
||||
+ (NSURLSession *)session;
|
||||
|
||||
/*! @brief Allows library consumers to change the @c NSURLSession instance used to make
|
||||
network requests.
|
||||
@param session The @c NSURLSession instance that should be used for making network requests.
|
||||
*/
|
||||
+ (void)setSession:(NSURLSession *)session;
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,39 @@
|
||||
/*! @file OIDURLSessionProvider.m
|
||||
@brief AppAuth iOS SDK
|
||||
@copyright
|
||||
Copyright 2015 Google Inc. 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 "OIDURLSessionProvider.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSURLSession *__nullable gURLSession;
|
||||
|
||||
@implementation OIDURLSessionProvider
|
||||
|
||||
+ (NSURLSession *)session {
|
||||
if (!gURLSession) {
|
||||
gURLSession = [NSURLSession sharedSession];
|
||||
}
|
||||
return gURLSession;
|
||||
}
|
||||
|
||||
+ (void)setSession:(NSURLSession *)session {
|
||||
NSAssert(session, @"Parameter: |session| must be non-nil.");
|
||||
gURLSession = session;
|
||||
}
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
Reference in New Issue
Block a user