PPOTAuthorizationRequest.m 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. //
  2. // PPOTAuthorizationRequest.m
  3. // PayPalOneTouch
  4. //
  5. // Copyright © 2015 PayPal, Inc. All rights reserved.
  6. //
  7. #import "PPOTRequest_Internal.h"
  8. #import "PPOTAppSwitchUtil.h"
  9. #import "PPOTConfiguration.h"
  10. #import "PPOTOAuth2AppSwitchRequest.h"
  11. #import "PPOTOAuth2BrowserSwitchRequest.h"
  12. #if __has_include("PayPalUtils.h")
  13. #import "PPOTDevice.h"
  14. #import "PPOTMacros.h"
  15. #import "PPOTEncryptionHelper.h"
  16. #import "PPOTString.h"
  17. #else
  18. #import <PayPalUtils/PPOTDevice.h>
  19. #import <PayPalUtils/PPOTMacros.h>
  20. #import <PayPalUtils/PPOTEncryptionHelper.h>
  21. #import <PayPalUtils/PPOTString.h>
  22. #endif
  23. #define PPRequestEnvironmentDevelop @"develop"
  24. @interface PPOTAuthorizationRequest ()
  25. /// Set of requested scope-values.
  26. /// Available scope-values are listed at https://developer.paypal.com/webapps/developer/docs/integration/direct/identity/attributes/
  27. @property (nonatomic, readwrite) NSSet *scopeValues;
  28. /// The URL of the merchant's privacy policy
  29. @property (nonatomic, readwrite) NSURL *privacyURL;
  30. /// The URL of the merchant's user agreement
  31. @property (nonatomic, readwrite) NSURL *agreementURL;
  32. @end
  33. #pragma mark - PPOTAuthorizationRequest implementation
  34. @implementation PPOTAuthorizationRequest
  35. - (instancetype)initWithScopeValues:(NSSet *)scopeValues
  36. privacyURL:(NSURL *)privacyURL
  37. agreementURL:(NSURL *)agreementURL
  38. clientID:(NSString *)clientID
  39. environment:(NSString *)environment
  40. callbackURLScheme:(NSString *)callbackURLScheme {
  41. if (scopeValues.count == 0) {
  42. PPSDKLog(@"scope is required.");
  43. return nil;
  44. }
  45. if (privacyURL == nil) {
  46. PPSDKLog(@"merchantPrivacyPolicyURL is required.");
  47. return nil;
  48. }
  49. if (agreementURL == nil) {
  50. PPSDKLog(@"merchantUserAgreementURL is required.");
  51. return nil;
  52. }
  53. self = [super initWithClientID:clientID environment:environment callbackURLScheme:callbackURLScheme];
  54. if (self) {
  55. _scopeValues = scopeValues;
  56. _privacyURL = privacyURL;
  57. _agreementURL = agreementURL;
  58. }
  59. return self;
  60. }
  61. + (instancetype)requestWithScopeValues:(NSSet *)scopeValues
  62. privacyURL:(NSURL *)privacyURL
  63. agreementURL:(NSURL *)agreementURL
  64. clientID:(NSString *)clientID
  65. environment:(NSString *)environment
  66. callbackURLScheme:(NSString *)callbackURLScheme {
  67. PPOTAuthorizationRequest *request = [[PPOTAuthorizationRequest alloc] initWithScopeValues:scopeValues
  68. privacyURL:privacyURL
  69. agreementURL:agreementURL
  70. clientID:clientID
  71. environment:environment
  72. callbackURLScheme:callbackURLScheme];
  73. return request;
  74. }
  75. #pragma mark - add subclass-specific info to appSwitchRequest
  76. - (PPOTSwitchRequest *)getAppSwitchRequestForConfigurationRecipe:(PPOTConfigurationRecipe *)configurationRecipe {
  77. PPOTOAuth2SwitchRequest *appSwitchRequest = nil;
  78. switch (configurationRecipe.target) {
  79. case PPOTRequestTargetOnDeviceApplication: {
  80. appSwitchRequest = [[PPOTOAuth2AppSwitchRequest alloc] initWithProtocolVersion:configurationRecipe.protocolVersion
  81. appGuid:[PPOTDevice appropriateIdentifier]
  82. clientID:self.clientID
  83. environment:self.environment
  84. callbackURLScheme:self.callbackURLScheme];
  85. break;
  86. }
  87. case PPOTRequestTargetBrowser: {
  88. PPOTOAuth2BrowserSwitchRequest *browserSwitchRequest = [[PPOTOAuth2BrowserSwitchRequest alloc] initWithProtocolVersion:configurationRecipe.protocolVersion
  89. appGuid:[PPOTDevice appropriateIdentifier]
  90. clientID:self.clientID
  91. environment:self.environment
  92. callbackURLScheme:self.callbackURLScheme];
  93. PPOTConfigurationOAuthRecipe *ooauthRecipe = (PPOTConfigurationOAuthRecipe *)configurationRecipe;
  94. NSString *relevantEnvironment = nil;
  95. if (ooauthRecipe.endpoints[self.environment]) {
  96. relevantEnvironment = self.environment;
  97. } else if (![self.environment isEqualToString:PPRequestEnvironmentProduction] &&
  98. ![self.environment isEqualToString:PPRequestEnvironmentNoNetwork] &&
  99. ooauthRecipe.endpoints[PPRequestEnvironmentDevelop]) {
  100. relevantEnvironment = PPRequestEnvironmentDevelop;
  101. } else if (ooauthRecipe.endpoints[PPRequestEnvironmentProduction]) {
  102. relevantEnvironment = PPRequestEnvironmentProduction;
  103. }
  104. if (relevantEnvironment) { // this is merely a sanity check; presence of "live" was guaranteed in [PPOTConfigurationOAuthRecipe initWithDictionary:]
  105. PPOTConfigurationRecipeEndpoint *endpoint = ooauthRecipe.endpoints[relevantEnvironment];
  106. browserSwitchRequest.endpoint = endpoint.url;
  107. browserSwitchRequest.keyID = endpoint.certificateSerialNumber;
  108. browserSwitchRequest.certificate = [[NSData alloc] initWithBase64EncodedString:endpoint.base64EncodedCertificate
  109. options:NSDataBase64DecodingIgnoreUnknownCharacters];
  110. browserSwitchRequest.encryptionKey = [PPOTString hexStringFromData:[PPOTEncryptionHelper generate256BitKey]];
  111. browserSwitchRequest.additionalPayloadAttributes = self.additionalPayloadAttributes;
  112. appSwitchRequest = browserSwitchRequest;
  113. }
  114. break;
  115. }
  116. default: {
  117. break;
  118. }
  119. }
  120. if (appSwitchRequest) {
  121. appSwitchRequest.targetAppURLScheme = configurationRecipe.targetAppURLScheme;
  122. appSwitchRequest.responseType = PPAppSwitchResponseTypeAuthorizationCode;
  123. appSwitchRequest.scope = [self.scopeValues allObjects];
  124. // mandatory field
  125. appSwitchRequest.merchantName = [PPOTAppSwitchUtil bundleName];
  126. appSwitchRequest.privacyURL = [self.privacyURL absoluteString];
  127. appSwitchRequest.agreementURL = [self.agreementURL absoluteString];
  128. }
  129. return appSwitchRequest;
  130. }
  131. #pragma mark - configuration methods
  132. - (BOOL)scopeIsSupportedByConfigurationRecipe:(PPOTConfigurationOAuthRecipe *)configurationRecipe {
  133. if ([configurationRecipe.scope count] == 1 && [configurationRecipe.scope containsObject:@"*"]) {
  134. return YES;
  135. }
  136. return ([self.scopeValues isSubsetOfSet:configurationRecipe.scope]);
  137. }
  138. - (void)getAppropriateConfigurationRecipe:(void (^)(PPOTConfigurationRecipe *configurationRecipe))completionBlock {
  139. PPAssert(completionBlock, @"getAppropriateConfigurationRecipe: completionBlock is required");
  140. PPOTConfiguration *currentConfiguration = [PPOTConfiguration getCurrentConfiguration];
  141. PPOTConfigurationOAuthRecipe *bestConfigurationRecipe = nil;
  142. for (PPOTConfigurationOAuthRecipe *configurationRecipe in currentConfiguration.prioritizedOAuthRecipes) {
  143. if (![self scopeIsSupportedByConfigurationRecipe:configurationRecipe]) {
  144. continue;
  145. }
  146. if (![self isConfigurationRecipeTargetSupported:configurationRecipe] ||
  147. ![self isConfigurationRecipeLocaleSupported:configurationRecipe]) {
  148. continue;
  149. }
  150. bestConfigurationRecipe = configurationRecipe;
  151. break;
  152. }
  153. completionBlock(bestConfigurationRecipe);
  154. }
  155. @end