From 73d65981f312ec69702095252d9bd6406f40abd7 Mon Sep 17 00:00:00 2001 From: Zsika Phillip Date: Mon, 6 Nov 2017 05:53:03 -0800 Subject: [PATCH 001/204] Fixes a few typos in preparation for release --- Source/OIDURLSessionProvider.h | 12 ++++++------ UnitTests/OIDURLSessionProviderTests.m | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/OIDURLSessionProvider.h b/Source/OIDURLSessionProvider.h index 814c669f9..28e911696 100644 --- a/Source/OIDURLSessionProvider.h +++ b/Source/OIDURLSessionProvider.h @@ -21,19 +21,19 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief A NSURLSession provider that allows clients to provide custom implementation - for NSURLSession + for NSURLSession */ @interface OIDURLSessionProvider : NSObject /*! @brief Obtains the current @c NSURLSession; using the +[NSURLSession sharedSession] if - no custom implementation provided - @return NSURLSession object to be used for making network requests + 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 used to create make - network requests - @param session The @c NSURLSession instance that should be used for making network requests +/*! @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 diff --git a/UnitTests/OIDURLSessionProviderTests.m b/UnitTests/OIDURLSessionProviderTests.m index 193d7cddf..4bd02f361 100644 --- a/UnitTests/OIDURLSessionProviderTests.m +++ b/UnitTests/OIDURLSessionProviderTests.m @@ -23,7 +23,7 @@ @interface OIDURLSessionProviderTests : XCTestCase @end -/*! @brief Unit tests for @c OIDURLSessionProvider +/*! @brief Unit tests for @c OIDURLSessionProvider. */ @implementation OIDURLSessionProviderTests From 26ead39e9cc28d010c35061af31f2120db4d4c73 Mon Sep 17 00:00:00 2001 From: Kiran Thorat Date: Sun, 12 Nov 2017 22:50:53 +1100 Subject: [PATCH 002/204] Added iOS example app using Swift --- .../Example-iOS_Swift-Carthage/.gitignore | 40 ++ Examples/Example-iOS_Swift-Carthage/Cartfile | 2 + .../Cartfile.resolved | 1 + .../Example.xcodeproj/project.pbxproj | 365 ++++++++++++ .../contents.xcworkspacedata | 7 + .../Source/AppAuthExampleViewController.swift | 529 ++++++++++++++++++ .../Source/AppDelegate.swift | 44 ++ .../AppIcon.appiconset/Contents.json | 98 ++++ .../Source/Base.lproj/LaunchScreen.storyboard | 25 + .../Source/Base.lproj/Main.storyboard | 170 ++++++ .../Source/Info.plist | 56 ++ 11 files changed, 1337 insertions(+) create mode 100644 Examples/Example-iOS_Swift-Carthage/.gitignore create mode 100644 Examples/Example-iOS_Swift-Carthage/Cartfile create mode 100644 Examples/Example-iOS_Swift-Carthage/Cartfile.resolved create mode 100644 Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj create mode 100644 Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/Base.lproj/LaunchScreen.storyboard create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/Base.lproj/Main.storyboard create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/Info.plist diff --git a/Examples/Example-iOS_Swift-Carthage/.gitignore b/Examples/Example-iOS_Swift-Carthage/.gitignore new file mode 100644 index 000000000..0d5a93889 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/.gitignore @@ -0,0 +1,40 @@ +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +# Pods are ignored in the samples as all Pods & their dependencies are either +# development Pods (this repo) or sourced from repos in the same organization. +# Generally we recommend committing Pod artifacts to version control, read about +# the pros & cons here: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +Pods + +# Carthage +# +# Add these lines if you want to avoid checking in source code from Carthage +# dependencies. Generally we recommend committing Carthage artifacts to version +# control. +Carthage/Checkouts +Carthage/Build diff --git a/Examples/Example-iOS_Swift-Carthage/Cartfile b/Examples/Example-iOS_Swift-Carthage/Cartfile new file mode 100644 index 000000000..5cb5fda23 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Cartfile @@ -0,0 +1,2 @@ +github "openid/AppAuth-iOS" "master" + diff --git a/Examples/Example-iOS_Swift-Carthage/Cartfile.resolved b/Examples/Example-iOS_Swift-Carthage/Cartfile.resolved new file mode 100644 index 000000000..782a3e9d9 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Cartfile.resolved @@ -0,0 +1 @@ +github "openid/AppAuth-iOS" "96ea3e3dc691d76d814015ce7c47a6c1ccaf8d10" diff --git a/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj new file mode 100644 index 000000000..825c2a30f --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj @@ -0,0 +1,365 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 9F265BD11F9AC69300DC14BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9F265BC91F9AC69300DC14BF /* Assets.xcassets */; }; + 9F265BD31F9AC69300DC14BF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9F265BCB1F9AC69300DC14BF /* LaunchScreen.storyboard */; }; + 9F265BD41F9AC69300DC14BF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9F265BCD1F9AC69300DC14BF /* Main.storyboard */; }; + 9F265BD51F9AC69300DC14BF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F265BCF1F9AC69300DC14BF /* AppDelegate.swift */; }; + 9F265BDA1F9AE50400DC14BF /* AppAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F265BD91F9AE50400DC14BF /* AppAuth.framework */; }; + 9FD378231FB7C6F800436204 /* AppAuthExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD378221FB7C6F800436204 /* AppAuthExampleViewController.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 9F265B971F9AC5D600DC14BF /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 9F265BC91F9AC69300DC14BF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 9F265BCC1F9AC69300DC14BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 9F265BCE1F9AC69300DC14BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 9F265BCF1F9AC69300DC14BF /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 9F265BD01F9AC69300DC14BF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9F265BD91F9AE50400DC14BF /* AppAuth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppAuth.framework; path = Carthage/Build/iOS/AppAuth.framework; sourceTree = ""; }; + 9FD378221FB7C6F800436204 /* AppAuthExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppAuthExampleViewController.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 9F265B941F9AC5D600DC14BF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9F265BDA1F9AE50400DC14BF /* AppAuth.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9F265B8E1F9AC5D600DC14BF = { + isa = PBXGroup; + children = ( + 9F265BC81F9AC69300DC14BF /* Source */, + 9F265BD81F9AE4CA00DC14BF /* Frameworks */, + 9F265B981F9AC5D600DC14BF /* Products */, + ); + sourceTree = ""; + }; + 9F265B981F9AC5D600DC14BF /* Products */ = { + isa = PBXGroup; + children = ( + 9F265B971F9AC5D600DC14BF /* Example.app */, + ); + name = Products; + sourceTree = ""; + }; + 9F265BC81F9AC69300DC14BF /* Source */ = { + isa = PBXGroup; + children = ( + 9F265BCF1F9AC69300DC14BF /* AppDelegate.swift */, + 9FD378221FB7C6F800436204 /* AppAuthExampleViewController.swift */, + 9F265BCB1F9AC69300DC14BF /* LaunchScreen.storyboard */, + 9F265BCD1F9AC69300DC14BF /* Main.storyboard */, + 9F265BC91F9AC69300DC14BF /* Assets.xcassets */, + 9F265BD01F9AC69300DC14BF /* Info.plist */, + ); + path = Source; + sourceTree = ""; + }; + 9F265BD81F9AE4CA00DC14BF /* Frameworks */ = { + isa = PBXGroup; + children = ( + 9F265BD91F9AE50400DC14BF /* AppAuth.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 9F265B961F9AC5D600DC14BF /* Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9F265BBF1F9AC5D600DC14BF /* Build configuration list for PBXNativeTarget "Example" */; + buildPhases = ( + 9F265B931F9AC5D600DC14BF /* Sources */, + 9F265B941F9AC5D600DC14BF /* Frameworks */, + 9F265B951F9AC5D600DC14BF /* Resources */, + 9F265BDB1F9AE52C00DC14BF /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Example; + productName = Example; + productReference = 9F265B971F9AC5D600DC14BF /* Example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 9F265B8F1F9AC5D600DC14BF /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0900; + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = "Google Inc."; + TargetAttributes = { + 9F265B961F9AC5D600DC14BF = { + CreatedOnToolsVersion = 9.0.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 9F265B921F9AC5D600DC14BF /* Build configuration list for PBXProject "Example" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 9F265B8E1F9AC5D600DC14BF; + productRefGroup = 9F265B981F9AC5D600DC14BF /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 9F265B961F9AC5D600DC14BF /* Example */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 9F265B951F9AC5D600DC14BF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9F265BD41F9AC69300DC14BF /* Main.storyboard in Resources */, + 9F265BD11F9AC69300DC14BF /* Assets.xcassets in Resources */, + 9F265BD31F9AC69300DC14BF /* LaunchScreen.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 9F265BDB1F9AE52C00DC14BF /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/AppAuth.framework", + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 9F265B931F9AC5D600DC14BF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9FD378231FB7C6F800436204 /* AppAuthExampleViewController.swift in Sources */, + 9F265BD51F9AC69300DC14BF /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 9F265BCB1F9AC69300DC14BF /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 9F265BCC1F9AC69300DC14BF /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; + 9F265BCD1F9AC69300DC14BF /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 9F265BCE1F9AC69300DC14BF /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 9F265BBD1F9AC5D600DC14BF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 9F265BBE1F9AC5D600DC14BF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 9F265BC01F9AC5D600DC14BF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = Source/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauth.Example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 9F265BC11F9AC5D600DC14BF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = Source/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauth.Example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 9F265B921F9AC5D600DC14BF /* Build configuration list for PBXProject "Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9F265BBD1F9AC5D600DC14BF /* Debug */, + 9F265BBE1F9AC5D600DC14BF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9F265BBF1F9AC5D600DC14BF /* Build configuration list for PBXNativeTarget "Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9F265BC01F9AC5D600DC14BF /* Debug */, + 9F265BC11F9AC5D600DC14BF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 9F265B8F1F9AC5D600DC14BF /* Project object */; +} diff --git a/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..6d2a51bbd --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift new file mode 100644 index 000000000..6bb464ff1 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift @@ -0,0 +1,529 @@ +// +// AppAuthExampleViewController.swift +// +// Copyright (c) 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import AppAuth +import UIKit + +typealias PostRegistrationCallback = (_ configuration: OIDServiceConfiguration?, _ registrationResponse: OIDRegistrationResponse?) -> Void + +/** + The OIDC issuer from which the configuration will be discovered. +*/ +let kIssuer: String = "https://issuer.example.com"; + +/** + The OAuth client ID. + + For client configuration instructions, see the [README](https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md). + Set to nil to use dynamic registration with this example. +*/ +let kClientID: String? = "YOUR_CLIENT_ID"; + +/** + The OAuth redirect URI for the client @c kClientID. + + For client configuration instructions, see the [README](https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md). +*/ +let kRedirectURI: String = "com.example.app:/oauth2redirect/example-provider"; + +/** + NSCoding key for the authState property. +*/ +let kAppAuthExampleAuthStateKey: String = "authState"; + + + +class AppAuthExampleViewController: UIViewController { + + @IBOutlet private weak var authAutoButton: UIButton! + @IBOutlet private weak var authManual: UIButton! + @IBOutlet private weak var codeExchangeButton: UIButton! + @IBOutlet private weak var userinfoButton: UIButton! + @IBOutlet private weak var logTextView: UITextView! + @IBOutlet private weak var trashButton: UIBarButtonItem! + + private var authState: OIDAuthState? + + override func viewDidLoad() { + super.viewDidLoad() + + self.validateOAuthConfiguration() + + self.loadState() + self.updateUI() + } +} + +extension AppAuthExampleViewController { + + func validateOAuthConfiguration() { + + // The example needs to be configured with your own client details. + // See: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md + + assert(kIssuer != "https://issuer.example.com", + "Update kIssuer with your own issuer.\n" + + "Instructions: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md"); + + assert(kClientID != "YOUR_CLIENT_ID", + "Update kClientID with your own client ID.\n" + + "Instructions: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md"); + + assert(kRedirectURI != "com.example.app:/oauth2redirect/example-provider", + "Update kRedirectURI with your own redirect URI.\n" + + "Instructions: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md"); + + // verifies that the custom URI scheme has been updated in the Info.plist + guard let urlTypes: [AnyObject] = Bundle.main.object(forInfoDictionaryKey: "CFBundleURLTypes") as? [AnyObject], urlTypes.count > 0 else { + assertionFailure("No custom URI scheme has been configured for the project.") + return + } + + guard let items = urlTypes[0] as? [String: AnyObject], + let urlSchemes = items["CFBundleURLSchemes"] as? [AnyObject], urlSchemes.count > 0 else { + assertionFailure("No custom URI scheme has been configured for the project.") + return + } + + guard let urlScheme = urlSchemes[0] as? String else { + assertionFailure("No custom URI scheme has been configured for the project.") + return + } + + assert(urlScheme != "com.example.app", + "Configure the URI scheme in Info.plist (URL Types -> Item 0 -> URL Schemes -> Item 0) " + + "with the scheme of your redirect URI. Full instructions: " + + "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md") + } + +} + +//MARK: IBActions +extension AppAuthExampleViewController { + + @IBAction func authWithAutoCodeExchange(_ sender: UIButton) { + + guard let issuer = URL(string: kIssuer) else { + self.logMessage("Error creating URL for : \(kIssuer)") + return + } + + self.logMessage("Fetching configuration for issuer: \(issuer)") + + // discovers endpoints + OIDAuthorizationService.discoverConfiguration(forIssuer: issuer) { configuration, error in + + guard let config = configuration else { + self.logMessage("Error retrieving discovery document: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + return + } + + self.logMessage("Got configuration: \(config)") + + if let clientId = kClientID { + self.doAuthWithAutoCodeExchange(configuration: config, clientID: clientId, clientSecret: nil) + } else { + self.doClientRegistration(configuration: config) { configuration, response in + + guard let configuration = configuration, let clientID = response?.clientID else { + self.logMessage("Error retrieving configuration OR clientID") + return + } + + self.doAuthWithAutoCodeExchange(configuration: configuration, + clientID: clientID, + clientSecret: response?.clientSecret) + } + } + } + + } + + @IBAction func authNoCodeExchange(_ sender: UIButton) { + + guard let issuer = URL(string: kIssuer) else { + self.logMessage("Error creating URL for : \(kIssuer)") + return + } + + self.logMessage("Fetching configuration for issuer: \(issuer)") + + OIDAuthorizationService.discoverConfiguration(forIssuer: issuer) { configuration, error in + + if let error = error { + self.logMessage("Error retrieving discovery document: \(error.localizedDescription)") + return + } + + guard let configuration = configuration else { + self.logMessage("Error retrieving discovery document. Error & Configuration both are NIL!") + return + } + + self.logMessage("Got configuration: \(configuration)") + + if let clientId = kClientID { + + self.doAuthWithoutCodeExchange(configuration: configuration, clientID: clientId, clientSecret: nil) + + } else { + + self.doClientRegistration(configuration: configuration) { configuration, response in + + guard let configuration = configuration, let response = response else { + return + } + + self.doAuthWithoutCodeExchange(configuration: configuration, + clientID: response.clientID, + clientSecret: response.clientSecret) + } + } + } + } + + @IBAction func codeExchange(_ sender: UIButton) { + + guard let tokenExchangeRequest = self.authState?.lastAuthorizationResponse.tokenExchangeRequest() else { + self.logMessage("Error creating authorization code exchange request") + return + } + + self.logMessage("Performing authorization code exchange with request \(tokenExchangeRequest)") + + OIDAuthorizationService.perform(tokenExchangeRequest) { response, error in + + if let tokenResponse = response { + self.logMessage("Received token response with accessToken: \(tokenResponse.accessToken ?? "DEFAULT_TOKEN")") + } else { + self.logMessage("Token exchange error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + } + self.authState?.update(with: response, error: error) + } + } + + @IBAction func userinfo(_ sender: UIButton) { + + guard let userinfoEndpoint = self.authState?.lastAuthorizationResponse.request.configuration.discoveryDocument?.userinfoEndpoint else { + self.logMessage("Userinfo endpoint not declared in discovery document") + return + } + + self.logMessage("Performing userinfo request") + + let currentAccessToken: String? = self.authState?.lastTokenResponse?.accessToken + + self.authState?.performAction() { (accessToken, idTOken, error) in + + if error != nil { + self.logMessage("Error fetching fresh tokens: \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let accessToken = accessToken else { + self.logMessage("Error getting accessToken") + return + } + + if currentAccessToken != accessToken { + self.logMessage("Access token was refreshed automatically (\(currentAccessToken ?? "CURRENT_ACCESS_TOKEN") to \(accessToken))") + } else { + self.logMessage("Access token was fresh and not updated \(accessToken)") + } + + var urlRequest = URLRequest(url: userinfoEndpoint) + urlRequest.allHTTPHeaderFields = ["Authorization":"Bearer \(accessToken)"] + + let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in + + DispatchQueue.main.async { + + guard error == nil else { + self.logMessage("HTTP request failed \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let response = response as? HTTPURLResponse else { + self.logMessage("Non-HTTP response") + return + } + + guard let data = data else { + self.logMessage("HTTP response data is empty") + return + } + + var json: [AnyHashable: Any]? + + do { + json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + } catch { + self.logMessage("JSON Serialization Error") + } + + if response.statusCode != 200 { + // server replied with an error + let responseText: String? = String(data: data, encoding: String.Encoding.utf8) + + if response.statusCode == 401 { + // "401 Unauthorized" generally indicates there is an issue with the authorization + // grant. Puts OIDAuthState into an error state. + let oauthError = OIDErrorUtilities.resourceServerAuthorizationError(withCode: 0, + errorResponse: json, + underlyingError: error) + self.authState?.update(withAuthorizationError: oauthError) + self.logMessage("Authorization Error (\(oauthError)). Response: \(responseText ?? "RESPONSE_TEXT")") + } else { + self.logMessage("HTTP: \(response.statusCode), Response: \(responseText ?? "RESPONSE_TEXT")") + } + + return + } + + if let json = json { + self.logMessage("Success: \(json)") + } + } + } + + task.resume() + } + } + + @IBAction func trashClicked(_ sender: UIBarButtonItem) { + + let alert = UIAlertController(title: nil, + message: nil, + preferredStyle: UIAlertControllerStyle.actionSheet) + + let clearAuthAction = UIAlertAction(title: "Clear OAuthState", style: .destructive) { (_: UIAlertAction) in + self.setAuthState(nil) + self.updateUI() + } + alert.addAction(clearAuthAction) + + let clearLogs = UIAlertAction(title: "Clear Logs", style: .default) { (_: UIAlertAction) in + DispatchQueue.main.async { + self.logTextView.text = "" + } + } + + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) + + + alert.addAction(clearLogs) + alert.addAction(cancelAction) + self.present(alert, animated: true, completion: nil) + + } +} + +//MARK: AppAuth Methods +extension AppAuthExampleViewController { + + func doClientRegistration(configuration: OIDServiceConfiguration, callback: @escaping PostRegistrationCallback) { + + guard let redirectURI = URL(string: kRedirectURI) else { + self.logMessage("Error creating URL for : \(kRedirectURI)") + return + } + + let request: OIDRegistrationRequest = OIDRegistrationRequest(configuration: configuration, + redirectURIs: [redirectURI], + responseTypes: nil, + grantTypes: nil, + subjectType: nil, + tokenEndpointAuthMethod: "client_secret_post", + additionalParameters: nil) + + // performs registration request + self.logMessage("Initiating registration request") + + OIDAuthorizationService.perform(request) { response, error in + + if let regResponse = response { + self.setAuthState(OIDAuthState(registrationResponse: regResponse)) + self.logMessage("Got registration response: \(regResponse)") + callback(configuration, regResponse) + } else { + self.logMessage("Registration error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + } + } + } + + func doAuthWithAutoCodeExchange(configuration: OIDServiceConfiguration, clientID: String, clientSecret: String?) { + + guard let redirectURI = URL(string: kRedirectURI) else { + self.logMessage("Error creating URL for : \(kRedirectURI)") + return + } + + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { + self.logMessage("Error accessing AppDelegate") + return + } + + // builds authentication request + let request = OIDAuthorizationRequest(configuration: configuration, + clientId: clientID, + clientSecret: clientSecret, + scopes: [OIDScopeOpenID, OIDScopeProfile], + redirectURL: redirectURI, + responseType: OIDResponseTypeCode, + additionalParameters: nil) + + // performs authentication request + logMessage("Initiating authorization request with scope: \(request.scope ?? "DEFAULT_SCOPE")") + + appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in + + if let authState = authState { + self.setAuthState(authState) + self.logMessage("Got authorization tokens. Access token: \(authState.lastTokenResponse?.accessToken ?? "DEFAULT_TOKEN")") + } else { + self.logMessage("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + } + } + } + + func doAuthWithoutCodeExchange(configuration: OIDServiceConfiguration, clientID: String, clientSecret: String?) { + + guard let redirectURI = URL(string: kRedirectURI) else { + self.logMessage("Error creating URL for : \(kRedirectURI)") + return + } + + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { + self.logMessage("Error accessing AppDelegate") + return + } + + // builds authentication request + let request = OIDAuthorizationRequest(configuration: configuration, + clientId: clientID, + clientSecret: clientSecret, + scopes: [OIDScopeOpenID, OIDScopeProfile], + redirectURL: redirectURI, + responseType: OIDResponseTypeCode, + additionalParameters: nil) + + // performs authentication request + logMessage("Initiating authorization request with scope: \(request.scope ?? "DEFAULT_SCOPE")") + + appDelegate.currentAuthorizationFlow = OIDAuthorizationService.present(request, presenting: self) { (response, error) in + + if let response = response { + let authState = OIDAuthState(authorizationResponse: response) + self.setAuthState(authState) + self.logMessage("Authorization response with code: \(response.authorizationCode ?? "DEFAULT_CODE")") + // could just call [self tokenExchange:nil] directly, but will let the user initiate it. + } else { + self.logMessage("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + } + } + } +} + +//MARK: OIDAuthState Delegate +extension AppAuthExampleViewController: OIDAuthStateChangeDelegate, OIDAuthStateErrorDelegate { + + func didChange(_ state: OIDAuthState) { + self.stateChanged() + } + + func authState(_ state: OIDAuthState, didEncounterAuthorizationError error: Error) { + self.logMessage("Received authorization error: \(error)") + } +} + +//MARK: Helper Methods +extension AppAuthExampleViewController { + + func saveState() { + + var data: Data? = nil + + if let authState = self.authState { + data = NSKeyedArchiver.archivedData(withRootObject: authState) + } + + UserDefaults.standard.set(data, forKey: kAppAuthExampleAuthStateKey) + UserDefaults.standard.synchronize() + } + + func loadState() { + guard let data = UserDefaults.standard.object(forKey: kAppAuthExampleAuthStateKey) as? Data else { + return + } + + if let authState = NSKeyedUnarchiver.unarchiveObject(with: data) as? OIDAuthState { + self.setAuthState(authState) + } + } + + func setAuthState(_ authState: OIDAuthState?) { + if (self.authState == authState) { + return; + } + self.authState = authState; + self.authState?.stateChangeDelegate = self; + self.stateChanged() + } + + func updateUI() { + + self.codeExchangeButton.isEnabled = self.authState?.lastAuthorizationResponse.authorizationCode != nil && !((self.authState?.lastTokenResponse) != nil) + + if let authState = self.authState { + self.authAutoButton.setTitle("1. Re-Auth", for: .normal) + self.authManual.setTitle("1(A) Re-Auth", for: .normal) + self.userinfoButton.isEnabled = authState.isAuthorized ? true : false + } else { + self.authAutoButton.setTitle("1. Auto", for: .normal) + self.authManual.setTitle("1(A) Manual", for: .normal) + self.userinfoButton.isEnabled = false + } + } + + func stateChanged() { + self.saveState() + self.updateUI() + } + + func logMessage(_ message: String?) { + + guard let message = message else { + return + } + + print(message); + + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "hh:mm:ss"; + let dateString = dateFormatter.string(from: Date()) + + // appends to output log + DispatchQueue.main.async { + let logText = "\(self.logTextView.text ?? "")\n\(dateString): \(message)" + self.logTextView.text = logText + } + } +} diff --git a/Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift b/Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift new file mode 100644 index 000000000..adf6904ee --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift @@ -0,0 +1,44 @@ +// +// AppDelegate.swift +// +// Copyright (c) 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import AppAuth +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + var currentAuthorizationFlow: OIDAuthorizationFlowSession? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { + + if let authorizationFlow = self.currentAuthorizationFlow, authorizationFlow.resumeAuthorizationFlow(with: url) { + self.currentAuthorizationFlow = nil + return true + } + + return false + } + +} + diff --git a/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..d8db8d65f --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Examples/Example-iOS_Swift-Carthage/Source/Base.lproj/LaunchScreen.storyboard b/Examples/Example-iOS_Swift-Carthage/Source/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..f83f6fd58 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Source/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Example-iOS_Swift-Carthage/Source/Base.lproj/Main.storyboard b/Examples/Example-iOS_Swift-Carthage/Source/Base.lproj/Main.storyboard new file mode 100644 index 000000000..e85c973e8 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Source/Base.lproj/Main.storyboard @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Example-iOS_Swift-Carthage/Source/Info.plist b/Examples/Example-iOS_Swift-Carthage/Source/Info.plist new file mode 100644 index 000000000..7df6bc8c1 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Source/Info.plist @@ -0,0 +1,56 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + com.example.app + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + From 969521e84498d0a008fe356e2b42223913acbf96 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 4 Jan 2018 16:53:12 -0800 Subject: [PATCH 003/204] Fixed broken link to RFC 8252 Appendix A. --- Examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/README.md b/Examples/README.md index ca394f7fe..3e2a3c7e3 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -18,7 +18,7 @@ may view the IdP-specific information in the next section. ## OpenID Certified Providers All [Certified OpenID providers](http://openid.net/certification/) that support -[RFC 8252](https://tools.ietf.org/html/rfc8252-08#appendix-A) +[RFC 8252](https://tools.ietf.org/html/rfc8252#appendix-A) are welcome to submit a README with IdP information. Those with instructions on file: From e704952dcac4abfbd58cb6c35a0315a8af42498d Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 5 Jan 2018 12:55:46 -0800 Subject: [PATCH 004/204] Add README for the Swift sample. Remove Cartfile.resolved file which isn't needed. --- .../Cartfile.resolved | 1 - Examples/Example-iOS_Swift-Carthage/README.md | 45 +++++++++++++++++++ .../Source/AppAuthExampleViewController.swift | 17 ++++--- .../Source/AppDelegate.swift | 2 +- 4 files changed, 54 insertions(+), 11 deletions(-) delete mode 100644 Examples/Example-iOS_Swift-Carthage/Cartfile.resolved create mode 100644 Examples/Example-iOS_Swift-Carthage/README.md diff --git a/Examples/Example-iOS_Swift-Carthage/Cartfile.resolved b/Examples/Example-iOS_Swift-Carthage/Cartfile.resolved deleted file mode 100644 index 782a3e9d9..000000000 --- a/Examples/Example-iOS_Swift-Carthage/Cartfile.resolved +++ /dev/null @@ -1 +0,0 @@ -github "openid/AppAuth-iOS" "96ea3e3dc691d76d814015ce7c47a6c1ccaf8d10" diff --git a/Examples/Example-iOS_Swift-Carthage/README.md b/Examples/Example-iOS_Swift-Carthage/README.md new file mode 100644 index 000000000..8eb120fb0 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/README.md @@ -0,0 +1,45 @@ +# Example Project + +## Setup & Open the Project + +You'll need to have [Carthage](https://github.com/Carthage/Carthage) installed +in order to pull the AppAuth dependency. + +So first run `carthage bootstrap` to build AppAuth framework then open the +`Example.xcodeproj` file. + +## Configuration + +The example doesn't work out of the box, you need to configure it with your own +client ID. + +### Information You'll Need + +* Issuer +* Client ID +* Redirect URI + +How to get this information varies by IdP, but we have +[instructions](../README.md#openid-certified-providers) for some OpenID +Certified providers. + +### Configure the Example + +#### In the file `AppAuthExampleViewController.swift` + +1. Update `kIssuer` with the IdP's issuer. +2. Update `kClientID` with your new client id. +3. Update `kRedirectURI` redirect URI + +#### In the file `Info.plist` + +Fully expand "URL types" (a.k.a. `CFBundleURLTypes`) and replace +`com.example.app` with the *scheme* of your redirect URI. +The scheme is everything before the colon (`:`). For example, if the redirect +URI is `com.example.app:/oauth2redirect/example-provider`, then the scheme +would be `com.example.app`. + +### Running the Example + +Now your example should be ready to run. + diff --git a/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift index 6bb464ff1..c88459745 100644 --- a/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift +++ b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift @@ -1,7 +1,7 @@ // // AppAuthExampleViewController.swift // -// Copyright (c) 2017 Google Inc. All Rights Reserved. +// Copyright (c) 2017 The AppAuth Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ let kIssuer: String = "https://issuer.example.com"; /** The OAuth client ID. - For client configuration instructions, see the [README](https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md). + For client configuration instructions, see the [README](https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_Swift-Carthage/README.md). Set to nil to use dynamic registration with this example. */ let kClientID: String? = "YOUR_CLIENT_ID"; @@ -37,7 +37,7 @@ let kClientID: String? = "YOUR_CLIENT_ID"; /** The OAuth redirect URI for the client @c kClientID. - For client configuration instructions, see the [README](https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md). + For client configuration instructions, see the [README](https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_Swift-Carthage/README.md). */ let kRedirectURI: String = "com.example.app:/oauth2redirect/example-provider"; @@ -47,7 +47,6 @@ let kRedirectURI: String = "com.example.app:/oauth2redirect/example-provider"; let kAppAuthExampleAuthStateKey: String = "authState"; - class AppAuthExampleViewController: UIViewController { @IBOutlet private weak var authAutoButton: UIButton! @@ -74,19 +73,19 @@ extension AppAuthExampleViewController { func validateOAuthConfiguration() { // The example needs to be configured with your own client details. - // See: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md + // See: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_Swift-Carthage/README.md assert(kIssuer != "https://issuer.example.com", "Update kIssuer with your own issuer.\n" + - "Instructions: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md"); + "Instructions: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_Swift-Carthage/README.md"); assert(kClientID != "YOUR_CLIENT_ID", "Update kClientID with your own client ID.\n" + - "Instructions: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md"); + "Instructions: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_Swift-Carthage/README.md"); assert(kRedirectURI != "com.example.app:/oauth2redirect/example-provider", "Update kRedirectURI with your own redirect URI.\n" + - "Instructions: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md"); + "Instructions: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_Swift-Carthage/README.md"); // verifies that the custom URI scheme has been updated in the Info.plist guard let urlTypes: [AnyObject] = Bundle.main.object(forInfoDictionaryKey: "CFBundleURLTypes") as? [AnyObject], urlTypes.count > 0 else { @@ -108,7 +107,7 @@ extension AppAuthExampleViewController { assert(urlScheme != "com.example.app", "Configure the URI scheme in Info.plist (URL Types -> Item 0 -> URL Schemes -> Item 0) " + "with the scheme of your redirect URI. Full instructions: " + - "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_ObjC/README.md") + "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-iOS_Swift-Carthage/README.md") } } diff --git a/Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift b/Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift index adf6904ee..8cfcf9bff 100644 --- a/Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift +++ b/Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift @@ -1,7 +1,7 @@ // // AppDelegate.swift // -// Copyright (c) 2017 Google Inc. All Rights Reserved. +// Copyright (c) 2017 The AppAuth Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 670878804564b83297d3f69099de75cbc8dfe91a Mon Sep 17 00:00:00 2001 From: Bertin Philippe Date: Fri, 21 Apr 2017 08:23:08 -0700 Subject: [PATCH 005/204] Fix state parameter mismatch error message. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case of “state” parameter mismatch between request and response, the generated error was not populated with the right information. Also, the OAuth 2 response is “valid” regardless of the state parameter so it makes sense to create the response object before checking for the state parameter. --- Source/OIDAuthorizationService.m | 33 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index a475e37ed..061cff681 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -37,9 +37,6 @@ */ static NSString *const kOpenIDConfigurationWellKnownPath = @".well-known/openid-configuration"; -/*! @brief The state authorization parameter. - */ -static NSString *const kStateParameter = @"state"; NS_ASSUME_NONNULL_BEGIN @@ -127,25 +124,25 @@ - (BOOL)resumeAuthorizationFlowWithURL:(NSURL *)URL { underlyingError:nil]; } - // verifies that the state in the response matches the state in the request, or both are nil - if (!OIDIsEqualIncludingNil(_request.state, query.dictionaryValue[kStateParameter])) { - 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]; - } - // 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]; + } } [_UICoordinator dismissAuthorizationAnimated:YES From d27db29ce26cf1d1c390034e94b00d44af3da750 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 5 Jan 2018 15:07:10 -0800 Subject: [PATCH 006/204] Version bump -> 0.92.0. --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index b1ffbf4da..1cc66b91d 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "0.91.0" + s.version = "0.92.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC From 8d6aabec1c811732528dde9e0a93a6f5a6b9a47d Mon Sep 17 00:00:00 2001 From: "D. Keith Casey, Jr" Date: Tue, 13 Jun 2017 09:58:43 -0700 Subject: [PATCH 007/204] Added Okta to the instruction list --- Examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Examples/README.md b/Examples/README.md index 3e2a3c7e3..2361d3d49 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -25,4 +25,5 @@ Those with instructions on file: * [Google](README-Google.md) * [IdentityServer](README-IdentityServer.md) +* [Okta](README-Okta.md) * [PingFederate](README-PingFederate.md) From 135f99d2cb4e9d18d310ac2588b905e612461561 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 8 Feb 2018 20:59:48 -0800 Subject: [PATCH 008/204] Add custom browser support - An UICoordinator for iOS to support auth with a custom iOS browser like Chrome or Firefox. - Implements #200. --- AppAuth.xcodeproj/project.pbxproj | 10 ++ Source/AppAuth.h | 1 + Source/Framework/AppAuth.h | 1 + Source/OIDURLQueryComponent.h | 6 + Source/OIDURLQueryComponent.m | 14 +- ...DAuthorizationUICoordinatorCustomBrowser.h | 106 +++++++++++ ...DAuthorizationUICoordinatorCustomBrowser.m | 168 ++++++++++++++++++ 7 files changed, 302 insertions(+), 4 deletions(-) create mode 100644 Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.h create mode 100644 Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.m diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 6657b7c8b..5f424427b 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -341,6 +341,9 @@ 343AAB9B1E834A8800F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB9C1E834A8900F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB9D1E834A8A00F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 345AE747202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m */; }; + 345AE748202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m */; }; + 345AE749202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = 345AE746202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 347423E41E7F3C4000D3E6D6 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; 347423FF1E7F4BA000D3E6D6 /* OIDAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */; }; 347424001E7F4BA000D3E6D6 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; @@ -542,6 +545,8 @@ 343AAAAE1E83489A00F9D36E /* AppAuth_tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_tvOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAAC21E8348A900F9D36E /* AppAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_macOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 345AE745202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDAuthorizationUICoordinatorCustomBrowser.m; path = iOS/OIDAuthorizationUICoordinatorCustomBrowser.m; sourceTree = ""; }; + 345AE746202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDAuthorizationUICoordinatorCustomBrowser.h; path = iOS/OIDAuthorizationUICoordinatorCustomBrowser.h; sourceTree = ""; }; 347423F61E7F4B5600D3E6D6 /* libAppAuth-watchOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-watchOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 34D5EC431E6D1AD900814354 /* OIDAppAuthTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OIDAppAuthTests-Bridging-Header.h"; sourceTree = ""; }; 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDSwiftTests.swift; sourceTree = ""; }; @@ -847,6 +852,8 @@ children = ( F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */, F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */, + 345AE746202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.h */, + 345AE745202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m */, F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */, F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */, F6F60FB41D2BFEFE00325CB3 /* OIDAuthorizationUICoordinatorIOS.h */, @@ -874,6 +881,7 @@ 343AAAE41E83499000F9D36E /* OIDAuthorizationResponse.h in Headers */, 343AAAF31E83499000F9D36E /* OIDScopes.h in Headers */, 343AAAE81E83499000F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, + 345AE749202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.h in Headers */, 343AAA6B1E83465500F9D36E /* AppAuth.h in Headers */, 343AAA6E1E83466B00F9D36E /* OIDAuthorizationUICoordinatorIOS.h in Headers */, 343AAAF21E83499000F9D36E /* OIDResponseTypes.h in Headers */, @@ -1457,6 +1465,7 @@ 60140F7A1DE4276800DA0DC3 /* OIDClientMetadataParameters.m in Sources */, 341741DE1C5D8243000EF209 /* OIDAuthState.m in Sources */, 341741DD1C5D8243000EF209 /* OIDAuthorizationService.m in Sources */, + 345AE747202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m in Sources */, 340DAECD1D582DE100EC285B /* OIDAuthorizationUICoordinatorIOS.m in Sources */, 341741EB1C5D8243000EF209 /* OIDURLQueryComponent.m in Sources */, 341741E11C5D8243000EF209 /* OIDFieldMapping.m in Sources */, @@ -1579,6 +1588,7 @@ 343AAA931E83478900F9D36E /* OIDTokenUtilities.m in Sources */, 343AAA901E83478900F9D36E /* OIDServiceDiscovery.m in Sources */, 343AAA911E83478900F9D36E /* OIDTokenRequest.m in Sources */, + 345AE748202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m in Sources */, 343AAA6F1E83467D00F9D36E /* OIDAuthorizationService+IOS.m in Sources */, 343AAA8F1E83478900F9D36E /* OIDServiceConfiguration.m in Sources */, 343AAA891E83478900F9D36E /* OIDRegistrationResponse.m in Sources */, diff --git a/Source/AppAuth.h b/Source/AppAuth.h index cfd3f9c62..fae50153f 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -43,6 +43,7 @@ #elif TARGET_OS_IOS #import "OIDAuthState+IOS.h" #import "OIDAuthorizationService+IOS.h" +#import "OIDAuthorizationUICoordinatorCustomBrowser.h" #import "OIDAuthorizationUICoordinatorIOS.h" #elif TARGET_OS_MAC #import "OIDAuthState+Mac.h" diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index 1275e36f2..c5a3ca26b 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -50,6 +50,7 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #elif TARGET_OS_IOS #import #import +#import #import #elif TARGET_OS_MAC #import diff --git a/Source/OIDURLQueryComponent.h b/Source/OIDURLQueryComponent.h index 068dcc369..2a87d4f81 100644 --- a/Source/OIDURLQueryComponent.h +++ b/Source/OIDURLQueryComponent.h @@ -79,6 +79,12 @@ extern BOOL gOIDURLQueryComponentForceIOS7Handling; */ - (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 diff --git a/Source/OIDURLQueryComponent.m b/Source/OIDURLQueryComponent.m index 6a70d91ad..0a94ec27a 100644 --- a/Source/OIDURLQueryComponent.m +++ b/Source/OIDURLQueryComponent.m @@ -124,6 +124,15 @@ - (void)addParameters:(NSDictionary *)parameters { 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. @@ -133,10 +142,7 @@ - (NSString *)percentEncodedQueryString { NSMutableArray *parameterizedValues = [NSMutableArray array]; // 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]; + NSMutableCharacterSet *allowedParamCharacters = [[self class] URLParamValueAllowedCharacters]; for (NSString *parameterName in _parameters.allKeys) { NSString *encodedParameterName = diff --git a/Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.h b/Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.h new file mode 100644 index 000000000..1eedb2f21 --- /dev/null +++ b/Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.h @@ -0,0 +1,106 @@ +/*! @file OIDAuthorizationUICoordinatorCustomBrowser.h + @brief AppAuth iOS SDK + @copyright + Copyright 2018 Google LLC + @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 + +#import "OIDAuthorizationUICoordinator.h" + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief A block that transforms a regular http/https URL into one that will open in an + alternative browser. + @param requestURL the http/https request URL to be transformed. + @return transformed URL. + */ +typedef NSURL *_Nullable (^OIDCustomBrowserURLTransformation)(NSURL *_Nullable requestURL); + +/*! @brief An implementation of the OIDAuthorizationUICoordinator protocol for iOS that uses + a custom browser (i.e. not Safari) for authorization. It is suitable for browsers that + offer a custom url scheme that simply replaces the "https" scheme. It is not designed + for browsers that require other modifications to the URL. If the browser is not installed + the user will be prompted to install it. + */ +@interface OIDAuthorizationUICoordinatorCustomBrowser : NSObject + +/*! @brief URL transformation block for the browser. + */ +@property(nonatomic, readonly) OIDCustomBrowserURLTransformation URLTransformation; + +/*! @brief URL Scheme used to test for whether the browser is installed. + */ +@property(nonatomic, readonly, nullable) NSString *canOpenURLScheme; + +/*! @brief URL of the browser's App Store listing. + */ +@property(nonatomic, readonly, nullable) NSURL *appStoreURL; + +/*! @brief An instance of @c OIDAuthorizationUICoordinatorCustomBrowser for Chrome. + */ ++ (instancetype)CustomBrowserChrome; + +/*! @brief An instance of @c OIDAuthorizationUICoordinatorCustomBrowser for Firefox. + */ ++ (instancetype)CustomBrowserFirefox; + +/*! @brief An instance of @c OIDAuthorizationUICoordinatorCustomBrowser for Opera. + */ ++ (instancetype)CustomBrowserOpera; + +/*! @brief An instance of @c OIDAuthorizationUICoordinatorCustomBrowser for Safari. + */ ++ (instancetype)CustomBrowserSafari; + +/*! @brief Creates a @c OIDCustomBrowserURLTransformation using the scheme substitution method used + iOS browsers like Chrome and Firefox. + */ ++ (OIDCustomBrowserURLTransformation) + URLTransformationSchemeSubstitutionHTTPS:(NSString *)browserSchemeHTTPS + HTTP:(nullable NSString *)browserSchemeHTTP; + +/*! @brief Creates a @c OIDCustomBrowserURLTransformation with the URL prefix method used by + iOS browsers like Firefox. + */ ++ (OIDCustomBrowserURLTransformation) URLTransformationSchemeConcatPrefix:(NSString*)URLprefix; + +/*! @internal + @brief Unavailable. Please use @c initWithURLTransformation:canOpenURLScheme:appStoreURL: + */ +- (nonnull instancetype)init NS_UNAVAILABLE; + +/*! @brief UICoordinator for a custom browser. @c presentAuthorizationRequest:session method + will return NO if the browser isn't installed. + */ +- (nullable instancetype)initWithURLTransformation:(OIDCustomBrowserURLTransformation)URLTransformation; + +/*! @brief The designated initializer. + @param URLTransformation the transformation block to translate the URL into one that will open + in the desired custom browser. + @param canOpenURLScheme any scheme supported by the browser used to check if the browser is + installed. + @param appStoreURL URL of the browser in the app store. When this and @c canOpenURLScheme + are non-nil, @c presentAuthorizationRequest:session will redirect the user to the app store + if the browser is not installed. + */ +- (nullable instancetype)initWithURLTransformation:(OIDCustomBrowserURLTransformation)URLTransformation + canOpenURLScheme:(nullable NSString *)canOpenURLScheme + appStoreURL:(nullable NSURL *)appStoreURL + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.m b/Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.m new file mode 100644 index 000000000..cba8dfe79 --- /dev/null +++ b/Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.m @@ -0,0 +1,168 @@ +/*! @file OIDAuthorizationUICoordinatorCustomBrowser.m + @brief AppAuth iOS SDK + @copyright + Copyright 2018 Google LLC + @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 "OIDAuthorizationUICoordinatorCustomBrowser.h" + +#import + +#import "OIDAuthorizationRequest.h" +#import "OIDAuthorizationService.h" +#import "OIDErrorUtilities.h" +#import "OIDURLQueryComponent.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation OIDAuthorizationUICoordinatorCustomBrowser { + OIDCustomBrowserURLTransformation _URLTransformation; + NSString *_canOpenURLScheme; + NSURL *_appStoreURL; +} + +@synthesize URLTransformation = _URLTransformation; +@synthesize canOpenURLScheme = _canOpenURLScheme; +@synthesize appStoreURL = _appStoreURL; + ++ (instancetype)CustomBrowserChrome { + // Chrome iOS documentation: https://developer.chrome.com/multidevice/ios/links + OIDCustomBrowserURLTransformation transform = [[self class] URLTransformationSchemeSubstitutionHTTPS:@"googlechromes" HTTP:@"googlechrome"]; + NSURL *appStoreURL = + [NSURL URLWithString:@"itms-apps://itunes.apple.com/us/app/chrome/id535886823"]; + return [[[self class] alloc] initWithURLTransformation:transform + canOpenURLScheme:@"googlechromes" + appStoreURL:appStoreURL]; +} + ++ (instancetype)CustomBrowserFirefox { + // Firefox iOS documentation: https://github.com/mozilla-mobile/firefox-ios-open-in-client + OIDCustomBrowserURLTransformation transform = + [[self class] URLTransformationSchemeConcatPrefix:@"firefox://open-url?url="]; + NSURL *appStoreURL = + [NSURL URLWithString:@"itms-apps://itunes.apple.com/us/app/firefox-web-browser/id989804926"]; + return [[[self class] alloc] initWithURLTransformation:transform + canOpenURLScheme:@"firefox" + appStoreURL:appStoreURL]; +} + ++ (instancetype)CustomBrowserOpera { + OIDCustomBrowserURLTransformation transform = + [[self class] URLTransformationSchemeSubstitutionHTTPS:@"opera-https" HTTP:@"opera-http"]; + NSURL *appStoreURL = + [NSURL URLWithString:@"itms-apps://itunes.apple.com/us/app/opera-mini-web-browser/id363729560"]; + return [[[self class] alloc] initWithURLTransformation:transform + canOpenURLScheme:@"opera-https" + appStoreURL:appStoreURL]; +} + ++ (instancetype)CustomBrowserSafari { + OIDCustomBrowserURLTransformation transformNOP = ^NSURL *(NSURL *requestURL) { + return requestURL; + }; + OIDAuthorizationUICoordinatorCustomBrowser *coordinator = + [[[self class] alloc] initWithURLTransformation:transformNOP]; + return coordinator; +} + ++ (OIDCustomBrowserURLTransformation) + URLTransformationSchemeSubstitutionHTTPS:(NSString *)browserSchemeHTTPS + HTTP:(nullable NSString *)browserSchemeHTTP { + OIDCustomBrowserURLTransformation transform = ^NSURL *(NSURL *requestURL) { + // Replace the URL Scheme with the Chrome equivalent. + NSString *newScheme = nil; + if ([requestURL.scheme isEqualToString:@"https"]) { + newScheme = browserSchemeHTTPS; + } else if ([requestURL.scheme isEqualToString:@"http"]) { + if (!browserSchemeHTTP) { + NSAssert(false, @"No HTTP scheme registered for browser"); + return nil; + } + newScheme = browserSchemeHTTP; + } + + // Replaces the URI scheme with the custom scheme + NSURLComponents *components = [NSURLComponents componentsWithURL:requestURL + resolvingAgainstBaseURL:YES]; + components.scheme = newScheme; + return components.URL; + }; + return transform; +} + ++ (OIDCustomBrowserURLTransformation)URLTransformationSchemeConcatPrefix:(NSString *)URLprefix { + OIDCustomBrowserURLTransformation transform = ^NSURL *(NSURL *requestURL) { + NSString *requestURLString = [requestURL absoluteString]; + NSMutableCharacterSet *allowedParamCharacters = + [OIDURLQueryComponent URLParamValueAllowedCharacters]; + NSString *encodedUrl = [requestURLString stringByAddingPercentEncodingWithAllowedCharacters:allowedParamCharacters]; + NSString *newURL = [NSString stringWithFormat:@"%@%@", URLprefix, encodedUrl]; + return [NSURL URLWithString:newURL]; + }; + return transform; +} + +- (nullable instancetype)initWithURLTransformation: + (OIDCustomBrowserURLTransformation)URLTransformation { + return [self initWithURLTransformation:URLTransformation canOpenURLScheme:nil appStoreURL:nil]; +} + +- (nullable instancetype) + initWithURLTransformation:(OIDCustomBrowserURLTransformation)URLTransformation + canOpenURLScheme:(nullable NSString *)canOpenURLScheme + appStoreURL:(nullable NSURL *)appStoreURL { + self = [super init]; + if (self) { + _URLTransformation = URLTransformation; + _canOpenURLScheme = canOpenURLScheme; + _appStoreURL = appStoreURL; + } + return self; +} + +- (BOOL)presentAuthorizationRequest:(OIDAuthorizationRequest *)request + session:(id)session { + // If the app store URL is set, checks if the app is installed and if not opens the app store. + if (_appStoreURL && _canOpenURLScheme) { + // Verifies existence of LSApplicationQueriesSchemes Info.plist key. + NSArray __unused* canOpenURLs = + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"LSApplicationQueriesSchemes"]; + NSAssert(canOpenURLs, @"plist missing LSApplicationQueriesSchemes key"); + NSAssert1([canOpenURLs containsObject:_canOpenURLScheme], + @"plist missing LSApplicationQueriesSchemes entry for '%@'", _canOpenURLScheme); + + // Opens AppStore if app isn't installed + NSString *testURLString = [NSString stringWithFormat:@"%@://example.com", _canOpenURLScheme]; + NSURL *testURL = [NSURL URLWithString:testURLString]; + if (![[UIApplication sharedApplication] canOpenURL:testURL]) { + [[UIApplication sharedApplication] openURL:_appStoreURL]; + return NO; + } + } + + // Transforms the request URL and opens it. + NSURL *requestURL = [request authorizationRequestURL]; + requestURL = _URLTransformation(requestURL); + BOOL openedInBrowser = [[UIApplication sharedApplication] openURL:requestURL]; + return openedInBrowser; +} + +- (void)dismissAuthorizationAnimated:(BOOL)animated completion:(nonnull void (^)(void))completion { + completion(); +} + +@end + +NS_ASSUME_NONNULL_END From c8c68649888e8187f42d2605832b10fb76ea4a5a Mon Sep 17 00:00:00 2001 From: Lucas Farris Date: Wed, 24 Jan 2018 17:26:48 +0100 Subject: [PATCH 009/204] Making the FlowSessions and UICoordinators generic, so they can be used for more than just authorization purposes. Relates to issue #195 --- AppAuth.xcodeproj/project.pbxproj | 110 +++++++++++------- Source/AppAuth.h | 9 +- Source/Framework/AppAuth.h | 8 +- Source/OIDAuthState.h | 16 +-- Source/OIDAuthState.m | 10 +- Source/OIDAuthorizationFlowSession.h | 42 +++++++ Source/OIDAuthorizationRequest.h | 3 +- Source/OIDAuthorizationRequest.m | 10 ++ Source/OIDAuthorizationService.h | 47 ++------ Source/OIDAuthorizationService.m | 50 ++++---- Source/OIDAuthorizationUICoordinator.h | 53 --------- Source/OIDExternalUserAgentFlowSession.h | 52 +++++++++ Source/OIDExternalUserAgentRequest.h | 37 ++++++ Source/OIDExternalUserAgentUICoordinator.h | 53 +++++++++ Source/iOS/OIDAuthState+IOS.h | 8 +- Source/iOS/OIDAuthState+IOS.m | 6 +- Source/iOS/OIDAuthorizationService+IOS.h | 9 +- Source/iOS/OIDAuthorizationService+IOS.m | 6 +- ...rnalUserAgentUICoordinatorCustomBrowser.h} | 22 ++-- ...rnalUserAgentUICoordinatorCustomBrowser.m} | 17 +-- ...=> OIDExternalUserAgentUICoordinatorIOS.h} | 10 +- ...=> OIDExternalUserAgentUICoordinatorIOS.m} | 44 +++---- Source/macOS/OIDAuthState+Mac.h | 8 +- Source/macOS/OIDAuthState+Mac.m | 6 +- Source/macOS/OIDAuthorizationService+Mac.h | 10 +- Source/macOS/OIDAuthorizationService+Mac.m | 8 +- ...=> OIDExternalUserAgentUICoordinatorMac.h} | 14 +-- ...=> OIDExternalUserAgentUICoordinatorMac.m} | 28 ++--- Source/macOS/OIDRedirectHTTPHandler.h | 45 +++---- Source/macOS/OIDRedirectHTTPHandler.m | 6 +- UnitTests/OIDAuthorizationRequestTests.m | 6 + 31 files changed, 457 insertions(+), 296 deletions(-) create mode 100644 Source/OIDAuthorizationFlowSession.h delete mode 100644 Source/OIDAuthorizationUICoordinator.h create mode 100644 Source/OIDExternalUserAgentFlowSession.h create mode 100644 Source/OIDExternalUserAgentRequest.h create mode 100644 Source/OIDExternalUserAgentUICoordinator.h rename Source/iOS/{OIDAuthorizationUICoordinatorCustomBrowser.h => OIDExternalUserAgentUICoordinatorCustomBrowser.h} (78%) rename Source/iOS/{OIDAuthorizationUICoordinatorCustomBrowser.m => OIDExternalUserAgentUICoordinatorCustomBrowser.m} (91%) rename Source/iOS/{OIDAuthorizationUICoordinatorIOS.h => OIDExternalUserAgentUICoordinatorIOS.h} (86%) rename Source/iOS/{OIDAuthorizationUICoordinatorIOS.m => OIDExternalUserAgentUICoordinatorIOS.m} (82%) rename Source/macOS/{OIDAuthorizationUICoordinatorMac.h => OIDExternalUserAgentUICoordinatorMac.h} (66%) rename Source/macOS/{OIDAuthorizationUICoordinatorMac.m => OIDExternalUserAgentUICoordinatorMac.m} (67%) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 5f424427b..5d2d45fac 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -10,7 +10,7 @@ 039697461FA8258D003D1FB2 /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; 0396974D1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0396974C1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m */; }; 340DAE571D5821A100EC285B /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; - 340DAE581D5821A100EC285B /* OIDAuthorizationUICoordinatorMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDAuthorizationUICoordinatorMac.m */; }; + 340DAE581D5821A100EC285B /* OIDExternalUserAgentUICoordinatorMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.m */; }; 340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */; }; 340DAE5A1D5821AB00EC285B /* OIDAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */; }; 340DAE5C1D5821AB00EC285B /* OIDAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B91C5D8243000EF209 /* OIDAuthorizationService.m */; }; @@ -19,7 +19,6 @@ 340DAEBC1D582AF100EC285B /* OIDRedirectHTTPHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAEBA1D582AF100EC285B /* OIDRedirectHTTPHandler.m */; }; 340DAECB1D582DE100EC285B /* OIDAuthorizationService+IOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */; }; 340DAECC1D582DE100EC285B /* OIDAuthState+IOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */; }; - 340DAECD1D582DE100EC285B /* OIDAuthorizationUICoordinatorIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F60FB21D2BFEFE00325CB3 /* OIDAuthorizationUICoordinatorIOS.m */; }; 341310BE1E6F943C00D5DEE5 /* OIDClientMetadataParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F791DE4276800DA0DC3 /* OIDClientMetadataParameters.m */; }; 341310BF1E6F943C00D5DEE5 /* OIDClientMetadataParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F791DE4276800DA0DC3 /* OIDClientMetadataParameters.m */; }; 341310C21E6F944B00D5DEE5 /* OIDError.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C01C5D8243000EF209 /* OIDError.m */; }; @@ -120,10 +119,8 @@ 343AAA6B1E83465500F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAA6C1E83466B00F9D36E /* OIDAuthorizationService+IOS.h in Headers */ = {isa = PBXBuildFile; fileRef = F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAA6D1E83466B00F9D36E /* OIDAuthState+IOS.h in Headers */ = {isa = PBXBuildFile; fileRef = F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 343AAA6E1E83466B00F9D36E /* OIDAuthorizationUICoordinatorIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = F6F60FB41D2BFEFE00325CB3 /* OIDAuthorizationUICoordinatorIOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAA6F1E83467D00F9D36E /* OIDAuthorizationService+IOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */; }; 343AAA701E83467D00F9D36E /* OIDAuthState+IOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */; }; - 343AAA711E83467D00F9D36E /* OIDAuthorizationUICoordinatorIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F60FB21D2BFEFE00325CB3 /* OIDAuthorizationUICoordinatorIOS.m */; }; 343AAA721E83469600F9D36E /* OIDAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */; }; 343AAA731E8346B400F9D36E /* OIDAuthorizationRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742011C5D82D3000EF209 /* OIDAuthorizationRequestTests.m */; }; 343AAA741E8346B400F9D36E /* OIDAuthorizationResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742031C5D82D3000EF209 /* OIDAuthorizationResponseTests.m */; }; @@ -162,18 +159,17 @@ 343AAACB1E8348AA00F9D36E /* AppAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 343AAAC21E8348A900F9D36E /* AppAuth.framework */; }; 343AAAD91E83493D00F9D36E /* OIDRedirectHTTPHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAEBA1D582AF100EC285B /* OIDRedirectHTTPHandler.m */; }; 343AAADA1E83493D00F9D36E /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; - 343AAADB1E83493D00F9D36E /* OIDAuthorizationUICoordinatorMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDAuthorizationUICoordinatorMac.m */; }; + 343AAADB1E83493D00F9D36E /* OIDExternalUserAgentUICoordinatorMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.m */; }; 343AAADC1E83493D00F9D36E /* OIDAuthState+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */; }; 343AAADD1E83494400F9D36E /* OIDRedirectHTTPHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 340DAEB91D582AF100EC285B /* OIDRedirectHTTPHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAADE1E83494400F9D36E /* OIDAuthorizationService+Mac.h in Headers */ = {isa = PBXBuildFile; fileRef = 340DAE251D581FE700EC285B /* OIDAuthorizationService+Mac.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 343AAADF1E83494400F9D36E /* OIDAuthorizationUICoordinatorMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 340DAE271D581FE700EC285B /* OIDAuthorizationUICoordinatorMac.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 343AAADF1E83494400F9D36E /* OIDExternalUserAgentUICoordinatorMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 340DAE271D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAE01E83494400F9D36E /* OIDAuthState+Mac.h in Headers */ = {isa = PBXBuildFile; fileRef = 340DAE291D581FE700EC285B /* OIDAuthState+Mac.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAE11E83494A00F9D36E /* OIDLoopbackHTTPServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */; }; 343AAAE21E83494F00F9D36E /* OIDLoopbackHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FEA6AD1DB6E083005C9212 /* OIDLoopbackHTTPServer.m */; }; 343AAAE31E83499000F9D36E /* OIDAuthorizationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAE41E83499000F9D36E /* OIDAuthorizationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAE51E83499000F9D36E /* OIDAuthorizationService.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B81C5D8243000EF209 /* OIDAuthorizationService.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 343AAAE61E83499000F9D36E /* OIDAuthorizationUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = F68103B61D2568D10053658E /* OIDAuthorizationUICoordinator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAE71E83499000F9D36E /* OIDAuthState.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BA1C5D8243000EF209 /* OIDAuthState.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAE81E83499000F9D36E /* OIDAuthStateChangeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BC1C5D8243000EF209 /* OIDAuthStateChangeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAE91E83499000F9D36E /* OIDAuthStateErrorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BD1C5D8243000EF209 /* OIDAuthStateErrorDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -193,7 +189,6 @@ 343AAAFB1E83499100F9D36E /* OIDAuthorizationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAFC1E83499100F9D36E /* OIDAuthorizationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAFD1E83499100F9D36E /* OIDAuthorizationService.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B81C5D8243000EF209 /* OIDAuthorizationService.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 343AAAFE1E83499100F9D36E /* OIDAuthorizationUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = F68103B61D2568D10053658E /* OIDAuthorizationUICoordinator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAFF1E83499100F9D36E /* OIDAuthState.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BA1C5D8243000EF209 /* OIDAuthState.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB001E83499100F9D36E /* OIDAuthStateChangeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BC1C5D8243000EF209 /* OIDAuthStateChangeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB011E83499100F9D36E /* OIDAuthStateErrorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BD1C5D8243000EF209 /* OIDAuthStateErrorDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -213,7 +208,6 @@ 343AAB131E83499200F9D36E /* OIDAuthorizationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB141E83499200F9D36E /* OIDAuthorizationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB151E83499200F9D36E /* OIDAuthorizationService.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B81C5D8243000EF209 /* OIDAuthorizationService.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 343AAB161E83499200F9D36E /* OIDAuthorizationUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = F68103B61D2568D10053658E /* OIDAuthorizationUICoordinator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB171E83499200F9D36E /* OIDAuthState.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BA1C5D8243000EF209 /* OIDAuthState.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB181E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BC1C5D8243000EF209 /* OIDAuthStateChangeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB191E83499200F9D36E /* OIDAuthStateErrorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BD1C5D8243000EF209 /* OIDAuthStateErrorDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -233,7 +227,6 @@ 343AAB2B1E83499200F9D36E /* OIDAuthorizationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB2C1E83499200F9D36E /* OIDAuthorizationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB2D1E83499200F9D36E /* OIDAuthorizationService.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B81C5D8243000EF209 /* OIDAuthorizationService.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 343AAB2E1E83499200F9D36E /* OIDAuthorizationUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = F68103B61D2568D10053658E /* OIDAuthorizationUICoordinator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB2F1E83499200F9D36E /* OIDAuthState.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BA1C5D8243000EF209 /* OIDAuthState.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB301E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BC1C5D8243000EF209 /* OIDAuthStateChangeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB311E83499200F9D36E /* OIDAuthStateErrorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BD1C5D8243000EF209 /* OIDAuthStateErrorDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -341,9 +334,9 @@ 343AAB9B1E834A8800F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB9C1E834A8900F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB9D1E834A8A00F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 345AE747202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m */; }; - 345AE748202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m */; }; - 345AE749202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = 345AE746202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 345AE747202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m */; }; + 345AE748202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m */; }; + 345AE749202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = 345AE746202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 347423E41E7F3C4000D3E6D6 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; 347423FF1E7F4BA000D3E6D6 /* OIDAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */; }; 347424001E7F4BA000D3E6D6 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; @@ -380,6 +373,25 @@ 60140F801DE4344200DA0DC3 /* OIDRegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */; }; 60140F831DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F821DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m */; }; 60140F861DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */; }; + A6339DAA20321ADD0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6339DAB20321AE50043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6339DAC20321AE70043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6339DAD20321AEB0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */; }; + A6DEAB9F2018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA02018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA22018E4B60022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA32018E4B70022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA42018E4B90022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA52018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA62018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA72018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m */; }; + A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -463,8 +475,8 @@ 0396974C1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDURLSessionProviderTests.m; sourceTree = ""; }; 340DAE251D581FE700EC285B /* OIDAuthorizationService+Mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OIDAuthorizationService+Mac.h"; sourceTree = ""; }; 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OIDAuthorizationService+Mac.m"; sourceTree = ""; }; - 340DAE271D581FE700EC285B /* OIDAuthorizationUICoordinatorMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDAuthorizationUICoordinatorMac.h; sourceTree = ""; }; - 340DAE281D581FE700EC285B /* OIDAuthorizationUICoordinatorMac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDAuthorizationUICoordinatorMac.m; sourceTree = ""; }; + 340DAE271D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentUICoordinatorMac.h; sourceTree = ""; }; + 340DAE281D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDExternalUserAgentUICoordinatorMac.m; sourceTree = ""; }; 340DAE291D581FE700EC285B /* OIDAuthState+Mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OIDAuthState+Mac.h"; sourceTree = ""; }; 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OIDAuthState+Mac.m"; sourceTree = ""; }; 340DAE4E1D58216A00EC285B /* libAppAuth-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -545,8 +557,8 @@ 343AAAAE1E83489A00F9D36E /* AppAuth_tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_tvOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAAC21E8348A900F9D36E /* AppAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_macOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 345AE745202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDAuthorizationUICoordinatorCustomBrowser.m; path = iOS/OIDAuthorizationUICoordinatorCustomBrowser.m; sourceTree = ""; }; - 345AE746202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDAuthorizationUICoordinatorCustomBrowser.h; path = iOS/OIDAuthorizationUICoordinatorCustomBrowser.h; sourceTree = ""; }; + 345AE745202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentUICoordinatorCustomBrowser.m; path = iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.m; sourceTree = ""; }; + 345AE746202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentUICoordinatorCustomBrowser.h; path = iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.h; sourceTree = ""; }; 347423F61E7F4B5600D3E6D6 /* libAppAuth-watchOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-watchOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 34D5EC431E6D1AD900814354 /* OIDAppAuthTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OIDAppAuthTests-Bridging-Header.h"; sourceTree = ""; }; 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDSwiftTests.swift; sourceTree = ""; }; @@ -562,12 +574,15 @@ 60140F821DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRegistrationRequestTests.m; sourceTree = ""; }; 60140F841DE43C8C00DA0DC3 /* OIDRegistrationResponseTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRegistrationResponseTests.h; sourceTree = ""; }; 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRegistrationResponseTests.m; sourceTree = ""; }; - F68103B61D2568D10053658E /* OIDAuthorizationUICoordinator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDAuthorizationUICoordinator.h; sourceTree = ""; }; + A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDAuthorizationFlowSession.h; sourceTree = ""; }; + A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentUICoordinator.h; sourceTree = ""; }; + A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentFlowSession.h; sourceTree = ""; }; + A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentRequest.h; sourceTree = ""; }; + A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentUICoordinatorIOS.m; path = iOS/OIDExternalUserAgentUICoordinatorIOS.m; sourceTree = ""; }; + A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentUICoordinatorIOS.h; path = iOS/OIDExternalUserAgentUICoordinatorIOS.h; sourceTree = ""; }; F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OIDAuthState+IOS.m"; path = "iOS/OIDAuthState+IOS.m"; sourceTree = ""; }; F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OIDAuthorizationService+IOS.m"; path = "iOS/OIDAuthorizationService+IOS.m"; sourceTree = ""; }; - F6F60FB21D2BFEFE00325CB3 /* OIDAuthorizationUICoordinatorIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDAuthorizationUICoordinatorIOS.m; path = iOS/OIDAuthorizationUICoordinatorIOS.m; sourceTree = ""; }; F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OIDAuthorizationService+IOS.h"; path = "iOS/OIDAuthorizationService+IOS.h"; sourceTree = ""; }; - F6F60FB41D2BFEFE00325CB3 /* OIDAuthorizationUICoordinatorIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDAuthorizationUICoordinatorIOS.h; path = iOS/OIDAuthorizationUICoordinatorIOS.h; sourceTree = ""; }; F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OIDAuthState+IOS.h"; path = "iOS/OIDAuthState+IOS.h"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -689,8 +704,8 @@ 340DAEBA1D582AF100EC285B /* OIDRedirectHTTPHandler.m */, 340DAE251D581FE700EC285B /* OIDAuthorizationService+Mac.h */, 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */, - 340DAE271D581FE700EC285B /* OIDAuthorizationUICoordinatorMac.h */, - 340DAE281D581FE700EC285B /* OIDAuthorizationUICoordinatorMac.m */, + 340DAE271D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.h */, + 340DAE281D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.m */, 340DAE291D581FE700EC285B /* OIDAuthState+Mac.h */, 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */, ); @@ -735,13 +750,13 @@ 340DAE241D581FE700EC285B /* macOS */, F6F60FAF1D2BFEF000325CB3 /* iOS */, 341741AF1C5D8243000EF209 /* AppAuth.h */, + A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */, 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */, 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */, 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */, 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */, 341741B81C5D8243000EF209 /* OIDAuthorizationService.h */, 341741B91C5D8243000EF209 /* OIDAuthorizationService.m */, - F68103B61D2568D10053658E /* OIDAuthorizationUICoordinator.h */, 341741BA1C5D8243000EF209 /* OIDAuthState.h */, 341741BB1C5D8243000EF209 /* OIDAuthState.m */, 341741BC1C5D8243000EF209 /* OIDAuthStateChangeDelegate.h */, @@ -753,6 +768,9 @@ 341741C01C5D8243000EF209 /* OIDError.m */, 341741C11C5D8243000EF209 /* OIDErrorUtilities.h */, 341741C21C5D8243000EF209 /* OIDErrorUtilities.m */, + A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */, + A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */, + A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */, 341741C31C5D8243000EF209 /* OIDFieldMapping.h */, 341741C41C5D8243000EF209 /* OIDFieldMapping.m */, 60140F7E1DE4335200DA0DC3 /* OIDRegistrationResponse.h */, @@ -852,12 +870,12 @@ children = ( F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */, F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */, - 345AE746202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.h */, - 345AE745202D526800738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m */, + 345AE746202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.h */, + 345AE745202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m */, F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */, F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */, - F6F60FB41D2BFEFE00325CB3 /* OIDAuthorizationUICoordinatorIOS.h */, - F6F60FB21D2BFEFE00325CB3 /* OIDAuthorizationUICoordinatorIOS.m */, + A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.h */, + A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m */, ); name = iOS; sourceTree = ""; @@ -880,10 +898,10 @@ files = ( 343AAAE41E83499000F9D36E /* OIDAuthorizationResponse.h in Headers */, 343AAAF31E83499000F9D36E /* OIDScopes.h in Headers */, + A6DEAB9F2018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAAE81E83499000F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, - 345AE749202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.h in Headers */, + 345AE749202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.h in Headers */, 343AAA6B1E83465500F9D36E /* AppAuth.h in Headers */, - 343AAA6E1E83466B00F9D36E /* OIDAuthorizationUICoordinatorIOS.h in Headers */, 343AAAF21E83499000F9D36E /* OIDResponseTypes.h in Headers */, 343AAAF71E83499000F9D36E /* OIDTokenRequest.h in Headers */, 343AAAF41E83499000F9D36E /* OIDScopeUtilities.h in Headers */, @@ -892,17 +910,20 @@ 343AAAF11E83499000F9D36E /* OIDGrantTypes.h in Headers */, 343AAA6D1E83466B00F9D36E /* OIDAuthState+IOS.h in Headers */, 343AAAEF1E83499000F9D36E /* OIDRegistrationResponse.h in Headers */, + A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */, + A6DEABA42018E4B90022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */, 343AAAF51E83499000F9D36E /* OIDServiceConfiguration.h in Headers */, 343AAAE91E83499000F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAAE51E83499000F9D36E /* OIDAuthorizationService.h in Headers */, 343AAAF01E83499000F9D36E /* OIDRegistrationRequest.h in Headers */, - 343AAAE61E83499000F9D36E /* OIDAuthorizationUICoordinator.h in Headers */, 343AAAE71E83499000F9D36E /* OIDAuthState.h in Headers */, 343AAAED1E83499000F9D36E /* OIDErrorUtilities.h in Headers */, 343AAA6C1E83466B00F9D36E /* OIDAuthorizationService+IOS.h in Headers */, 343AAAE31E83499000F9D36E /* OIDAuthorizationRequest.h in Headers */, 343AAAF91E83499000F9D36E /* OIDTokenUtilities.h in Headers */, 343AAAEC1E83499000F9D36E /* OIDError.h in Headers */, + A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.h in Headers */, + A6339DAA20321ADD0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -913,13 +934,16 @@ 343AAB041E83499100F9D36E /* OIDError.h in Headers */, 343AAB091E83499100F9D36E /* OIDGrantTypes.h in Headers */, 343AAAFF1E83499100F9D36E /* OIDAuthState.h in Headers */, + A6DEABA52018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */, 343AAB0D1E83499100F9D36E /* OIDServiceConfiguration.h in Headers */, + A6DEABA02018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAAFD1E83499100F9D36E /* OIDAuthorizationService.h in Headers */, 343AAB0F1E83499100F9D36E /* OIDTokenRequest.h in Headers */, 343AAB071E83499100F9D36E /* OIDRegistrationResponse.h in Headers */, 343AAB0E1E83499100F9D36E /* OIDServiceDiscovery.h in Headers */, 343AAB111E83499100F9D36E /* OIDTokenUtilities.h in Headers */, 343AAB0A1E83499100F9D36E /* OIDResponseTypes.h in Headers */, + A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */, 343AAB0B1E83499100F9D36E /* OIDScopes.h in Headers */, 343AAB9B1E834A8800F9D36E /* AppAuth.h in Headers */, 343AAB001E83499100F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, @@ -927,8 +951,8 @@ 343AAB101E83499100F9D36E /* OIDTokenResponse.h in Headers */, 343AAAFC1E83499100F9D36E /* OIDAuthorizationResponse.h in Headers */, 343AAB0C1E83499100F9D36E /* OIDScopeUtilities.h in Headers */, + A6339DAB20321AE50043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, 343AAB011E83499100F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, - 343AAAFE1E83499100F9D36E /* OIDAuthorizationUICoordinator.h in Headers */, 343AAAFB1E83499100F9D36E /* OIDAuthorizationRequest.h in Headers */, 343AAB051E83499100F9D36E /* OIDErrorUtilities.h in Headers */, ); @@ -941,13 +965,16 @@ 343AAB1C1E83499200F9D36E /* OIDError.h in Headers */, 343AAB211E83499200F9D36E /* OIDGrantTypes.h in Headers */, 343AAB171E83499200F9D36E /* OIDAuthState.h in Headers */, + A6DEABA62018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */, 343AAB251E83499200F9D36E /* OIDServiceConfiguration.h in Headers */, + A6DEABA22018E4B60022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAB151E83499200F9D36E /* OIDAuthorizationService.h in Headers */, 343AAB271E83499200F9D36E /* OIDTokenRequest.h in Headers */, 343AAB1F1E83499200F9D36E /* OIDRegistrationResponse.h in Headers */, 343AAB261E83499200F9D36E /* OIDServiceDiscovery.h in Headers */, 343AAB291E83499200F9D36E /* OIDTokenUtilities.h in Headers */, 343AAB221E83499200F9D36E /* OIDResponseTypes.h in Headers */, + A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */, 343AAB231E83499200F9D36E /* OIDScopes.h in Headers */, 343AAB9C1E834A8900F9D36E /* AppAuth.h in Headers */, 343AAB181E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, @@ -955,8 +982,8 @@ 343AAB281E83499200F9D36E /* OIDTokenResponse.h in Headers */, 343AAB141E83499200F9D36E /* OIDAuthorizationResponse.h in Headers */, 343AAB241E83499200F9D36E /* OIDScopeUtilities.h in Headers */, + A6339DAC20321AE70043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, 343AAB191E83499200F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, - 343AAB161E83499200F9D36E /* OIDAuthorizationUICoordinator.h in Headers */, 343AAB131E83499200F9D36E /* OIDAuthorizationRequest.h in Headers */, 343AAB1D1E83499200F9D36E /* OIDErrorUtilities.h in Headers */, ); @@ -967,12 +994,14 @@ buildActionMask = 2147483647; files = ( 343AAB9D1E834A8A00F9D36E /* AppAuth.h in Headers */, - 343AAADF1E83494400F9D36E /* OIDAuthorizationUICoordinatorMac.h in Headers */, + A6DEABA72018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */, + 343AAADF1E83494400F9D36E /* OIDExternalUserAgentUICoordinatorMac.h in Headers */, 343AAAE01E83494400F9D36E /* OIDAuthState+Mac.h in Headers */, 343AAADD1E83494400F9D36E /* OIDRedirectHTTPHandler.h in Headers */, 343AAB3C1E83499200F9D36E /* OIDScopeUtilities.h in Headers */, 343AAB3F1E83499200F9D36E /* OIDTokenRequest.h in Headers */, 343AAB411E83499200F9D36E /* OIDTokenUtilities.h in Headers */, + A6DEABA32018E4B70022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAB371E83499200F9D36E /* OIDRegistrationResponse.h in Headers */, 343AAB2B1E83499200F9D36E /* OIDAuthorizationRequest.h in Headers */, 343AAB3B1E83499200F9D36E /* OIDScopes.h in Headers */, @@ -986,9 +1015,10 @@ 343AAAE11E83494A00F9D36E /* OIDLoopbackHTTPServer.h in Headers */, 343AAB311E83499200F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAB2F1E83499200F9D36E /* OIDAuthState.h in Headers */, + A6339DAD20321AEB0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, 343AAB3E1E83499200F9D36E /* OIDServiceDiscovery.h in Headers */, + A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */, 343AAADE1E83494400F9D36E /* OIDAuthorizationService+Mac.h in Headers */, - 343AAB2E1E83499200F9D36E /* OIDAuthorizationUICoordinator.h in Headers */, 343AAB301E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, 343AAB381E83499200F9D36E /* OIDRegistrationRequest.h in Headers */, 343AAB2D1E83499200F9D36E /* OIDAuthorizationService.h in Headers */, @@ -1437,7 +1467,7 @@ 341310C91E6F944B00D5DEE5 /* OIDScopes.m in Sources */, 341310CE1E6F944B00D5DEE5 /* OIDTokenResponse.m in Sources */, 341310C31E6F944B00D5DEE5 /* OIDErrorUtilities.m in Sources */, - 340DAE581D5821A100EC285B /* OIDAuthorizationUICoordinatorMac.m in Sources */, + 340DAE581D5821A100EC285B /* OIDExternalUserAgentUICoordinatorMac.m in Sources */, 340DAE5A1D5821AB00EC285B /* OIDAuthorizationRequest.m in Sources */, 347423E41E7F3C4000D3E6D6 /* OIDAuthorizationResponse.m in Sources */, 340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */, @@ -1452,6 +1482,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m in Sources */, 341741E01C5D8243000EF209 /* OIDErrorUtilities.m in Sources */, 341741EA1C5D8243000EF209 /* OIDTokenUtilities.m in Sources */, 341741E21C5D8243000EF209 /* OIDGrantTypes.m in Sources */, @@ -1465,8 +1496,7 @@ 60140F7A1DE4276800DA0DC3 /* OIDClientMetadataParameters.m in Sources */, 341741DE1C5D8243000EF209 /* OIDAuthState.m in Sources */, 341741DD1C5D8243000EF209 /* OIDAuthorizationService.m in Sources */, - 345AE747202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m in Sources */, - 340DAECD1D582DE100EC285B /* OIDAuthorizationUICoordinatorIOS.m in Sources */, + 345AE747202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m in Sources */, 341741EB1C5D8243000EF209 /* OIDURLQueryComponent.m in Sources */, 341741E11C5D8243000EF209 /* OIDFieldMapping.m in Sources */, 039697461FA8258D003D1FB2 /* OIDURLSessionProvider.m in Sources */, @@ -1575,12 +1605,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m in Sources */, 343AAA881E83478900F9D36E /* OIDFieldMapping.m in Sources */, 343AAA841E83478900F9D36E /* OIDAuthState.m in Sources */, 343AAA701E83467D00F9D36E /* OIDAuthState+IOS.m in Sources */, 343AAA921E83478900F9D36E /* OIDTokenResponse.m in Sources */, 343AAA871E83478900F9D36E /* OIDErrorUtilities.m in Sources */, - 343AAA711E83467D00F9D36E /* OIDAuthorizationUICoordinatorIOS.m in Sources */, 343AAA811E83477100F9D36E /* OIDURLQueryComponent.m in Sources */, 343AAA721E83469600F9D36E /* OIDAuthorizationRequest.m in Sources */, 343AAA831E83478900F9D36E /* OIDAuthorizationService.m in Sources */, @@ -1588,7 +1618,7 @@ 343AAA931E83478900F9D36E /* OIDTokenUtilities.m in Sources */, 343AAA901E83478900F9D36E /* OIDServiceDiscovery.m in Sources */, 343AAA911E83478900F9D36E /* OIDTokenRequest.m in Sources */, - 345AE748202D526900738D22 /* OIDAuthorizationUICoordinatorCustomBrowser.m in Sources */, + 345AE748202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m in Sources */, 343AAA6F1E83467D00F9D36E /* OIDAuthorizationService+IOS.m in Sources */, 343AAA8F1E83478900F9D36E /* OIDServiceConfiguration.m in Sources */, 343AAA891E83478900F9D36E /* OIDRegistrationResponse.m in Sources */, @@ -1724,7 +1754,7 @@ 343AAB4F1E8349AF00F9D36E /* OIDScopes.m in Sources */, 343AAB541E8349AF00F9D36E /* OIDTokenResponse.m in Sources */, 343AAB491E8349AF00F9D36E /* OIDErrorUtilities.m in Sources */, - 343AAADB1E83493D00F9D36E /* OIDAuthorizationUICoordinatorMac.m in Sources */, + 343AAADB1E83493D00F9D36E /* OIDExternalUserAgentUICoordinatorMac.m in Sources */, 343AAB471E8349AF00F9D36E /* OIDClientMetadataParameters.m in Sources */, 343AAB461E8349AF00F9D36E /* OIDAuthState.m in Sources */, 34AF736D1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */, diff --git a/Source/AppAuth.h b/Source/AppAuth.h index fae50153f..6abddd3f0 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -17,14 +17,16 @@ */ #import "OIDAuthState.h" +#import "OIDAuthorizationFlowSession.h" #import "OIDAuthStateChangeDelegate.h" #import "OIDAuthStateErrorDelegate.h" #import "OIDAuthorizationRequest.h" #import "OIDAuthorizationResponse.h" #import "OIDAuthorizationService.h" -#import "OIDAuthorizationUICoordinator.h" #import "OIDError.h" #import "OIDErrorUtilities.h" +#import "OIDExternalUserAgentFlowSession.h" +#import "OIDExternalUserAgentUICoordinator.h" #import "OIDGrantTypes.h" #import "OIDRegistrationRequest.h" #import "OIDRegistrationResponse.h" @@ -43,12 +45,11 @@ #elif TARGET_OS_IOS #import "OIDAuthState+IOS.h" #import "OIDAuthorizationService+IOS.h" -#import "OIDAuthorizationUICoordinatorCustomBrowser.h" -#import "OIDAuthorizationUICoordinatorIOS.h" +#import "OIDExternalUserAgentUICoordinatorCustomBrowser.h" #elif TARGET_OS_MAC #import "OIDAuthState+Mac.h" #import "OIDAuthorizationService+Mac.h" -#import "OIDAuthorizationUICoordinatorMac.h" +#import "OIDExternalUserAgentUICoordinatorMac.h" #import "OIDRedirectHTTPHandler.h" #else #error "Platform Undefined" diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index c5a3ca26b..4891f31bb 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -30,7 +30,7 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import -#import +#import #import #import #import @@ -50,12 +50,12 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #elif TARGET_OS_IOS #import #import -#import -#import +#import +#import #elif TARGET_OS_MAC #import #import -#import +#import #import #else #error "Platform Undefined" diff --git a/Source/OIDAuthState.h b/Source/OIDAuthState.h index 8c5e11b5f..c45c5a32b 100644 --- a/Source/OIDAuthState.h +++ b/Source/OIDAuthState.h @@ -24,9 +24,10 @@ @class OIDTokenResponse; @class OIDTokenRequest; @protocol OIDAuthorizationFlowSession; -@protocol OIDAuthorizationUICoordinator; @protocol OIDAuthStateChangeDelegate; @protocol OIDAuthStateErrorDelegate; +@protocol OIDExternalUserAgentUICoordinator; +@protocol OIDExternalUserAgentFlowSession; NS_ASSUME_NONNULL_BEGIN @@ -142,16 +143,15 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt /*! @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. @param authorizationRequest The authorization request to present. - @param UICoordinator Generic authorization UI coordinator that can present an authorization - request. + @param UICoordinator Generic UI coordinator that can present an external user-agent request. @param callback The method called when the request has completed or failed. - @return A @c OIDAuthorizationFlowSession instance which will terminate when it - receives a @c OIDAuthorizationFlowSession.cancel message, or after processing a - @c OIDAuthorizationFlowSession.resumeAuthorizationFlowWithURL: message. + @return A @c OIDExternalUserAgentFlowSession instance which will terminate when it + receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a + @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest - UICoordinator:(id)UICoordinator + UICoordinator:(id)UICoordinator callback:(OIDAuthStateAuthorizationCallback)callback; /*! @internal diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index 57a47aa91..c2832b5d1 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -104,12 +104,12 @@ @implementation OIDAuthState #pragma mark - Convenience initializers -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest - UICoordinator:(id)UICoordinator + UICoordinator:(id)UICoordinator callback:(OIDAuthStateAuthorizationCallback)callback { // presents the authorization request - id authFlowSession = [OIDAuthorizationService + id authFlowSession = [OIDAuthorizationService presentAuthorizationRequest:authorizationRequest UICoordinator:UICoordinator callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, @@ -157,7 +157,7 @@ - (nonnull instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithAuthorizationResponse:tokenResponse:)); /*! @brief Creates an auth state from an authorization response. - @param response The authorization response. + @param authorizationResponse The authorization response. */ - (instancetype)initWithAuthorizationResponse:(OIDAuthorizationResponse *)authorizationResponse { return [self initWithAuthorizationResponse:authorizationResponse tokenResponse:nil]; @@ -165,7 +165,7 @@ - (instancetype)initWithAuthorizationResponse:(OIDAuthorizationResponse *)author /*! @brief Designated initializer. - @param response The authorization response. + @param authorizationResponse The authorization response. @discussion Creates an auth state from an authorization response and token response. */ - (instancetype)initWithAuthorizationResponse:(OIDAuthorizationResponse *)authorizationResponse diff --git a/Source/OIDAuthorizationFlowSession.h b/Source/OIDAuthorizationFlowSession.h new file mode 100644 index 000000000..0201e3520 --- /dev/null +++ b/Source/OIDAuthorizationFlowSession.h @@ -0,0 +1,42 @@ +/*! @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. + */ + +/*! @brief Represents an in-flight authorization flow session. + */ +@protocol OIDAuthorizationFlowSession + +/*! @brief Clients should call this method with the result of the authorization code flow if it + becomes available. + @param URL The redirect URL invoked by the authorization server. + @discussion When the URL represented a valid authorization response, implementations + should clean up any left-over UI state from the authorization, for example by + closing the \SFSafariViewController or looback HTTP listener if those were used. + The completion block of the pending authorization 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)resumeAuthorizationFlowWithURL:(NSURL *)URL; + +/*! @brief @c OIDAuthorizationUICoordinator or clients should call this method when the + authorization flow failed with a non-OAuth error. + @param error The error that is the reason for the failure of this authorization flow. + @remarks Has no effect if called more than once, or after a @c cancel message was received. + */ +- (void)failAuthorizationFlowWithError:(NSError *)error; + +@end diff --git a/Source/OIDAuthorizationRequest.h b/Source/OIDAuthorizationRequest.h index 971557ed9..c9611ea15 100644 --- a/Source/OIDAuthorizationRequest.h +++ b/Source/OIDAuthorizationRequest.h @@ -20,6 +20,7 @@ // 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" @@ -37,7 +38,7 @@ extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256; @see https://tools.ietf.org/html/rfc6749#section-4 @see https://tools.ietf.org/html/rfc6749#section-4.1.1 */ -@interface OIDAuthorizationRequest : NSObject { +@interface OIDAuthorizationRequest : NSObject { // property variables OIDServiceConfiguration *_configuration; NSString *_responseType; diff --git a/Source/OIDAuthorizationRequest.m b/Source/OIDAuthorizationRequest.m index 3c00ecb78..73052b09d 100644 --- a/Source/OIDAuthorizationRequest.m +++ b/Source/OIDAuthorizationRequest.m @@ -329,4 +329,14 @@ - (NSURL *)authorizationRequestURL { return [query URLByReplacingQueryInURL:_configuration.authorizationEndpoint]; } +#pragma mark - OIDExternalUserAgentRequest + +- (NSURL *)externalUserAgentRequestURL { + return [self authorizationRequestURL]; +} + +- (NSString *)redirectScheme { + return [[self redirectURL] scheme]; +} + @end diff --git a/Source/OIDAuthorizationService.h b/Source/OIDAuthorizationService.h index fe80c0b41..6bf8f3418 100644 --- a/Source/OIDAuthorizationService.h +++ b/Source/OIDAuthorizationService.h @@ -27,7 +27,8 @@ @class OIDTokenRequest; @class OIDTokenResponse; @protocol OIDAuthorizationFlowSession; -@protocol OIDAuthorizationUICoordinator; +@protocol OIDExternalUserAgentFlowSession; +@protocol OIDExternalUserAgentUICoordinator; NS_ASSUME_NONNULL_BEGIN @@ -115,13 +116,13 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg @param UICoordinator Generic authorization UI coordinator that can present an authorization request. @param callback The method called when the request has completed or failed. - @return A @c OIDAuthorizationFlowSession instance which will terminate when it - receives a @c OIDAuthorizationFlowSession.cancel message, or after processing a - @c OIDAuthorizationFlowSession.resumeAuthorizationFlowWithURL: message. + @return A @c OIDExternalUserAgentFlowSession instance which will terminate when it + receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a + @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id) ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request - UICoordinator:(id)UICoordinator + UICoordinator:(id)UICoordinator callback:(OIDAuthorizationCallback)callback; /*! @brief Performs a token request. @@ -139,38 +140,4 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg @end -/*! @brief Represents an in-flight authorization flow session. - */ -@protocol OIDAuthorizationFlowSession - -/*! @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 OIDAuthorizationFlowSession.resumeAuthorizationFlowWithURL: 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 Clients should call this method with the result of the authorization code flow if it - becomes available. - @param URL The redirect URL invoked by the authorization server. - @discussion When the URL represented a valid authorization response, implementations - should clean up any left-over UI state from the authorization, for example by - closing the \SFSafariViewController or looback HTTP listener if those were used. - The completion block of the pending authorization 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)resumeAuthorizationFlowWithURL:(NSURL *)URL; - -/*! @brief @c OIDAuthorizationUICoordinator or clients should call this method when the - authorization flow failed with a non-OAuth error. - @param error The error that is the reason for the failure of this authorization flow. - @remarks Has no effect if called more than once, or after a @c cancel message was received. - */ -- (void)failAuthorizationFlowWithError:(NSError *)error; - -@end - NS_ASSUME_NONNULL_END diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 061cff681..9ee53f6b7 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -20,9 +20,11 @@ #import "OIDAuthorizationRequest.h" #import "OIDAuthorizationResponse.h" -#import "OIDAuthorizationUICoordinator.h" #import "OIDDefines.h" #import "OIDErrorUtilities.h" +#import "OIDAuthorizationFlowSession.h" +#import "OIDExternalUserAgentFlowSession.h" +#import "OIDExternalUserAgentUICoordinator.h" #import "OIDRegistrationRequest.h" #import "OIDRegistrationResponse.h" #import "OIDServiceConfiguration.h" @@ -40,10 +42,10 @@ NS_ASSUME_NONNULL_BEGIN -@interface OIDAuthorizationFlowSessionImplementation : NSObject { +@interface OIDAuthorizationFlowSessionImplementation : NSObject { // private variables OIDAuthorizationRequest *_request; - id _UICoordinator; + id _UICoordinator; OIDAuthorizationCallback _pendingauthorizationFlowCallback; } @@ -64,12 +66,12 @@ - (instancetype)initWithRequest:(OIDAuthorizationRequest *)request { return self; } -- (void)presentAuthorizationWithCoordinator:(id)UICoordinator +- (void)presentAuthorizationWithCoordinator:(id)UICoordinator callback:(OIDAuthorizationCallback)authorizationFlowCallback { _UICoordinator = UICoordinator; _pendingauthorizationFlowCallback = authorizationFlowCallback; BOOL authorizationFlowStarted = - [_UICoordinator presentAuthorizationRequest:_request session:self]; + [_UICoordinator presentExternalUserAgentRequest:_request session:self]; if (!authorizationFlowStarted) { NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeSafariOpenError underlyingError:nil @@ -79,14 +81,13 @@ - (void)presentAuthorizationWithCoordinator:(id)U } - (void)cancel { - [_UICoordinator dismissAuthorizationAnimated:YES - completion:^{ - NSError *error = [OIDErrorUtilities - errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow - underlyingError:nil - description:nil]; - [self didFinishWithResponse:nil error:error]; - }]; + [_UICoordinator dismissExternalUserAgentUIAnimated:YES completion:^{ + NSError *error = [OIDErrorUtilities + errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow + underlyingError:nil + description:nil]; + [self didFinishWithResponse:nil error:error]; + }]; } - (BOOL)shouldHandleURL:(NSURL *)URL { @@ -101,7 +102,7 @@ - (BOOL)shouldHandleURL:(NSURL *)URL { OIDIsEqualIncludingNil(standardizedURL.path, standardizedRedirectURL.path); } -- (BOOL)resumeAuthorizationFlowWithURL:(NSURL *)URL { +- (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; @@ -145,15 +146,14 @@ - (BOOL)resumeAuthorizationFlowWithURL:(NSURL *)URL { } } - [_UICoordinator dismissAuthorizationAnimated:YES - completion:^{ - [self didFinishWithResponse:response error:error]; - }]; + [_UICoordinator dismissExternalUserAgentUIAnimated:YES completion:^{ + [self didFinishWithResponse:response error:error]; + }]; return YES; } -- (void)failAuthorizationFlowWithError:(NSError *)error { +- (void)failExternalUserAgentFlowWithError:(NSError *)error { [self didFinishWithResponse:nil error:error]; } @@ -171,6 +171,14 @@ - (void)didFinishWithResponse:(nullable OIDAuthorizationResponse *)response } } +- (void)failAuthorizationFlowWithError:(NSError *)error { + [self failAuthorizationFlowWithError:error]; +} + +- (BOOL)resumeAuthorizationFlowWithURL:(NSURL *)URL { + return [self resumeExternalUserAgentFlowWithURL:URL]; +} + @end @implementation OIDAuthorizationService @@ -245,9 +253,9 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL #pragma mark - Authorization Endpoint -+ (id) ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request - UICoordinator:(id)UICoordinator + UICoordinator:(id)UICoordinator callback:(OIDAuthorizationCallback)callback { OIDAuthorizationFlowSessionImplementation *flowSession = [[OIDAuthorizationFlowSessionImplementation alloc] initWithRequest:request]; diff --git a/Source/OIDAuthorizationUICoordinator.h b/Source/OIDAuthorizationUICoordinator.h deleted file mode 100644 index d42d8c5f5..000000000 --- a/Source/OIDAuthorizationUICoordinator.h +++ /dev/null @@ -1,53 +0,0 @@ -/*! @file OIDAuthorizationUICoordinator.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 - -@class OIDAuthorizationRequest; -@protocol OIDAuthorizationFlowSession; - -NS_ASSUME_NONNULL_BEGIN - -/*! @protocol OIDAuthorizationUICoordinator - @brief An authorization UI coordinator that presents an authorization request. Clients may - provide custom implementations of an authorization UI coordinator to customize the way the - authorization request is presented to the user. - */ -@protocol OIDAuthorizationUICoordinator - -/*! @brief Presents the authroization request in the user agent. - @param request The authorizatoin request to be presented in the user agent. - @param session The @c OIDAuthorizationFlowSession instance that initiates presenting the - authorization UI. Concrete implementations of a @c OIDAuthorizationUICoordinator may call - resumeAuthorizationFlowWithURL or failAuthorizationFlowWithError on session to either - resume or fail the authorization. - @return YES If the authorization UI was successfully presented to the user. - */ -- (BOOL)presentAuthorizationRequest:(OIDAuthorizationRequest *)request - session:(id)session; - -/*! @brief Dimisses the authorization UI and calls completion when the dismiss operation ends. - @param animated Wheter or not the dismiss operation should be animated. - @remarks Has no effect if no authorization UI is presented. - @param completion The block to be called when the dismiss operations ends - */ -- (void)dismissAuthorizationAnimated:(BOOL)animated completion:(void (^)(void))completion; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Source/OIDExternalUserAgentFlowSession.h b/Source/OIDExternalUserAgentFlowSession.h new file mode 100644 index 000000000..2123bd2ce --- /dev/null +++ b/Source/OIDExternalUserAgentFlowSession.h @@ -0,0 +1,52 @@ +/*! @file OIDExternalUserAgentFlowSession.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. + */ + + +/*! @brief Represents an in-flight external user-agent flow session. + */ +@protocol OIDExternalUserAgentFlowSession + +/*! @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 OIDExternalUserAgentFlowSession.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 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 OIDExternalUserAgentUICoordinator 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 diff --git a/Source/OIDExternalUserAgentRequest.h b/Source/OIDExternalUserAgentRequest.h new file mode 100644 index 000000000..18cff8f45 --- /dev/null +++ b/Source/OIDExternalUserAgentRequest.h @@ -0,0 +1,37 @@ +/*! @file OIDExternalUserAgentUICoordinator.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 OIDExternalUserAgentUICoordinator + @brief An interface that any external user-agent request may implement to use the + @c OIDExternalUserAgentUICoordinator 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 diff --git a/Source/OIDExternalUserAgentUICoordinator.h b/Source/OIDExternalUserAgentUICoordinator.h new file mode 100644 index 000000000..1941e72b0 --- /dev/null +++ b/Source/OIDExternalUserAgentUICoordinator.h @@ -0,0 +1,53 @@ +/*! @file OIDExternalUserAgentUICoordinator.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 + +@protocol OIDExternalUserAgentFlowSession; +@protocol OIDExternalUserAgentRequest; + +NS_ASSUME_NONNULL_BEGIN + +/*! @protocol OIDExternalUserAgentUICoordinator + @brief An external user-agent UI coordinator that presents a request. Clients may provide custom + implementations of an external UI coordinator to customize the way the requests are + presented to the end user. + */ +@protocol OIDExternalUserAgentUICoordinator + +/*! @brief Presents the external user-agent request in the UI coordinator. + @param request The request to be presented in the UI. + @param session The @c OIDExternalUserAgentFlowSession instance that initiates presenting the UI. + Concrete implementations of a @c OIDExternalUserAgentUICoordinator 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 )request + session:(id)session; + +/*! @brief Dimisses the external user-agent UI and calls completion when the dismiss operation ends. + @param animated Wheter 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)dismissExternalUserAgentUIAnimated:(BOOL)animated completion:(void (^)(void))completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/iOS/OIDAuthState+IOS.h b/Source/iOS/OIDAuthState+IOS.h index 80f162587..a55fab2c7 100644 --- a/Source/iOS/OIDAuthState+IOS.h +++ b/Source/iOS/OIDAuthState+IOS.h @@ -32,11 +32,11 @@ NS_ASSUME_NONNULL_BEGIN @param presentingViewController The view controller from which to present the @c SFSafariViewController. @param callback The method called when the request has completed or failed. - @return A @c OIDAuthorizationFlowSession instance which will terminate when it - receives a @c OIDAuthorizationFlowSession.cancel message, or after processing a - @c OIDAuthorizationFlowSession.resumeAuthorizationFlowWithURL: message. + @return A @c OIDExternalUserAgentFlowSession instance which will terminate when it + receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a + @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthStateAuthorizationCallback)callback; diff --git a/Source/iOS/OIDAuthState+IOS.m b/Source/iOS/OIDAuthState+IOS.m index 3e32a8be9..01e60c21e 100644 --- a/Source/iOS/OIDAuthState+IOS.m +++ b/Source/iOS/OIDAuthState+IOS.m @@ -18,15 +18,15 @@ #import "OIDAuthState+IOS.h" -#import "OIDAuthorizationUICoordinatorIOS.h" +#import "OIDExternalUserAgentUICoordinatorIOS.h" @implementation OIDAuthState (IOS) -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthStateAuthorizationCallback)callback { - OIDAuthorizationUICoordinatorIOS *coordinator = [[OIDAuthorizationUICoordinatorIOS alloc] + OIDExternalUserAgentUICoordinatorIOS *coordinator = [[OIDExternalUserAgentUICoordinatorIOS alloc] initWithPresentingViewController:presentingViewController]; return [self authStateByPresentingAuthorizationRequest:authorizationRequest UICoordinator:coordinator diff --git a/Source/iOS/OIDAuthorizationService+IOS.h b/Source/iOS/OIDAuthorizationService+IOS.h index 3d76340e8..fc81a4460 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.h +++ b/Source/iOS/OIDAuthorizationService+IOS.h @@ -19,6 +19,7 @@ #import #import "OIDAuthorizationService.h" +#import "OIDExternalUserAgentFlowSession.h" NS_ASSUME_NONNULL_BEGIN @@ -31,11 +32,11 @@ NS_ASSUME_NONNULL_BEGIN @param presentingViewController The view controller from which to present the \SFSafariViewController. @param callback The method called when the request has completed or failed. - @return A @c OIDAuthorizationFlowSession instance which will terminate when it - receives a @c OIDAuthorizationFlowSession.cancel message, or after processing a - @c OIDAuthorizationFlowSession.resumeAuthorizationFlowWithURL: message. + @return A @c OIDExternalUserAgentFlowSession instance which will terminate when it + receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a + @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id) ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthorizationCallback)callback; diff --git a/Source/iOS/OIDAuthorizationService+IOS.m b/Source/iOS/OIDAuthorizationService+IOS.m index 2a8d6da86..42fd7dfcc 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.m +++ b/Source/iOS/OIDAuthorizationService+IOS.m @@ -18,17 +18,17 @@ #import "OIDAuthorizationService+IOS.h" -#import "OIDAuthorizationUICoordinatorIOS.h" +#import "OIDExternalUserAgentUICoordinatorIOS.h" NS_ASSUME_NONNULL_BEGIN @implementation OIDAuthorizationService (IOS) -+ (id) ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthorizationCallback)callback { - OIDAuthorizationUICoordinatorIOS *coordinator = [[OIDAuthorizationUICoordinatorIOS alloc] + OIDExternalUserAgentUICoordinatorIOS *coordinator = [[OIDExternalUserAgentUICoordinatorIOS alloc] initWithPresentingViewController:presentingViewController]; return [self presentAuthorizationRequest:request UICoordinator:coordinator callback:callback]; } diff --git a/Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.h b/Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.h similarity index 78% rename from Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.h rename to Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.h index 1eedb2f21..09493daf8 100644 --- a/Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.h +++ b/Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.h @@ -1,4 +1,4 @@ -/*! @file OIDAuthorizationUICoordinatorCustomBrowser.h +/*! @file OIDExternalUserAgentUICoordinatorCustomBrowser.h @brief AppAuth iOS SDK @copyright Copyright 2018 Google LLC @@ -18,7 +18,7 @@ #import -#import "OIDAuthorizationUICoordinator.h" +#import "OIDExternalUserAgentUICoordinator.h" NS_ASSUME_NONNULL_BEGIN @@ -29,13 +29,13 @@ NS_ASSUME_NONNULL_BEGIN */ typedef NSURL *_Nullable (^OIDCustomBrowserURLTransformation)(NSURL *_Nullable requestURL); -/*! @brief An implementation of the OIDAuthorizationUICoordinator protocol for iOS that uses - a custom browser (i.e. not Safari) for authorization. It is suitable for browsers that +/*! @brief An implementation of the OIDExternalUserAgentUICoordinator protocol for iOS that uses + a custom browser (i.e. not Safari) for external requests. It is suitable for browsers that offer a custom url scheme that simply replaces the "https" scheme. It is not designed for browsers that require other modifications to the URL. If the browser is not installed the user will be prompted to install it. */ -@interface OIDAuthorizationUICoordinatorCustomBrowser : NSObject +@interface OIDExternalUserAgentUICoordinatorCustomBrowser : NSObject /*! @brief URL transformation block for the browser. */ @@ -49,19 +49,19 @@ typedef NSURL *_Nullable (^OIDCustomBrowserURLTransformation)(NSURL *_Nullable r */ @property(nonatomic, readonly, nullable) NSURL *appStoreURL; -/*! @brief An instance of @c OIDAuthorizationUICoordinatorCustomBrowser for Chrome. +/*! @brief An instance of @c OIDExternalUserAgentUICoordinatorCustomBrowser for Chrome. */ + (instancetype)CustomBrowserChrome; -/*! @brief An instance of @c OIDAuthorizationUICoordinatorCustomBrowser for Firefox. +/*! @brief An instance of @c OIDExternalUserAgentUICoordinatorCustomBrowser for Firefox. */ + (instancetype)CustomBrowserFirefox; -/*! @brief An instance of @c OIDAuthorizationUICoordinatorCustomBrowser for Opera. +/*! @brief An instance of @c OIDExternalUserAgentUICoordinatorCustomBrowser for Opera. */ + (instancetype)CustomBrowserOpera; -/*! @brief An instance of @c OIDAuthorizationUICoordinatorCustomBrowser for Safari. +/*! @brief An instance of @c OIDExternalUserAgentUICoordinatorCustomBrowser for Safari. */ + (instancetype)CustomBrowserSafari; @@ -82,7 +82,7 @@ typedef NSURL *_Nullable (^OIDCustomBrowserURLTransformation)(NSURL *_Nullable r */ - (nonnull instancetype)init NS_UNAVAILABLE; -/*! @brief UICoordinator for a custom browser. @c presentAuthorizationRequest:session method +/*! @brief UICoordinator for a custom browser. @c presentExternalUserAgentRequest:session method will return NO if the browser isn't installed. */ - (nullable instancetype)initWithURLTransformation:(OIDCustomBrowserURLTransformation)URLTransformation; @@ -93,7 +93,7 @@ typedef NSURL *_Nullable (^OIDCustomBrowserURLTransformation)(NSURL *_Nullable r @param canOpenURLScheme any scheme supported by the browser used to check if the browser is installed. @param appStoreURL URL of the browser in the app store. When this and @c canOpenURLScheme - are non-nil, @c presentAuthorizationRequest:session will redirect the user to the app store + are non-nil, @c presentExternalUserAgentRequest:session will redirect the user to the app store if the browser is not installed. */ - (nullable instancetype)initWithURLTransformation:(OIDCustomBrowserURLTransformation)URLTransformation diff --git a/Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.m b/Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.m similarity index 91% rename from Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.m rename to Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.m index cba8dfe79..2285305da 100644 --- a/Source/iOS/OIDAuthorizationUICoordinatorCustomBrowser.m +++ b/Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.m @@ -1,4 +1,4 @@ -/*! @file OIDAuthorizationUICoordinatorCustomBrowser.m +/*! @file OIDExternalUserAgentUICoordinatorCustomBrowser.m @brief AppAuth iOS SDK @copyright Copyright 2018 Google LLC @@ -16,7 +16,7 @@ limitations under the License. */ -#import "OIDAuthorizationUICoordinatorCustomBrowser.h" +#import "OIDExternalUserAgentUICoordinatorCustomBrowser.h" #import @@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN -@implementation OIDAuthorizationUICoordinatorCustomBrowser { +@implementation OIDExternalUserAgentUICoordinatorCustomBrowser { OIDCustomBrowserURLTransformation _URLTransformation; NSString *_canOpenURLScheme; NSURL *_appStoreURL; @@ -72,7 +72,7 @@ + (instancetype)CustomBrowserSafari { OIDCustomBrowserURLTransformation transformNOP = ^NSURL *(NSURL *requestURL) { return requestURL; }; - OIDAuthorizationUICoordinatorCustomBrowser *coordinator = + OIDExternalUserAgentUICoordinatorCustomBrowser *coordinator = [[[self class] alloc] initWithURLTransformation:transformNOP]; return coordinator; } @@ -132,8 +132,8 @@ - (nullable instancetype)initWithURLTransformation: return self; } -- (BOOL)presentAuthorizationRequest:(OIDAuthorizationRequest *)request - session:(id)session { +- (BOOL)presentExternalUserAgentRequest:(nonnull id)request + session:(nonnull id)session { // If the app store URL is set, checks if the app is installed and if not opens the app store. if (_appStoreURL && _canOpenURLScheme) { // Verifies existence of LSApplicationQueriesSchemes Info.plist key. @@ -153,13 +153,14 @@ - (BOOL)presentAuthorizationRequest:(OIDAuthorizationRequest *)request } // Transforms the request URL and opens it. - NSURL *requestURL = [request authorizationRequestURL]; + NSURL *requestURL = [request externalUserAgentRequestURL]; requestURL = _URLTransformation(requestURL); BOOL openedInBrowser = [[UIApplication sharedApplication] openURL:requestURL]; return openedInBrowser; } -- (void)dismissAuthorizationAnimated:(BOOL)animated completion:(nonnull void (^)(void))completion { +- (void)dismissExternalUserAgentUIAnimated:(BOOL)animated + completion:(nonnull void (^)(void))completion { completion(); } diff --git a/Source/iOS/OIDAuthorizationUICoordinatorIOS.h b/Source/iOS/OIDExternalUserAgentUICoordinatorIOS.h similarity index 86% rename from Source/iOS/OIDAuthorizationUICoordinatorIOS.h rename to Source/iOS/OIDExternalUserAgentUICoordinatorIOS.h index 2e6295f24..aeddbddfa 100644 --- a/Source/iOS/OIDAuthorizationUICoordinatorIOS.h +++ b/Source/iOS/OIDExternalUserAgentUICoordinatorIOS.h @@ -1,4 +1,4 @@ -/*! @file OIDAuthorizationUICoordinator.h +/*! @file OIDExternalUserAgentUICoordinatorIOS.h @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. All Rights Reserved. @@ -18,7 +18,7 @@ #import -#import "OIDAuthorizationUICoordinator.h" +#import "OIDExternalUserAgentUICoordinator.h" @class SFSafariViewController; @@ -36,10 +36,10 @@ NS_ASSUME_NONNULL_BEGIN @end -/*! @brief An iOS specific authorization UI Coordinator that uses a \SFSafariViewController to - present an authorization request. +/*! @brief An iOS specific external user-agent UI Coordinator that uses a \SFSafariViewController to + present an user-agent request. */ -@interface OIDAuthorizationUICoordinatorIOS : NSObject +@interface OIDExternalUserAgentUICoordinatorIOS : NSObject /*! @brief Allows library consumers to change the @c OIDSafariViewControllerFactory used to create new instances of @c SFSafariViewController. diff --git a/Source/iOS/OIDAuthorizationUICoordinatorIOS.m b/Source/iOS/OIDExternalUserAgentUICoordinatorIOS.m similarity index 82% rename from Source/iOS/OIDAuthorizationUICoordinatorIOS.m rename to Source/iOS/OIDExternalUserAgentUICoordinatorIOS.m index 70bd3104e..12149065a 100644 --- a/Source/iOS/OIDAuthorizationUICoordinatorIOS.m +++ b/Source/iOS/OIDExternalUserAgentUICoordinatorIOS.m @@ -16,13 +16,13 @@ limitations under the License. */ -#import "OIDAuthorizationUICoordinatorIOS.h" +#import "OIDExternalUserAgentUICoordinatorIOS.h" #import -#import "OIDAuthorizationRequest.h" -#import "OIDAuthorizationService.h" #import "OIDErrorUtilities.h" +#import "OIDExternalUserAgentFlowSession.h" +#import "OIDExternalUserAgentRequest.h" NS_ASSUME_NONNULL_BEGIN @@ -37,14 +37,14 @@ @interface OIDDefaultSafariViewControllerFactory : NSObject @end -@interface OIDAuthorizationUICoordinatorIOS () +@interface OIDExternalUserAgentUICoordinatorIOS () @end -@implementation OIDAuthorizationUICoordinatorIOS { +@implementation OIDExternalUserAgentUICoordinatorIOS { UIViewController *_presentingViewController; - BOOL _authorizationFlowInProgress; - __weak id _session; + BOOL _externalUserAgentFlowInProgress; + __weak id _session; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpartial-availability" __weak SFSafariViewController *_safariVC; @@ -76,20 +76,20 @@ - (nullable instancetype)initWithPresentingViewController: return self; } -- (BOOL)presentAuthorizationRequest:(OIDAuthorizationRequest *)request - session:(id)session { - if (_authorizationFlowInProgress) { +- (BOOL)presentExternalUserAgentRequest:(id)request + session:(id)session { + if (_externalUserAgentFlowInProgress) { // TODO: Handle errors as authorization is already in progress. return NO; } - _authorizationFlowInProgress = YES; + _externalUserAgentFlowInProgress = YES; _session = session; BOOL openedSafari = NO; - NSURL *requestURL = [request authorizationRequestURL]; + NSURL *requestURL = [request externalUserAgentRequestURL]; if (@available(iOS 11.0, *)) { - NSString *redirectScheme = request.redirectURL.scheme; + NSString *redirectScheme = request.redirectScheme; SFAuthenticationSession* authenticationVC = [[SFAuthenticationSession alloc] initWithURL:requestURL callbackURLScheme:redirectScheme @@ -97,13 +97,13 @@ - (BOOL)presentAuthorizationRequest:(OIDAuthorizationRequest *)request NSError * _Nullable error) { _authenticationVC = nil; if (callbackURL) { - [_session resumeAuthorizationFlowWithURL:callbackURL]; + [_session resumeExternalUserAgentFlowWithURL:callbackURL]; } else { NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow underlyingError:error description:nil]; - [_session failAuthorizationFlowWithError:safariError]; + [_session failExternalUserAgentFlowWithError:safariError]; } }]; _authenticationVC = authenticationVC; @@ -124,13 +124,13 @@ - (BOOL)presentAuthorizationRequest:(OIDAuthorizationRequest *)request NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeSafariOpenError underlyingError:nil description:@"Unable to open Safari."]; - [session failAuthorizationFlowWithError:safariError]; + [session failExternalUserAgentFlowWithError:safariError]; } return openedSafari; } -- (void)dismissAuthorizationAnimated:(BOOL)animated completion:(void (^)(void))completion { - if (!_authorizationFlowInProgress) { +- (void)dismissExternalUserAgentUIAnimated:(BOOL)animated completion:(void (^)(void))completion { + if (!_externalUserAgentFlowInProgress) { // Ignore this call if there is no authorization flow in progress. return; } @@ -163,7 +163,7 @@ - (void)cleanUp { _safariVC = nil; _authenticationVC = nil; _session = nil; - _authorizationFlowInProgress = NO; + _externalUserAgentFlowInProgress = NO; } #pragma mark - SFSafariViewControllerDelegate @@ -173,16 +173,16 @@ - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller NS_AV // Ignore this call if the safari view controller do not match. return; } - if (!_authorizationFlowInProgress) { + if (!_externalUserAgentFlowInProgress) { // Ignore this call if there is no authorization flow in progress. return; } - id session = _session; + id session = _session; [self cleanUp]; NSError *error = [OIDErrorUtilities errorWithCode:OIDErrorCodeProgramCanceledAuthorizationFlow underlyingError:nil description:nil]; - [session failAuthorizationFlowWithError:error]; + [session failExternalUserAgentFlowWithError:error]; } @end diff --git a/Source/macOS/OIDAuthState+Mac.h b/Source/macOS/OIDAuthState+Mac.h index ddb6e377f..4103a75a5 100644 --- a/Source/macOS/OIDAuthState+Mac.h +++ b/Source/macOS/OIDAuthState+Mac.h @@ -28,11 +28,11 @@ NS_ASSUME_NONNULL_BEGIN and performing the authorization code exchange in the case of code flow requests. @param authorizationRequest The authorization request to present. @param callback The method called when the request has completed or failed. - @return A @c OIDAuthorizationFlowSession instance which will terminate when it - receives a @c OIDAuthorizationFlowSession.cancel message, or after processing a - @c OIDAuthorizationFlowSession.resumeAuthorizationFlowWithURL: message. + @return A @c OIDExternalUserAgentFlowSession instance which will terminate when it + receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a + @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest callback:(OIDAuthStateAuthorizationCallback)callback; @end diff --git a/Source/macOS/OIDAuthState+Mac.m b/Source/macOS/OIDAuthState+Mac.m index 02f367b6d..6e7562ca9 100644 --- a/Source/macOS/OIDAuthState+Mac.m +++ b/Source/macOS/OIDAuthState+Mac.m @@ -18,14 +18,14 @@ #import "OIDAuthState+Mac.h" -#import "OIDAuthorizationUICoordinatorMac.h" +#import "OIDExternalUserAgentUICoordinatorMac.h" @implementation OIDAuthState (Mac) -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest callback:(OIDAuthStateAuthorizationCallback)callback { - OIDAuthorizationUICoordinatorMac *coordinator = [[OIDAuthorizationUICoordinatorMac alloc] init]; + OIDExternalUserAgentUICoordinatorMac *coordinator = [[OIDExternalUserAgentUICoordinatorMac alloc] init]; return [self authStateByPresentingAuthorizationRequest:authorizationRequest UICoordinator:coordinator callback:callback]; diff --git a/Source/macOS/OIDAuthorizationService+Mac.h b/Source/macOS/OIDAuthorizationService+Mac.h index c8c98c15d..ce4cf9569 100644 --- a/Source/macOS/OIDAuthorizationService+Mac.h +++ b/Source/macOS/OIDAuthorizationService+Mac.h @@ -27,12 +27,12 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief Perform an authorization flow using the default browser. @param request The authorization request. @param callback The method called when the request has completed or failed. - @return A @c OIDAuthorizationFlowSession instance which will terminate when it - receives a @c OIDAuthorizationFlowSession.cancel message, or after processing a - @c OIDAuthorizationFlowSession.resumeAuthorizationFlowWithURL: message. + @return A @c OIDExternalUserAgentFlowSession instance which will terminate when it + receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a + @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id)presentAuthorizationRequest:(OIDAuthorizationRequest *)request - callback:(OIDAuthorizationCallback)callback; ++ (id)presentAuthorizationRequest:(OIDAuthorizationRequest *)request + callback:(OIDAuthorizationCallback)callback; @end NS_ASSUME_NONNULL_END diff --git a/Source/macOS/OIDAuthorizationService+Mac.m b/Source/macOS/OIDAuthorizationService+Mac.m index d1b138a12..c7dd2076f 100644 --- a/Source/macOS/OIDAuthorizationService+Mac.m +++ b/Source/macOS/OIDAuthorizationService+Mac.m @@ -18,15 +18,15 @@ #import "OIDAuthorizationService+Mac.h" -#import "OIDAuthorizationUICoordinatorMac.h" +#import "OIDExternalUserAgentUICoordinatorMac.h" NS_ASSUME_NONNULL_BEGIN @implementation OIDAuthorizationService (Mac) -+ (id)presentAuthorizationRequest:(OIDAuthorizationRequest *)request - callback:(OIDAuthorizationCallback)callback { - OIDAuthorizationUICoordinatorMac *coordinator = [[OIDAuthorizationUICoordinatorMac alloc] init]; ++ (id)presentAuthorizationRequest:(OIDAuthorizationRequest *)request + callback:(OIDAuthorizationCallback)callback { + OIDExternalUserAgentUICoordinatorMac *coordinator = [[OIDExternalUserAgentUICoordinatorMac alloc] init]; return [self presentAuthorizationRequest:request UICoordinator:coordinator callback:callback]; } diff --git a/Source/macOS/OIDAuthorizationUICoordinatorMac.h b/Source/macOS/OIDExternalUserAgentUICoordinatorMac.h similarity index 66% rename from Source/macOS/OIDAuthorizationUICoordinatorMac.h rename to Source/macOS/OIDExternalUserAgentUICoordinatorMac.h index 06f0a3422..81c747111 100644 --- a/Source/macOS/OIDAuthorizationUICoordinatorMac.h +++ b/Source/macOS/OIDExternalUserAgentUICoordinatorMac.h @@ -1,4 +1,4 @@ -/*! @file OIDAuthorizationUICoordinatorMac.h +/*! @file OIDExternalUserAgentUICoordinatorMac.h @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. All Rights Reserved. @@ -16,17 +16,17 @@ limitations under the License. */ -#import "OIDAuthorizationUICoordinator.h" +#import "OIDExternalUserAgentUICoordinator.h" NS_ASSUME_NONNULL_BEGIN -/*! @brief An Mac specific authorization UI Coordinator that uses the default browser to - present an authorization request. +/*! @brief A Mac-specific external user-agent UI Coordinator that uses the default browser to + present a request. */ -@interface OIDAuthorizationUICoordinatorMac : NSObject { +@interface OIDExternalUserAgentUICoordinatorMac : NSObject { // private variables - BOOL _authorizationFlowInProgress; - __weak id _session; + BOOL _externalUserAgentFlowInProgress; + __weak id _session; } @end diff --git a/Source/macOS/OIDAuthorizationUICoordinatorMac.m b/Source/macOS/OIDExternalUserAgentUICoordinatorMac.m similarity index 67% rename from Source/macOS/OIDAuthorizationUICoordinatorMac.m rename to Source/macOS/OIDExternalUserAgentUICoordinatorMac.m index 165d34694..99cb645ca 100644 --- a/Source/macOS/OIDAuthorizationUICoordinatorMac.m +++ b/Source/macOS/OIDExternalUserAgentUICoordinatorMac.m @@ -1,4 +1,4 @@ -/*! @file OIDAuthorizationUICoordinatorMac.m +/*! @file OIDExternalUserAgentUICoordinatorMac.m @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. All Rights Reserved. @@ -16,28 +16,28 @@ limitations under the License. */ -#import "OIDAuthorizationUICoordinatorMac.h" +#import "OIDExternalUserAgentUICoordinatorMac.h" #import -#import "OIDAuthorizationRequest.h" -#import "OIDAuthorizationService.h" #import "OIDErrorUtilities.h" +#import "OIDExternalUserAgentFlowSession.h" +#import "OIDExternalUserAgentRequest.h" NS_ASSUME_NONNULL_BEGIN -@implementation OIDAuthorizationUICoordinatorMac +@implementation OIDExternalUserAgentUICoordinatorMac -- (BOOL)presentAuthorizationRequest:(OIDAuthorizationRequest *)request - session:(id)session { - if (_authorizationFlowInProgress) { +- (BOOL)presentExternalUserAgentRequest:(id)request + session:(id)session { + if (_externalUserAgentFlowInProgress) { // TODO: Handle errors as authorization is already in progress. return NO; } - _authorizationFlowInProgress = YES; + _externalUserAgentFlowInProgress = YES; _session = session; - NSURL *requestURL = [request authorizationRequestURL]; + NSURL *requestURL = [request externalUserAgentRequestURL]; BOOL openedBrowser = [[NSWorkspace sharedWorkspace] openURL:requestURL]; if (!openedBrowser) { @@ -45,13 +45,13 @@ - (BOOL)presentAuthorizationRequest:(OIDAuthorizationRequest *)request NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeBrowserOpenError underlyingError:nil description:@"Unable to open the browser."]; - [session failAuthorizationFlowWithError:safariError]; + [session failExternalUserAgentFlowWithError:safariError]; } return openedBrowser; } -- (void)dismissAuthorizationAnimated:(BOOL)animated completion:(void (^)(void))completion { - if (!_authorizationFlowInProgress) { +- (void)dismissExternalUserAgentUIAnimated:(BOOL)animated completion:(void (^)(void))completion { + if (!_externalUserAgentFlowInProgress) { // Ignore this call if there is no authorization flow in progress. return; } @@ -63,7 +63,7 @@ - (void)dismissAuthorizationAnimated:(BOOL)animated completion:(void (^)(void))c - (void)cleanUp { _session = nil; - _authorizationFlowInProgress = NO; + _externalUserAgentFlowInProgress = NO; } @end diff --git a/Source/macOS/OIDRedirectHTTPHandler.h b/Source/macOS/OIDRedirectHTTPHandler.h index f445ebbea..59212fb9c 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.h +++ b/Source/macOS/OIDRedirectHTTPHandler.h @@ -22,53 +22,56 @@ NS_ASSUME_NONNULL_BEGIN @class HTTPServer; @protocol OIDAuthorizationFlowSession; +@protocol OIDExternalUserAgentFlowSession; -/*! @brief Start a HTTP server on the loopback interface (i.e. @c 127.0.0.1) to receive OAuth - authorization response redirects on macOS. +/*! @brief Start a HTTP server on the loopback interface (i.e. @c 127.0.0.1) to receive the OAuth + response redirects on macOS. */ @interface OIDRedirectHTTPHandler : NSObject { // private variables HTTPServer *_httpServ; NSURL *_successURL; // property variables - NSObject *_currentAuthorizationFlow; + NSObject *_currentAuthorizationFlow; } -/*! @brief The authorization flow session which receives the return URL from the browser. +/*! @brief The external user-agent request flow session which receives the return URL from the + browser. @discussion The loopback HTTP server will try sending incoming request URLs to the OAuth - redirect handler to continue the flow. This should be set while an authorization flow is - in progress. + redirect handler to continue the flow. This should be set while an external user-agent + request flow is in progress. */ -@property(nonatomic, strong, nullable) id currentAuthorizationFlow; +@property(nonatomic, strong, nullable) id currentAuthorizationFlow; /*! @brief Creates an a loopback HTTP redirect URI handler with the given success URL. - @param successURL The URL that the user is redirected to after the authorization flow completes + @param successURL The URL that the user is redirected to after the request flow completes either with a result of success or error. The contents of this page should instruct the user to return to the app. - @discussion Once you have initiated the authorization request, be sure to set - @c currentAuthorizationFlow on this object so that any authorization responses received by - this listener will be routed accordingly. + @discussion Once you have initiated the external user-agent request, be sure to set + @c currentExternalUserAgentFlow on this object so that any responses received by this listener will + be routed accordingly. */ - (instancetype)initWithSuccessURL:(nullable NSURL *)successURL; /*! @brief Starts listening on the loopback interface on a random available port, and returns a URL - with the base address. Use the returned redirect URI to build an @c OIDAuthorizationRequest, - and once you initiate the request, set the resulting @c OIDAuthorizationFlowSession to - @c currentAuthorizationFlow so the response can be handled. + with the base address. Use the returned redirect URI to build a @c OIDExternalUserAgentRequest, + and once you initiate the request, set the resulting @c OIDExternalUserAgentFlowSession to + @c currentExternalUserAgentFlow so the response can be handled. @param error The error if an error occurred while starting the local HTTP server. @return The URL containing the address of the server with the randomly assigned available port. - @discussion Each instance of @c OIDRedirectHTTPHandler can only listen for a single - authorization response. Calling this more than once will result in the previous listener - being cancelled (equivalent of @c cancelHTTPListener being called). + @discussion Each instance of @c OIDRedirectHTTPHandler can only listen for a single response. + Calling this more than once will result in the previous listener being cancelled (equivalent + of @c cancelHTTPListener being called). */ - (NSURL *)startHTTPListener:(NSError **)error; /*! @brief Stops listening the loopback interface and sends an cancellation error (in the domain ::OIDGeneralErrorDomain, with the code ::OIDErrorCodeProgramCanceledAuthorizationFlow) to - the @c currentAuthorizationFlow. Has no effect if called when no requests are pending. - @discussion The HTTP listener is stopped automatically on receiving a valid authorization - response (regardless of whether the authorization succeeded or not), this method should not - be called except when abandoning the authorization request. + the @c currentExternalUserAgentFlow. Has no effect if called when no requests are pending. + @discussion The HTTP listener is stopped automatically on receiving a valid response (regardless + of whether the request succeeded or not), this method should not be called except when + abandoning the external user-agent request. */ - (void)cancelHTTPListener; diff --git a/Source/macOS/OIDRedirectHTTPHandler.m b/Source/macOS/OIDRedirectHTTPHandler.m index 3003de02f..4b284e69b 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.m +++ b/Source/macOS/OIDRedirectHTTPHandler.m @@ -18,8 +18,10 @@ #import "OIDRedirectHTTPHandler.h" +#import "OIDAuthorizationFlowSession.h" #import "OIDAuthorizationService.h" #import "OIDErrorUtilities.h" +#import "OIDExternalUserAgentFlowSession.h" #import "OIDLoopbackHTTPServer.h" /*! @brief Page that is returned following a completed authorization. Show your own page instead by @@ -97,7 +99,7 @@ - (void)cancelHTTPListener { [OIDErrorUtilities errorWithCode:OIDErrorCodeProgramCanceledAuthorizationFlow underlyingError:nil description:@"The HTTP listener was cancelled programmatically."]; - [_currentAuthorizationFlow failAuthorizationFlowWithError:cancelledError]; + [_currentAuthorizationFlow failExternalUserAgentFlowWithError:cancelledError]; _currentAuthorizationFlow = nil; } @@ -114,7 +116,7 @@ - (void)stopHTTPListener { - (void)HTTPConnection:(HTTPConnection *)conn didReceiveRequest:(HTTPServerRequest *)mess { // Sends URL to AppAuth. CFURLRef url = CFHTTPMessageCopyRequestURL(mess.request); - BOOL handled = [_currentAuthorizationFlow resumeAuthorizationFlowWithURL:(__bridge NSURL *)url]; + BOOL handled = [_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:(__bridge NSURL *)url]; // Stops listening to further requests after the first valid authorization response. if (handled) { diff --git a/UnitTests/OIDAuthorizationRequestTests.m b/UnitTests/OIDAuthorizationRequestTests.m index 4a4af75af..9486440be 100644 --- a/UnitTests/OIDAuthorizationRequestTests.m +++ b/UnitTests/OIDAuthorizationRequestTests.m @@ -471,4 +471,10 @@ - (void)testSupportedResponseTypes { } +- (void)testExternalUserAgentMethods { + OIDAuthorizationRequest *request = [[self class] testInstance]; + XCTAssertEqualObjects([request externalUserAgentRequestURL], [request authorizationRequestURL]); + XCTAssert([[request redirectScheme] isEqualToString:request.redirectURL.scheme]); +} + @end From 5d321b83d8b8cd64068c774cf2377d69a61b23a3 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 7 Mar 2018 11:32:55 -0800 Subject: [PATCH 010/204] Fix link markdown syntax --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 84a2e7ca2..2b1c6db50 100644 --- a/README.md +++ b/README.md @@ -333,8 +333,7 @@ needing to worry about token freshness. ## API Documentation -Browse the [API documentation] -(http://openid.github.io/AppAuth-iOS/docs/latest/annotated.html). +Browse the [API documentation](http://openid.github.io/AppAuth-iOS/docs/latest/annotated.html). ## Included Samples From cb5670e4d9340e95f9bae7dd98fc1a35c613111b Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 7 Mar 2018 14:07:29 -0800 Subject: [PATCH 011/204] Rename OIDExternalUserAgentUICoordinator to OIDExternalUserAgent Drop the "UICoordinator". Just "OIDExternalUserAgent". It's cleaner. --- AppAuth.xcodeproj/project.pbxproj | 100 +++++++++--------- Source/AppAuth.h | 9 +- Source/Framework/AppAuth.h | 8 +- Source/OIDAuthState.h | 16 +-- Source/OIDAuthState.m | 8 +- Source/OIDAuthorizationFlowSession.h | 2 +- Source/OIDAuthorizationService.h | 14 +-- Source/OIDAuthorizationService.m | 22 ++-- ...UICoordinator.h => OIDExternalUserAgent.h} | 24 ++--- Source/OIDExternalUserAgentRequest.h | 6 +- ...ession.h => OIDExternalUserAgentSession.h} | 8 +- Source/iOS/OIDAuthState+IOS.h | 8 +- Source/iOS/OIDAuthState+IOS.m | 11 +- Source/iOS/OIDAuthorizationService+IOS.h | 10 +- Source/iOS/OIDAuthorizationService+IOS.m | 6 +- ...dinatorIOS.h => OIDExternalUserAgentIOS.h} | 6 +- ...dinatorIOS.m => OIDExternalUserAgentIOS.m} | 18 ++-- ...=> OIDExternalUserAgentIOSCustomBrowser.h} | 18 ++-- ...=> OIDExternalUserAgentIOSCustomBrowser.m} | 12 +-- Source/macOS/OIDAuthState+Mac.h | 8 +- Source/macOS/OIDAuthState+Mac.m | 8 +- Source/macOS/OIDAuthorizationService+Mac.h | 11 +- Source/macOS/OIDAuthorizationService+Mac.m | 9 +- ...dinatorMac.h => OIDExternalUserAgentMac.h} | 10 +- ...dinatorMac.m => OIDExternalUserAgentMac.m} | 12 +-- Source/macOS/OIDRedirectHTTPHandler.h | 16 +-- Source/macOS/OIDRedirectHTTPHandler.m | 2 +- 27 files changed, 193 insertions(+), 189 deletions(-) rename Source/{OIDExternalUserAgentUICoordinator.h => OIDExternalUserAgent.h} (66%) rename Source/{OIDExternalUserAgentFlowSession.h => OIDExternalUserAgentSession.h} (88%) rename Source/iOS/{OIDExternalUserAgentUICoordinatorIOS.h => OIDExternalUserAgentIOS.h} (92%) rename Source/iOS/{OIDExternalUserAgentUICoordinatorIOS.m => OIDExternalUserAgentIOS.m} (92%) rename Source/iOS/{OIDExternalUserAgentUICoordinatorCustomBrowser.h => OIDExternalUserAgentIOSCustomBrowser.h} (83%) rename Source/iOS/{OIDExternalUserAgentUICoordinatorCustomBrowser.m => OIDExternalUserAgentIOSCustomBrowser.m} (95%) rename Source/macOS/{OIDExternalUserAgentUICoordinatorMac.h => OIDExternalUserAgentMac.h} (77%) rename Source/macOS/{OIDExternalUserAgentUICoordinatorMac.m => OIDExternalUserAgentMac.m} (86%) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 5d2d45fac..ab708c81c 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -10,7 +10,7 @@ 039697461FA8258D003D1FB2 /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; 0396974D1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0396974C1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m */; }; 340DAE571D5821A100EC285B /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; - 340DAE581D5821A100EC285B /* OIDExternalUserAgentUICoordinatorMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.m */; }; + 340DAE581D5821A100EC285B /* OIDExternalUserAgentMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentMac.m */; }; 340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */; }; 340DAE5A1D5821AB00EC285B /* OIDAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */; }; 340DAE5C1D5821AB00EC285B /* OIDAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B91C5D8243000EF209 /* OIDAuthorizationService.m */; }; @@ -159,11 +159,11 @@ 343AAACB1E8348AA00F9D36E /* AppAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 343AAAC21E8348A900F9D36E /* AppAuth.framework */; }; 343AAAD91E83493D00F9D36E /* OIDRedirectHTTPHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAEBA1D582AF100EC285B /* OIDRedirectHTTPHandler.m */; }; 343AAADA1E83493D00F9D36E /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; - 343AAADB1E83493D00F9D36E /* OIDExternalUserAgentUICoordinatorMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.m */; }; + 343AAADB1E83493D00F9D36E /* OIDExternalUserAgentMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentMac.m */; }; 343AAADC1E83493D00F9D36E /* OIDAuthState+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */; }; 343AAADD1E83494400F9D36E /* OIDRedirectHTTPHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 340DAEB91D582AF100EC285B /* OIDRedirectHTTPHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAADE1E83494400F9D36E /* OIDAuthorizationService+Mac.h in Headers */ = {isa = PBXBuildFile; fileRef = 340DAE251D581FE700EC285B /* OIDAuthorizationService+Mac.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 343AAADF1E83494400F9D36E /* OIDExternalUserAgentUICoordinatorMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 340DAE271D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 343AAADF1E83494400F9D36E /* OIDExternalUserAgentMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 340DAE271D581FE700EC285B /* OIDExternalUserAgentMac.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAE01E83494400F9D36E /* OIDAuthState+Mac.h in Headers */ = {isa = PBXBuildFile; fileRef = 340DAE291D581FE700EC285B /* OIDAuthState+Mac.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAAE11E83494A00F9D36E /* OIDLoopbackHTTPServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */; }; 343AAAE21E83494F00F9D36E /* OIDLoopbackHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FEA6AD1DB6E083005C9212 /* OIDLoopbackHTTPServer.m */; }; @@ -334,9 +334,9 @@ 343AAB9B1E834A8800F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB9C1E834A8900F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB9D1E834A8A00F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 345AE747202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m */; }; - 345AE748202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m */; }; - 345AE749202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = 345AE746202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 345AE747202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */; }; + 345AE748202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */; }; + 345AE749202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 347423E41E7F3C4000D3E6D6 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; 347423FF1E7F4BA000D3E6D6 /* OIDAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */; }; 347424001E7F4BA000D3E6D6 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; @@ -377,21 +377,21 @@ A6339DAB20321AE50043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6339DAC20321AE70043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6339DAD20321AEB0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */; }; + A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; }; A6DEAB9F2018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEABA02018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEABA22018E4B60022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEABA32018E4B70022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABA42018E4B90022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABA52018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABA62018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABA72018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m */; }; - A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m */; }; + A6DEABA42018E4B90022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA52018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA62018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA72018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; + A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -475,8 +475,8 @@ 0396974C1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDURLSessionProviderTests.m; sourceTree = ""; }; 340DAE251D581FE700EC285B /* OIDAuthorizationService+Mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OIDAuthorizationService+Mac.h"; sourceTree = ""; }; 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OIDAuthorizationService+Mac.m"; sourceTree = ""; }; - 340DAE271D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentUICoordinatorMac.h; sourceTree = ""; }; - 340DAE281D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDExternalUserAgentUICoordinatorMac.m; sourceTree = ""; }; + 340DAE271D581FE700EC285B /* OIDExternalUserAgentMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentMac.h; sourceTree = ""; }; + 340DAE281D581FE700EC285B /* OIDExternalUserAgentMac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDExternalUserAgentMac.m; sourceTree = ""; }; 340DAE291D581FE700EC285B /* OIDAuthState+Mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OIDAuthState+Mac.h"; sourceTree = ""; }; 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OIDAuthState+Mac.m"; sourceTree = ""; }; 340DAE4E1D58216A00EC285B /* libAppAuth-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -557,8 +557,8 @@ 343AAAAE1E83489A00F9D36E /* AppAuth_tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_tvOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAAC21E8348A900F9D36E /* AppAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_macOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 345AE745202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentUICoordinatorCustomBrowser.m; path = iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.m; sourceTree = ""; }; - 345AE746202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentUICoordinatorCustomBrowser.h; path = iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.h; sourceTree = ""; }; + 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentIOSCustomBrowser.m; path = iOS/OIDExternalUserAgentIOSCustomBrowser.m; sourceTree = ""; }; + 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentIOSCustomBrowser.h; path = iOS/OIDExternalUserAgentIOSCustomBrowser.h; sourceTree = ""; }; 347423F61E7F4B5600D3E6D6 /* libAppAuth-watchOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-watchOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 34D5EC431E6D1AD900814354 /* OIDAppAuthTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OIDAppAuthTests-Bridging-Header.h"; sourceTree = ""; }; 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDSwiftTests.swift; sourceTree = ""; }; @@ -575,11 +575,11 @@ 60140F841DE43C8C00DA0DC3 /* OIDRegistrationResponseTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRegistrationResponseTests.h; sourceTree = ""; }; 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRegistrationResponseTests.m; sourceTree = ""; }; A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDAuthorizationFlowSession.h; sourceTree = ""; }; - A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentUICoordinator.h; sourceTree = ""; }; - A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentFlowSession.h; sourceTree = ""; }; + A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgent.h; sourceTree = ""; }; + A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentSession.h; sourceTree = ""; }; A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentRequest.h; sourceTree = ""; }; - A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentUICoordinatorIOS.m; path = iOS/OIDExternalUserAgentUICoordinatorIOS.m; sourceTree = ""; }; - A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentUICoordinatorIOS.h; path = iOS/OIDExternalUserAgentUICoordinatorIOS.h; sourceTree = ""; }; + A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentIOS.m; path = iOS/OIDExternalUserAgentIOS.m; sourceTree = ""; }; + A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentIOS.h; path = iOS/OIDExternalUserAgentIOS.h; sourceTree = ""; }; F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OIDAuthState+IOS.m"; path = "iOS/OIDAuthState+IOS.m"; sourceTree = ""; }; F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OIDAuthorizationService+IOS.m"; path = "iOS/OIDAuthorizationService+IOS.m"; sourceTree = ""; }; F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OIDAuthorizationService+IOS.h"; path = "iOS/OIDAuthorizationService+IOS.h"; sourceTree = ""; }; @@ -704,8 +704,8 @@ 340DAEBA1D582AF100EC285B /* OIDRedirectHTTPHandler.m */, 340DAE251D581FE700EC285B /* OIDAuthorizationService+Mac.h */, 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */, - 340DAE271D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.h */, - 340DAE281D581FE700EC285B /* OIDExternalUserAgentUICoordinatorMac.m */, + 340DAE271D581FE700EC285B /* OIDExternalUserAgentMac.h */, + 340DAE281D581FE700EC285B /* OIDExternalUserAgentMac.m */, 340DAE291D581FE700EC285B /* OIDAuthState+Mac.h */, 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */, ); @@ -768,9 +768,9 @@ 341741C01C5D8243000EF209 /* OIDError.m */, 341741C11C5D8243000EF209 /* OIDErrorUtilities.h */, 341741C21C5D8243000EF209 /* OIDErrorUtilities.m */, - A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentFlowSession.h */, + A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */, A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */, - A6DEAB982018E4A20022AC32 /* OIDExternalUserAgentUICoordinator.h */, + A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */, 341741C31C5D8243000EF209 /* OIDFieldMapping.h */, 341741C41C5D8243000EF209 /* OIDFieldMapping.m */, 60140F7E1DE4335200DA0DC3 /* OIDRegistrationResponse.h */, @@ -870,12 +870,12 @@ children = ( F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */, F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */, - 345AE746202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.h */, - 345AE745202D526800738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m */, + 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */, + 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */, F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */, F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */, - A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.h */, - A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m */, + A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */, + A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */, ); name = iOS; sourceTree = ""; @@ -900,7 +900,7 @@ 343AAAF31E83499000F9D36E /* OIDScopes.h in Headers */, A6DEAB9F2018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAAE81E83499000F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, - 345AE749202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.h in Headers */, + 345AE749202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */, 343AAA6B1E83465500F9D36E /* AppAuth.h in Headers */, 343AAAF21E83499000F9D36E /* OIDResponseTypes.h in Headers */, 343AAAF71E83499000F9D36E /* OIDTokenRequest.h in Headers */, @@ -910,8 +910,8 @@ 343AAAF11E83499000F9D36E /* OIDGrantTypes.h in Headers */, 343AAA6D1E83466B00F9D36E /* OIDAuthState+IOS.h in Headers */, 343AAAEF1E83499000F9D36E /* OIDRegistrationResponse.h in Headers */, - A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */, - A6DEABA42018E4B90022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */, + A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */, + A6DEABA42018E4B90022AC32 /* OIDExternalUserAgentSession.h in Headers */, 343AAAF51E83499000F9D36E /* OIDServiceConfiguration.h in Headers */, 343AAAE91E83499000F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAAE51E83499000F9D36E /* OIDAuthorizationService.h in Headers */, @@ -922,7 +922,7 @@ 343AAAE31E83499000F9D36E /* OIDAuthorizationRequest.h in Headers */, 343AAAF91E83499000F9D36E /* OIDTokenUtilities.h in Headers */, 343AAAEC1E83499000F9D36E /* OIDError.h in Headers */, - A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.h in Headers */, + A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentIOS.h in Headers */, A6339DAA20321ADD0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -934,7 +934,7 @@ 343AAB041E83499100F9D36E /* OIDError.h in Headers */, 343AAB091E83499100F9D36E /* OIDGrantTypes.h in Headers */, 343AAAFF1E83499100F9D36E /* OIDAuthState.h in Headers */, - A6DEABA52018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */, + A6DEABA52018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */, 343AAB0D1E83499100F9D36E /* OIDServiceConfiguration.h in Headers */, A6DEABA02018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAAFD1E83499100F9D36E /* OIDAuthorizationService.h in Headers */, @@ -943,7 +943,7 @@ 343AAB0E1E83499100F9D36E /* OIDServiceDiscovery.h in Headers */, 343AAB111E83499100F9D36E /* OIDTokenUtilities.h in Headers */, 343AAB0A1E83499100F9D36E /* OIDResponseTypes.h in Headers */, - A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */, + A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */, 343AAB0B1E83499100F9D36E /* OIDScopes.h in Headers */, 343AAB9B1E834A8800F9D36E /* AppAuth.h in Headers */, 343AAB001E83499100F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, @@ -965,7 +965,7 @@ 343AAB1C1E83499200F9D36E /* OIDError.h in Headers */, 343AAB211E83499200F9D36E /* OIDGrantTypes.h in Headers */, 343AAB171E83499200F9D36E /* OIDAuthState.h in Headers */, - A6DEABA62018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */, + A6DEABA62018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */, 343AAB251E83499200F9D36E /* OIDServiceConfiguration.h in Headers */, A6DEABA22018E4B60022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAB151E83499200F9D36E /* OIDAuthorizationService.h in Headers */, @@ -974,7 +974,7 @@ 343AAB261E83499200F9D36E /* OIDServiceDiscovery.h in Headers */, 343AAB291E83499200F9D36E /* OIDTokenUtilities.h in Headers */, 343AAB221E83499200F9D36E /* OIDResponseTypes.h in Headers */, - A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */, + A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */, 343AAB231E83499200F9D36E /* OIDScopes.h in Headers */, 343AAB9C1E834A8900F9D36E /* AppAuth.h in Headers */, 343AAB181E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, @@ -994,8 +994,8 @@ buildActionMask = 2147483647; files = ( 343AAB9D1E834A8A00F9D36E /* AppAuth.h in Headers */, - A6DEABA72018E4BA0022AC32 /* OIDExternalUserAgentFlowSession.h in Headers */, - 343AAADF1E83494400F9D36E /* OIDExternalUserAgentUICoordinatorMac.h in Headers */, + A6DEABA72018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */, + 343AAADF1E83494400F9D36E /* OIDExternalUserAgentMac.h in Headers */, 343AAAE01E83494400F9D36E /* OIDAuthState+Mac.h in Headers */, 343AAADD1E83494400F9D36E /* OIDRedirectHTTPHandler.h in Headers */, 343AAB3C1E83499200F9D36E /* OIDScopeUtilities.h in Headers */, @@ -1017,7 +1017,7 @@ 343AAB2F1E83499200F9D36E /* OIDAuthState.h in Headers */, A6339DAD20321AEB0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, 343AAB3E1E83499200F9D36E /* OIDServiceDiscovery.h in Headers */, - A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgentUICoordinator.h in Headers */, + A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgent.h in Headers */, 343AAADE1E83494400F9D36E /* OIDAuthorizationService+Mac.h in Headers */, 343AAB301E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, 343AAB381E83499200F9D36E /* OIDRegistrationRequest.h in Headers */, @@ -1467,7 +1467,7 @@ 341310C91E6F944B00D5DEE5 /* OIDScopes.m in Sources */, 341310CE1E6F944B00D5DEE5 /* OIDTokenResponse.m in Sources */, 341310C31E6F944B00D5DEE5 /* OIDErrorUtilities.m in Sources */, - 340DAE581D5821A100EC285B /* OIDExternalUserAgentUICoordinatorMac.m in Sources */, + 340DAE581D5821A100EC285B /* OIDExternalUserAgentMac.m in Sources */, 340DAE5A1D5821AB00EC285B /* OIDAuthorizationRequest.m in Sources */, 347423E41E7F3C4000D3E6D6 /* OIDAuthorizationResponse.m in Sources */, 340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */, @@ -1482,7 +1482,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m in Sources */, + A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentIOS.m in Sources */, 341741E01C5D8243000EF209 /* OIDErrorUtilities.m in Sources */, 341741EA1C5D8243000EF209 /* OIDTokenUtilities.m in Sources */, 341741E21C5D8243000EF209 /* OIDGrantTypes.m in Sources */, @@ -1496,7 +1496,7 @@ 60140F7A1DE4276800DA0DC3 /* OIDClientMetadataParameters.m in Sources */, 341741DE1C5D8243000EF209 /* OIDAuthState.m in Sources */, 341741DD1C5D8243000EF209 /* OIDAuthorizationService.m in Sources */, - 345AE747202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m in Sources */, + 345AE747202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */, 341741EB1C5D8243000EF209 /* OIDURLQueryComponent.m in Sources */, 341741E11C5D8243000EF209 /* OIDFieldMapping.m in Sources */, 039697461FA8258D003D1FB2 /* OIDURLSessionProvider.m in Sources */, @@ -1605,7 +1605,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentUICoordinatorIOS.m in Sources */, + A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentIOS.m in Sources */, 343AAA881E83478900F9D36E /* OIDFieldMapping.m in Sources */, 343AAA841E83478900F9D36E /* OIDAuthState.m in Sources */, 343AAA701E83467D00F9D36E /* OIDAuthState+IOS.m in Sources */, @@ -1618,7 +1618,7 @@ 343AAA931E83478900F9D36E /* OIDTokenUtilities.m in Sources */, 343AAA901E83478900F9D36E /* OIDServiceDiscovery.m in Sources */, 343AAA911E83478900F9D36E /* OIDTokenRequest.m in Sources */, - 345AE748202D526900738D22 /* OIDExternalUserAgentUICoordinatorCustomBrowser.m in Sources */, + 345AE748202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */, 343AAA6F1E83467D00F9D36E /* OIDAuthorizationService+IOS.m in Sources */, 343AAA8F1E83478900F9D36E /* OIDServiceConfiguration.m in Sources */, 343AAA891E83478900F9D36E /* OIDRegistrationResponse.m in Sources */, @@ -1754,7 +1754,7 @@ 343AAB4F1E8349AF00F9D36E /* OIDScopes.m in Sources */, 343AAB541E8349AF00F9D36E /* OIDTokenResponse.m in Sources */, 343AAB491E8349AF00F9D36E /* OIDErrorUtilities.m in Sources */, - 343AAADB1E83493D00F9D36E /* OIDExternalUserAgentUICoordinatorMac.m in Sources */, + 343AAADB1E83493D00F9D36E /* OIDExternalUserAgentMac.m in Sources */, 343AAB471E8349AF00F9D36E /* OIDClientMetadataParameters.m in Sources */, 343AAB461E8349AF00F9D36E /* OIDAuthState.m in Sources */, 34AF736D1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */, diff --git a/Source/AppAuth.h b/Source/AppAuth.h index 6abddd3f0..06375affe 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -25,8 +25,8 @@ #import "OIDAuthorizationService.h" #import "OIDError.h" #import "OIDErrorUtilities.h" -#import "OIDExternalUserAgentFlowSession.h" -#import "OIDExternalUserAgentUICoordinator.h" +#import "OIDExternalUserAgent.h" +#import "OIDExternalUserAgentSession.h" #import "OIDGrantTypes.h" #import "OIDRegistrationRequest.h" #import "OIDRegistrationResponse.h" @@ -45,11 +45,12 @@ #elif TARGET_OS_IOS #import "OIDAuthState+IOS.h" #import "OIDAuthorizationService+IOS.h" -#import "OIDExternalUserAgentUICoordinatorCustomBrowser.h" +#import "OIDExternalUserAgentIOS.h" +#import "OIDExternalUserAgentIOSCustomBrowser.h" #elif TARGET_OS_MAC #import "OIDAuthState+Mac.h" #import "OIDAuthorizationService+Mac.h" -#import "OIDExternalUserAgentUICoordinatorMac.h" +#import "OIDExternalUserAgentMac.h" #import "OIDRedirectHTTPHandler.h" #else #error "Platform Undefined" diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index 4891f31bb..94716b73d 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -30,7 +30,7 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import -#import +#import #import #import #import @@ -50,12 +50,12 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #elif TARGET_OS_IOS #import #import -#import -#import +#import +#import #elif TARGET_OS_MAC #import #import -#import +#import #import #else #error "Platform Undefined" diff --git a/Source/OIDAuthState.h b/Source/OIDAuthState.h index c45c5a32b..35e87ed8b 100644 --- a/Source/OIDAuthState.h +++ b/Source/OIDAuthState.h @@ -26,8 +26,8 @@ @protocol OIDAuthorizationFlowSession; @protocol OIDAuthStateChangeDelegate; @protocol OIDAuthStateErrorDelegate; -@protocol OIDExternalUserAgentUICoordinator; -@protocol OIDExternalUserAgentFlowSession; +@protocol OIDExternalUserAgent; +@protocol OIDExternalUserAgentSession; NS_ASSUME_NONNULL_BEGIN @@ -143,15 +143,15 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt /*! @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. @param authorizationRequest The authorization request to present. - @param UICoordinator Generic UI coordinator that can present an external user-agent request. + @param UICoordinator 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 OIDExternalUserAgentFlowSession instance which will terminate when it - receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a - @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. + @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) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest - UICoordinator:(id)UICoordinator + UICoordinator:(id)externalUserAgent callback:(OIDAuthStateAuthorizationCallback)callback; /*! @internal diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index c2832b5d1..1c81a3fee 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -104,14 +104,14 @@ @implementation OIDAuthState #pragma mark - Convenience initializers -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest - UICoordinator:(id)UICoordinator + UICoordinator:(id)externalUserAgent callback:(OIDAuthStateAuthorizationCallback)callback { // presents the authorization request - id authFlowSession = [OIDAuthorizationService + id authFlowSession = [OIDAuthorizationService presentAuthorizationRequest:authorizationRequest - UICoordinator:UICoordinator + UICoordinator:externalUserAgent callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable authorizationError) { // inspects response and processes further if needed (e.g. authorization diff --git a/Source/OIDAuthorizationFlowSession.h b/Source/OIDAuthorizationFlowSession.h index 0201e3520..5493f0e17 100644 --- a/Source/OIDAuthorizationFlowSession.h +++ b/Source/OIDAuthorizationFlowSession.h @@ -32,7 +32,7 @@ */ - (BOOL)resumeAuthorizationFlowWithURL:(NSURL *)URL; -/*! @brief @c OIDAuthorizationUICoordinator or clients should call this method when the +/*! @brief @c OIDExternalUserAgent implementations should call this method when the authorization flow failed with a non-OAuth error. @param error The error that is the reason for the failure of this authorization flow. @remarks Has no effect if called more than once, or after a @c cancel message was received. diff --git a/Source/OIDAuthorizationService.h b/Source/OIDAuthorizationService.h index 6bf8f3418..8583406d1 100644 --- a/Source/OIDAuthorizationService.h +++ b/Source/OIDAuthorizationService.h @@ -27,8 +27,8 @@ @class OIDTokenRequest; @class OIDTokenResponse; @protocol OIDAuthorizationFlowSession; -@protocol OIDExternalUserAgentFlowSession; -@protocol OIDExternalUserAgentUICoordinator; +@protocol OIDExternalUserAgent; +@protocol OIDExternalUserAgentSession; NS_ASSUME_NONNULL_BEGIN @@ -116,13 +116,13 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg @param UICoordinator Generic authorization UI coordinator that can present an authorization request. @param callback The method called when the request has completed or failed. - @return A @c OIDExternalUserAgentFlowSession instance which will terminate when it - receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a - @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. + @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) ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request - UICoordinator:(id)UICoordinator + UICoordinator:(id)externalUserAgent callback:(OIDAuthorizationCallback)callback; /*! @brief Performs a token request. diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 9ee53f6b7..d01b55c5f 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -23,8 +23,8 @@ #import "OIDDefines.h" #import "OIDErrorUtilities.h" #import "OIDAuthorizationFlowSession.h" -#import "OIDExternalUserAgentFlowSession.h" -#import "OIDExternalUserAgentUICoordinator.h" +#import "OIDExternalUserAgent.h" +#import "OIDExternalUserAgentSession.h" #import "OIDRegistrationRequest.h" #import "OIDRegistrationResponse.h" #import "OIDServiceConfiguration.h" @@ -42,10 +42,10 @@ NS_ASSUME_NONNULL_BEGIN -@interface OIDAuthorizationFlowSessionImplementation : NSObject { +@interface OIDAuthorizationFlowSessionImplementation : NSObject { // private variables OIDAuthorizationRequest *_request; - id _UICoordinator; + id _UICoordinator; OIDAuthorizationCallback _pendingauthorizationFlowCallback; } @@ -66,7 +66,7 @@ - (instancetype)initWithRequest:(OIDAuthorizationRequest *)request { return self; } -- (void)presentAuthorizationWithCoordinator:(id)UICoordinator +- (void)presentAuthorizationWithCoordinator:(id)UICoordinator callback:(OIDAuthorizationCallback)authorizationFlowCallback { _UICoordinator = UICoordinator; _pendingauthorizationFlowCallback = authorizationFlowCallback; @@ -81,7 +81,7 @@ - (void)presentAuthorizationWithCoordinator:(id) ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request - UICoordinator:(id)UICoordinator + UICoordinator:(id)externalUserAgent callback:(OIDAuthorizationCallback)callback { OIDAuthorizationFlowSessionImplementation *flowSession = [[OIDAuthorizationFlowSessionImplementation alloc] initWithRequest:request]; - [flowSession presentAuthorizationWithCoordinator:UICoordinator callback:callback]; + [flowSession presentAuthorizationWithCoordinator:externalUserAgent callback:callback]; return flowSession; } diff --git a/Source/OIDExternalUserAgentUICoordinator.h b/Source/OIDExternalUserAgent.h similarity index 66% rename from Source/OIDExternalUserAgentUICoordinator.h rename to Source/OIDExternalUserAgent.h index 1941e72b0..defaedb15 100644 --- a/Source/OIDExternalUserAgentUICoordinator.h +++ b/Source/OIDExternalUserAgent.h @@ -1,4 +1,4 @@ -/*! @file OIDExternalUserAgentUICoordinator.h +/*! @file OIDExternalUserAgent.h @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. All Rights Reserved. @@ -18,35 +18,35 @@ #import -@protocol OIDExternalUserAgentFlowSession; +@protocol OIDExternalUserAgentSession; @protocol OIDExternalUserAgentRequest; NS_ASSUME_NONNULL_BEGIN -/*! @protocol OIDExternalUserAgentUICoordinator +/*! @protocol OIDExternalUserAgent @brief An external user-agent UI coordinator that presents a request. Clients may provide custom implementations of an external UI coordinator to customize the way the requests are presented to the end user. */ -@protocol OIDExternalUserAgentUICoordinator +@protocol OIDExternalUserAgent -/*! @brief Presents the external user-agent request in the UI coordinator. - @param request The request to be presented in the UI. - @param session The @c OIDExternalUserAgentFlowSession instance that initiates presenting the UI. - Concrete implementations of a @c OIDExternalUserAgentUICoordinator may call +/*! @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 )request - session:(id)session; + session:(id)session; -/*! @brief Dimisses the external user-agent UI and calls completion when the dismiss operation ends. - @param animated Wheter or not the dismiss operation should be animated. +/*! @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)dismissExternalUserAgentUIAnimated:(BOOL)animated completion:(void (^)(void))completion; +- (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(void))completion; @end diff --git a/Source/OIDExternalUserAgentRequest.h b/Source/OIDExternalUserAgentRequest.h index 18cff8f45..8ea40cb69 100644 --- a/Source/OIDExternalUserAgentRequest.h +++ b/Source/OIDExternalUserAgentRequest.h @@ -1,4 +1,4 @@ -/*! @file OIDExternalUserAgentUICoordinator.h +/*! @file OIDExternalUserAgent.h @brief AppAuth iOS SDK @copyright Copyright 2017 The AppAuth Authors. All Rights Reserved. @@ -16,9 +16,9 @@ limitations under the License. */ -/*! @protocol OIDExternalUserAgentUICoordinator +/*! @protocol OIDExternalUserAgent @brief An interface that any external user-agent request may implement to use the - @c OIDExternalUserAgentUICoordinator flow. + @c OIDExternalUserAgent flow. */ @protocol OIDExternalUserAgentRequest diff --git a/Source/OIDExternalUserAgentFlowSession.h b/Source/OIDExternalUserAgentSession.h similarity index 88% rename from Source/OIDExternalUserAgentFlowSession.h rename to Source/OIDExternalUserAgentSession.h index 2123bd2ce..747df8395 100644 --- a/Source/OIDExternalUserAgentFlowSession.h +++ b/Source/OIDExternalUserAgentSession.h @@ -1,4 +1,4 @@ -/*! @file OIDExternalUserAgentFlowSession.h +/*! @file OIDExternalUserAgentSession.h @brief AppAuth iOS SDK @copyright Copyright 2017 The AppAuth Authors. All Rights Reserved. @@ -19,11 +19,11 @@ /*! @brief Represents an in-flight external user-agent flow session. */ -@protocol OIDExternalUserAgentFlowSession +@protocol OIDExternalUserAgentSession /*! @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 OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message was received. + @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: @@ -42,7 +42,7 @@ */ - (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL; -/*! @brief @c OIDExternalUserAgentUICoordinator or clients should call this method when the +/*! @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. diff --git a/Source/iOS/OIDAuthState+IOS.h b/Source/iOS/OIDAuthState+IOS.h index a55fab2c7..3ebc9b028 100644 --- a/Source/iOS/OIDAuthState+IOS.h +++ b/Source/iOS/OIDAuthState+IOS.h @@ -32,11 +32,11 @@ NS_ASSUME_NONNULL_BEGIN @param presentingViewController The view controller from which to present the @c SFSafariViewController. @param callback The method called when the request has completed or failed. - @return A @c OIDExternalUserAgentFlowSession instance which will terminate when it - receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a - @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. + @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) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthStateAuthorizationCallback)callback; diff --git a/Source/iOS/OIDAuthState+IOS.m b/Source/iOS/OIDAuthState+IOS.m index 01e60c21e..e825cb042 100644 --- a/Source/iOS/OIDAuthState+IOS.m +++ b/Source/iOS/OIDAuthState+IOS.m @@ -18,18 +18,19 @@ #import "OIDAuthState+IOS.h" -#import "OIDExternalUserAgentUICoordinatorIOS.h" +#import "OIDExternalUserAgentIOS.h" @implementation OIDAuthState (IOS) -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthStateAuthorizationCallback)callback { - OIDExternalUserAgentUICoordinatorIOS *coordinator = [[OIDExternalUserAgentUICoordinatorIOS alloc] - initWithPresentingViewController:presentingViewController]; + OIDExternalUserAgentIOS *externalUserAgent = + [[OIDExternalUserAgentIOS alloc] + initWithPresentingViewController:presentingViewController]; return [self authStateByPresentingAuthorizationRequest:authorizationRequest - UICoordinator:coordinator + UICoordinator:externalUserAgent callback:callback]; } diff --git a/Source/iOS/OIDAuthorizationService+IOS.h b/Source/iOS/OIDAuthorizationService+IOS.h index fc81a4460..162b20311 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.h +++ b/Source/iOS/OIDAuthorizationService+IOS.h @@ -19,7 +19,7 @@ #import #import "OIDAuthorizationService.h" -#import "OIDExternalUserAgentFlowSession.h" +#import "OIDExternalUserAgentSession.h" NS_ASSUME_NONNULL_BEGIN @@ -32,11 +32,11 @@ NS_ASSUME_NONNULL_BEGIN @param presentingViewController The view controller from which to present the \SFSafariViewController. @param callback The method called when the request has completed or failed. - @return A @c OIDExternalUserAgentFlowSession instance which will terminate when it - receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a - @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. + @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) ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthorizationCallback)callback; diff --git a/Source/iOS/OIDAuthorizationService+IOS.m b/Source/iOS/OIDAuthorizationService+IOS.m index 42fd7dfcc..d7a118223 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.m +++ b/Source/iOS/OIDAuthorizationService+IOS.m @@ -18,17 +18,17 @@ #import "OIDAuthorizationService+IOS.h" -#import "OIDExternalUserAgentUICoordinatorIOS.h" +#import "OIDExternalUserAgentIOS.h" NS_ASSUME_NONNULL_BEGIN @implementation OIDAuthorizationService (IOS) -+ (id) ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthorizationCallback)callback { - OIDExternalUserAgentUICoordinatorIOS *coordinator = [[OIDExternalUserAgentUICoordinatorIOS alloc] + OIDExternalUserAgentIOS *coordinator = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController:presentingViewController]; return [self presentAuthorizationRequest:request UICoordinator:coordinator callback:callback]; } diff --git a/Source/iOS/OIDExternalUserAgentUICoordinatorIOS.h b/Source/iOS/OIDExternalUserAgentIOS.h similarity index 92% rename from Source/iOS/OIDExternalUserAgentUICoordinatorIOS.h rename to Source/iOS/OIDExternalUserAgentIOS.h index aeddbddfa..e09f82b3f 100644 --- a/Source/iOS/OIDExternalUserAgentUICoordinatorIOS.h +++ b/Source/iOS/OIDExternalUserAgentIOS.h @@ -1,4 +1,4 @@ -/*! @file OIDExternalUserAgentUICoordinatorIOS.h +/*! @file OIDExternalUserAgentIOS.h @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. All Rights Reserved. @@ -18,7 +18,7 @@ #import -#import "OIDExternalUserAgentUICoordinator.h" +#import "OIDExternalUserAgent.h" @class SFSafariViewController; @@ -39,7 +39,7 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief An iOS specific external user-agent UI Coordinator that uses a \SFSafariViewController to present an user-agent request. */ -@interface OIDExternalUserAgentUICoordinatorIOS : NSObject +@interface OIDExternalUserAgentIOS : NSObject /*! @brief Allows library consumers to change the @c OIDSafariViewControllerFactory used to create new instances of @c SFSafariViewController. diff --git a/Source/iOS/OIDExternalUserAgentUICoordinatorIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m similarity index 92% rename from Source/iOS/OIDExternalUserAgentUICoordinatorIOS.m rename to Source/iOS/OIDExternalUserAgentIOS.m index 12149065a..d87aecf07 100644 --- a/Source/iOS/OIDExternalUserAgentUICoordinatorIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -1,4 +1,4 @@ -/*! @file OIDAuthorizationUICoordinatorIOS.m +/*! @file OIDExternalUserAgentIOS.m @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. All Rights Reserved. @@ -16,12 +16,12 @@ limitations under the License. */ -#import "OIDExternalUserAgentUICoordinatorIOS.h" +#import "OIDExternalUserAgentIOS.h" #import #import "OIDErrorUtilities.h" -#import "OIDExternalUserAgentFlowSession.h" +#import "OIDExternalUserAgentSession.h" #import "OIDExternalUserAgentRequest.h" NS_ASSUME_NONNULL_BEGIN @@ -37,14 +37,14 @@ @interface OIDDefaultSafariViewControllerFactory : NSObject @end -@interface OIDExternalUserAgentUICoordinatorIOS () +@interface OIDExternalUserAgentIOS () @end -@implementation OIDExternalUserAgentUICoordinatorIOS { +@implementation OIDExternalUserAgentIOS { UIViewController *_presentingViewController; BOOL _externalUserAgentFlowInProgress; - __weak id _session; + __weak id _session; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpartial-availability" __weak SFSafariViewController *_safariVC; @@ -77,7 +77,7 @@ - (nullable instancetype)initWithPresentingViewController: } - (BOOL)presentExternalUserAgentRequest:(id)request - session:(id)session { + session:(id)session { if (_externalUserAgentFlowInProgress) { // TODO: Handle errors as authorization is already in progress. return NO; @@ -129,7 +129,7 @@ - (BOOL)presentExternalUserAgentRequest:(id)request return openedSafari; } -- (void)dismissExternalUserAgentUIAnimated:(BOOL)animated completion:(void (^)(void))completion { +- (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(void))completion { if (!_externalUserAgentFlowInProgress) { // Ignore this call if there is no authorization flow in progress. return; @@ -177,7 +177,7 @@ - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller NS_AV // Ignore this call if there is no authorization flow in progress. return; } - id session = _session; + id session = _session; [self cleanUp]; NSError *error = [OIDErrorUtilities errorWithCode:OIDErrorCodeProgramCanceledAuthorizationFlow underlyingError:nil diff --git a/Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.h b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.h similarity index 83% rename from Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.h rename to Source/iOS/OIDExternalUserAgentIOSCustomBrowser.h index 09493daf8..8d1dc9367 100644 --- a/Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.h +++ b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.h @@ -1,4 +1,4 @@ -/*! @file OIDExternalUserAgentUICoordinatorCustomBrowser.h +/*! @file OIDExternalUserAgentIOSCustomBrowser.h @brief AppAuth iOS SDK @copyright Copyright 2018 Google LLC @@ -18,7 +18,7 @@ #import -#import "OIDExternalUserAgentUICoordinator.h" +#import "OIDExternalUserAgent.h" NS_ASSUME_NONNULL_BEGIN @@ -29,13 +29,13 @@ NS_ASSUME_NONNULL_BEGIN */ typedef NSURL *_Nullable (^OIDCustomBrowserURLTransformation)(NSURL *_Nullable requestURL); -/*! @brief An implementation of the OIDExternalUserAgentUICoordinator protocol for iOS that uses +/*! @brief An implementation of the OIDExternalUserAgent protocol for iOS that uses a custom browser (i.e. not Safari) for external requests. It is suitable for browsers that offer a custom url scheme that simply replaces the "https" scheme. It is not designed for browsers that require other modifications to the URL. If the browser is not installed the user will be prompted to install it. */ -@interface OIDExternalUserAgentUICoordinatorCustomBrowser : NSObject +@interface OIDExternalUserAgentIOSCustomBrowser : NSObject /*! @brief URL transformation block for the browser. */ @@ -49,19 +49,19 @@ typedef NSURL *_Nullable (^OIDCustomBrowserURLTransformation)(NSURL *_Nullable r */ @property(nonatomic, readonly, nullable) NSURL *appStoreURL; -/*! @brief An instance of @c OIDExternalUserAgentUICoordinatorCustomBrowser for Chrome. +/*! @brief An instance of @c OIDExternalUserAgentIOSCustomBrowser for Chrome. */ + (instancetype)CustomBrowserChrome; -/*! @brief An instance of @c OIDExternalUserAgentUICoordinatorCustomBrowser for Firefox. +/*! @brief An instance of @c OIDExternalUserAgentIOSCustomBrowser for Firefox. */ + (instancetype)CustomBrowserFirefox; -/*! @brief An instance of @c OIDExternalUserAgentUICoordinatorCustomBrowser for Opera. +/*! @brief An instance of @c OIDExternalUserAgentIOSCustomBrowser for Opera. */ + (instancetype)CustomBrowserOpera; -/*! @brief An instance of @c OIDExternalUserAgentUICoordinatorCustomBrowser for Safari. +/*! @brief An instance of @c OIDExternalUserAgentIOSCustomBrowser for Safari. */ + (instancetype)CustomBrowserSafari; @@ -82,7 +82,7 @@ typedef NSURL *_Nullable (^OIDCustomBrowserURLTransformation)(NSURL *_Nullable r */ - (nonnull instancetype)init NS_UNAVAILABLE; -/*! @brief UICoordinator for a custom browser. @c presentExternalUserAgentRequest:session method +/*! @brief OIDExternalUserAgent for a custom browser. @c presentExternalUserAgentRequest:session method will return NO if the browser isn't installed. */ - (nullable instancetype)initWithURLTransformation:(OIDCustomBrowserURLTransformation)URLTransformation; diff --git a/Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.m b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m similarity index 95% rename from Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.m rename to Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m index 2285305da..af12d67e8 100644 --- a/Source/iOS/OIDExternalUserAgentUICoordinatorCustomBrowser.m +++ b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m @@ -1,4 +1,4 @@ -/*! @file OIDExternalUserAgentUICoordinatorCustomBrowser.m +/*! @file OIDExternalUserAgentIOSCustomBrowser.m @brief AppAuth iOS SDK @copyright Copyright 2018 Google LLC @@ -16,7 +16,7 @@ limitations under the License. */ -#import "OIDExternalUserAgentUICoordinatorCustomBrowser.h" +#import "OIDExternalUserAgentIOSCustomBrowser.h" #import @@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN -@implementation OIDExternalUserAgentUICoordinatorCustomBrowser { +@implementation OIDExternalUserAgentIOSCustomBrowser { OIDCustomBrowserURLTransformation _URLTransformation; NSString *_canOpenURLScheme; NSURL *_appStoreURL; @@ -72,7 +72,7 @@ + (instancetype)CustomBrowserSafari { OIDCustomBrowserURLTransformation transformNOP = ^NSURL *(NSURL *requestURL) { return requestURL; }; - OIDExternalUserAgentUICoordinatorCustomBrowser *coordinator = + OIDExternalUserAgentIOSCustomBrowser *coordinator = [[[self class] alloc] initWithURLTransformation:transformNOP]; return coordinator; } @@ -133,7 +133,7 @@ - (nullable instancetype)initWithURLTransformation: } - (BOOL)presentExternalUserAgentRequest:(nonnull id)request - session:(nonnull id)session { + session:(nonnull id)session { // If the app store URL is set, checks if the app is installed and if not opens the app store. if (_appStoreURL && _canOpenURLScheme) { // Verifies existence of LSApplicationQueriesSchemes Info.plist key. @@ -159,7 +159,7 @@ - (BOOL)presentExternalUserAgentRequest:(nonnull id return openedInBrowser; } -- (void)dismissExternalUserAgentUIAnimated:(BOOL)animated +- (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(nonnull void (^)(void))completion { completion(); } diff --git a/Source/macOS/OIDAuthState+Mac.h b/Source/macOS/OIDAuthState+Mac.h index 4103a75a5..7de9f7324 100644 --- a/Source/macOS/OIDAuthState+Mac.h +++ b/Source/macOS/OIDAuthState+Mac.h @@ -28,11 +28,11 @@ NS_ASSUME_NONNULL_BEGIN and performing the authorization code exchange in the case of code flow requests. @param authorizationRequest The authorization request to present. @param callback The method called when the request has completed or failed. - @return A @c OIDExternalUserAgentFlowSession instance which will terminate when it - receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a - @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. + @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) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest callback:(OIDAuthStateAuthorizationCallback)callback; @end diff --git a/Source/macOS/OIDAuthState+Mac.m b/Source/macOS/OIDAuthState+Mac.m index 6e7562ca9..8dac0ca96 100644 --- a/Source/macOS/OIDAuthState+Mac.m +++ b/Source/macOS/OIDAuthState+Mac.m @@ -18,16 +18,16 @@ #import "OIDAuthState+Mac.h" -#import "OIDExternalUserAgentUICoordinatorMac.h" +#import "OIDExternalUserAgentMac.h" @implementation OIDAuthState (Mac) -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest callback:(OIDAuthStateAuthorizationCallback)callback { - OIDExternalUserAgentUICoordinatorMac *coordinator = [[OIDExternalUserAgentUICoordinatorMac alloc] init]; + OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] init]; return [self authStateByPresentingAuthorizationRequest:authorizationRequest - UICoordinator:coordinator + UICoordinator:externalUserAgent callback:callback]; } diff --git a/Source/macOS/OIDAuthorizationService+Mac.h b/Source/macOS/OIDAuthorizationService+Mac.h index ce4cf9569..9ef0c76ee 100644 --- a/Source/macOS/OIDAuthorizationService+Mac.h +++ b/Source/macOS/OIDAuthorizationService+Mac.h @@ -27,12 +27,13 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief Perform an authorization flow using the default browser. @param request The authorization request. @param callback The method called when the request has completed or failed. - @return A @c OIDExternalUserAgentFlowSession instance which will terminate when it - receives a @c OIDExternalUserAgentFlowSession.cancel message, or after processing a - @c OIDExternalUserAgentFlowSession.resumeExternalUserAgentFlowWithURL: message. + @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)presentAuthorizationRequest:(OIDAuthorizationRequest *)request - callback:(OIDAuthorizationCallback)callback; ++ (id) + presentAuthorizationRequest:(OIDAuthorizationRequest *)request + callback:(OIDAuthorizationCallback)callback; @end NS_ASSUME_NONNULL_END diff --git a/Source/macOS/OIDAuthorizationService+Mac.m b/Source/macOS/OIDAuthorizationService+Mac.m index c7dd2076f..8187ef963 100644 --- a/Source/macOS/OIDAuthorizationService+Mac.m +++ b/Source/macOS/OIDAuthorizationService+Mac.m @@ -18,15 +18,16 @@ #import "OIDAuthorizationService+Mac.h" -#import "OIDExternalUserAgentUICoordinatorMac.h" +#import "OIDExternalUserAgentMac.h" NS_ASSUME_NONNULL_BEGIN @implementation OIDAuthorizationService (Mac) -+ (id)presentAuthorizationRequest:(OIDAuthorizationRequest *)request - callback:(OIDAuthorizationCallback)callback { - OIDExternalUserAgentUICoordinatorMac *coordinator = [[OIDExternalUserAgentUICoordinatorMac alloc] init]; ++ (id) + presentAuthorizationRequest:(OIDAuthorizationRequest *)request + callback:(OIDAuthorizationCallback)callback { + OIDExternalUserAgentMac *coordinator = [[OIDExternalUserAgentMac alloc] init]; return [self presentAuthorizationRequest:request UICoordinator:coordinator callback:callback]; } diff --git a/Source/macOS/OIDExternalUserAgentUICoordinatorMac.h b/Source/macOS/OIDExternalUserAgentMac.h similarity index 77% rename from Source/macOS/OIDExternalUserAgentUICoordinatorMac.h rename to Source/macOS/OIDExternalUserAgentMac.h index 81c747111..eea1bf55e 100644 --- a/Source/macOS/OIDExternalUserAgentUICoordinatorMac.h +++ b/Source/macOS/OIDExternalUserAgentMac.h @@ -1,4 +1,4 @@ -/*! @file OIDExternalUserAgentUICoordinatorMac.h +/*! @file OIDExternalUserAgentMac.h @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. All Rights Reserved. @@ -16,17 +16,17 @@ limitations under the License. */ -#import "OIDExternalUserAgentUICoordinator.h" +#import "OIDExternalUserAgent.h" NS_ASSUME_NONNULL_BEGIN /*! @brief A Mac-specific external user-agent UI Coordinator that uses the default browser to - present a request. + present an external user-agent request. */ -@interface OIDExternalUserAgentUICoordinatorMac : NSObject { +@interface OIDExternalUserAgentMac : NSObject { // private variables BOOL _externalUserAgentFlowInProgress; - __weak id _session; + __weak id _session; } @end diff --git a/Source/macOS/OIDExternalUserAgentUICoordinatorMac.m b/Source/macOS/OIDExternalUserAgentMac.m similarity index 86% rename from Source/macOS/OIDExternalUserAgentUICoordinatorMac.m rename to Source/macOS/OIDExternalUserAgentMac.m index 99cb645ca..37e67c166 100644 --- a/Source/macOS/OIDExternalUserAgentUICoordinatorMac.m +++ b/Source/macOS/OIDExternalUserAgentMac.m @@ -1,4 +1,4 @@ -/*! @file OIDExternalUserAgentUICoordinatorMac.m +/*! @file OIDExternalUserAgentMac.m @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. All Rights Reserved. @@ -16,20 +16,20 @@ limitations under the License. */ -#import "OIDExternalUserAgentUICoordinatorMac.h" +#import "OIDExternalUserAgentMac.h" #import #import "OIDErrorUtilities.h" -#import "OIDExternalUserAgentFlowSession.h" +#import "OIDExternalUserAgentSession.h" #import "OIDExternalUserAgentRequest.h" NS_ASSUME_NONNULL_BEGIN -@implementation OIDExternalUserAgentUICoordinatorMac +@implementation OIDExternalUserAgentMac - (BOOL)presentExternalUserAgentRequest:(id)request - session:(id)session { + session:(id)session { if (_externalUserAgentFlowInProgress) { // TODO: Handle errors as authorization is already in progress. return NO; @@ -50,7 +50,7 @@ - (BOOL)presentExternalUserAgentRequest:(id)request return openedBrowser; } -- (void)dismissExternalUserAgentUIAnimated:(BOOL)animated completion:(void (^)(void))completion { +- (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(void))completion { if (!_externalUserAgentFlowInProgress) { // Ignore this call if there is no authorization flow in progress. return; diff --git a/Source/macOS/OIDRedirectHTTPHandler.h b/Source/macOS/OIDRedirectHTTPHandler.h index 59212fb9c..25fd6b4c8 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.h +++ b/Source/macOS/OIDRedirectHTTPHandler.h @@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN @class HTTPServer; @protocol OIDAuthorizationFlowSession; -@protocol OIDExternalUserAgentFlowSession; +@protocol OIDExternalUserAgentSession; /*! @brief Start a HTTP server on the loopback interface (i.e. @c 127.0.0.1) to receive the OAuth response redirects on macOS. @@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN HTTPServer *_httpServ; NSURL *_successURL; // property variables - NSObject *_currentAuthorizationFlow; + NSObject *_currentAuthorizationFlow; } /*! @brief The external user-agent request flow session which receives the return URL from the @@ -41,23 +41,23 @@ NS_ASSUME_NONNULL_BEGIN redirect handler to continue the flow. This should be set while an external user-agent request flow is in progress. */ -@property(nonatomic, strong, nullable) id currentAuthorizationFlow; /*! @brief Creates an a loopback HTTP redirect URI handler with the given success URL. - @param successURL The URL that the user is redirected to after the request flow completes + @param successURL The URL that the user is redirected to after the external user-agent request flow completes either with a result of success or error. The contents of this page should instruct the user to return to the app. @discussion Once you have initiated the external user-agent request, be sure to set - @c currentExternalUserAgentFlow on this object so that any responses received by this listener will + @c currentAuthorizationFlow on this object so that any responses received by this listener will be routed accordingly. */ - (instancetype)initWithSuccessURL:(nullable NSURL *)successURL; /*! @brief Starts listening on the loopback interface on a random available port, and returns a URL with the base address. Use the returned redirect URI to build a @c OIDExternalUserAgentRequest, - and once you initiate the request, set the resulting @c OIDExternalUserAgentFlowSession to - @c currentExternalUserAgentFlow so the response can be handled. + and once you initiate the request, set the resulting @c OIDExternalUserAgentSession to + @c currentAuthorizationFlow so the response can be handled. @param error The error if an error occurred while starting the local HTTP server. @return The URL containing the address of the server with the randomly assigned available port. @discussion Each instance of @c OIDRedirectHTTPHandler can only listen for a single response. @@ -68,7 +68,7 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief Stops listening the loopback interface and sends an cancellation error (in the domain ::OIDGeneralErrorDomain, with the code ::OIDErrorCodeProgramCanceledAuthorizationFlow) to - the @c currentExternalUserAgentFlow. Has no effect if called when no requests are pending. + the @c currentAuthorizationFlow. Has no effect if called when no requests are pending. @discussion The HTTP listener is stopped automatically on receiving a valid response (regardless of whether the request succeeded or not), this method should not be called except when abandoning the external user-agent request. diff --git a/Source/macOS/OIDRedirectHTTPHandler.m b/Source/macOS/OIDRedirectHTTPHandler.m index 4b284e69b..fa19d416a 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.m +++ b/Source/macOS/OIDRedirectHTTPHandler.m @@ -21,7 +21,7 @@ #import "OIDAuthorizationFlowSession.h" #import "OIDAuthorizationService.h" #import "OIDErrorUtilities.h" -#import "OIDExternalUserAgentFlowSession.h" +#import "OIDExternalUserAgentSession.h" #import "OIDLoopbackHTTPServer.h" /*! @brief Page that is returned following a completed authorization. Show your own page instead by From 43541338d5a28a2305564d37997aef9e647b4dfa Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 7 Mar 2018 18:00:35 -0800 Subject: [PATCH 012/204] Rename "UICoordinator" parameter to "externalUserAgent" Follow up of previous class name refactor. Cleaned up a few other instances of "coordinator". --- Source/OIDAuthState.h | 4 ++-- Source/OIDAuthState.m | 4 ++-- Source/OIDAuthorizationService.h | 4 ++-- Source/OIDAuthorizationService.m | 20 +++++++++---------- Source/OIDExternalUserAgent.h | 6 +++--- Source/iOS/OIDAuthState+IOS.m | 2 +- Source/iOS/OIDAuthorizationService+IOS.m | 4 ++-- Source/iOS/OIDExternalUserAgentIOS.h | 4 ++-- .../OIDExternalUserAgentIOSCustomBrowser.m | 4 ++-- Source/macOS/OIDAuthState+Mac.m | 2 +- Source/macOS/OIDAuthorizationService+Mac.m | 4 ++-- 11 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Source/OIDAuthState.h b/Source/OIDAuthState.h index 35e87ed8b..3a98d378a 100644 --- a/Source/OIDAuthState.h +++ b/Source/OIDAuthState.h @@ -143,7 +143,7 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt /*! @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. @param authorizationRequest The authorization request to present. - @param UICoordinator A external user agent that can present an external user-agent request. + @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 @@ -151,7 +151,7 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt */ + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest - UICoordinator:(id)externalUserAgent + externalUserAgent:(id)externalUserAgent callback:(OIDAuthStateAuthorizationCallback)callback; /*! @internal diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index 1c81a3fee..06a515d44 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -106,12 +106,12 @@ @implementation OIDAuthState + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest - UICoordinator:(id)externalUserAgent + externalUserAgent:(id)externalUserAgent callback:(OIDAuthStateAuthorizationCallback)callback { // presents the authorization request id authFlowSession = [OIDAuthorizationService presentAuthorizationRequest:authorizationRequest - UICoordinator:externalUserAgent + externalUserAgent:externalUserAgent callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable authorizationError) { // inspects response and processes further if needed (e.g. authorization diff --git a/Source/OIDAuthorizationService.h b/Source/OIDAuthorizationService.h index 8583406d1..6ffa5e241 100644 --- a/Source/OIDAuthorizationService.h +++ b/Source/OIDAuthorizationService.h @@ -113,7 +113,7 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg /*! @brief Perform an authorization flow using a generic flow shim. @param request The authorization request. - @param UICoordinator Generic authorization UI coordinator that can present an authorization + @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 @@ -122,7 +122,7 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg */ + (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request - UICoordinator:(id)externalUserAgent + externalUserAgent:(id)externalUserAgent callback:(OIDAuthorizationCallback)callback; /*! @brief Performs a token request. diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index d01b55c5f..473517841 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -45,7 +45,7 @@ @interface OIDAuthorizationFlowSessionImplementation : NSObject { // private variables OIDAuthorizationRequest *_request; - id _UICoordinator; + id _externalUserAgent; OIDAuthorizationCallback _pendingauthorizationFlowCallback; } @@ -66,12 +66,12 @@ - (instancetype)initWithRequest:(OIDAuthorizationRequest *)request { return self; } -- (void)presentAuthorizationWithCoordinator:(id)UICoordinator - callback:(OIDAuthorizationCallback)authorizationFlowCallback { - _UICoordinator = UICoordinator; +- (void)presentAuthorizationWithExternalUserAgent:(id)externalUserAgent + callback:(OIDAuthorizationCallback)authorizationFlowCallback { + _externalUserAgent = externalUserAgent; _pendingauthorizationFlowCallback = authorizationFlowCallback; BOOL authorizationFlowStarted = - [_UICoordinator presentExternalUserAgentRequest:_request session:self]; + [_externalUserAgent presentExternalUserAgentRequest:_request session:self]; if (!authorizationFlowStarted) { NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeSafariOpenError underlyingError:nil @@ -81,7 +81,7 @@ - (void)presentAuthorizationWithCoordinator:(id)UICoordina } - (void)cancel { - [_UICoordinator dismissExternalUserAgentAnimated:YES completion:^{ + [_externalUserAgent dismissExternalUserAgentAnimated:YES completion:^{ NSError *error = [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow underlyingError:nil @@ -146,7 +146,7 @@ - (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL { } } - [_UICoordinator dismissExternalUserAgentAnimated:YES completion:^{ + [_externalUserAgent dismissExternalUserAgentAnimated:YES completion:^{ [self didFinishWithResponse:response error:error]; }]; @@ -165,7 +165,7 @@ - (void)didFinishWithResponse:(nullable OIDAuthorizationResponse *)response error:(nullable NSError *)error { OIDAuthorizationCallback callback = _pendingauthorizationFlowCallback; _pendingauthorizationFlowCallback = nil; - _UICoordinator = nil; + _externalUserAgent = nil; if (callback) { callback(response, error); } @@ -255,11 +255,11 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL + (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request - UICoordinator:(id)externalUserAgent + externalUserAgent:(id)externalUserAgent callback:(OIDAuthorizationCallback)callback { OIDAuthorizationFlowSessionImplementation *flowSession = [[OIDAuthorizationFlowSessionImplementation alloc] initWithRequest:request]; - [flowSession presentAuthorizationWithCoordinator:externalUserAgent callback:callback]; + [flowSession presentAuthorizationWithExternalUserAgent:externalUserAgent callback:callback]; return flowSession; } diff --git a/Source/OIDExternalUserAgent.h b/Source/OIDExternalUserAgent.h index defaedb15..c4eb0a908 100644 --- a/Source/OIDExternalUserAgent.h +++ b/Source/OIDExternalUserAgent.h @@ -24,9 +24,9 @@ NS_ASSUME_NONNULL_BEGIN /*! @protocol OIDExternalUserAgent - @brief An external user-agent UI coordinator that presents a request. Clients may provide custom - implementations of an external UI coordinator to customize the way the requests are - presented to the end user. + @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 diff --git a/Source/iOS/OIDAuthState+IOS.m b/Source/iOS/OIDAuthState+IOS.m index e825cb042..37f614021 100644 --- a/Source/iOS/OIDAuthState+IOS.m +++ b/Source/iOS/OIDAuthState+IOS.m @@ -30,7 +30,7 @@ @implementation OIDAuthState (IOS) [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController:presentingViewController]; return [self authStateByPresentingAuthorizationRequest:authorizationRequest - UICoordinator:externalUserAgent + externalUserAgent:externalUserAgent callback:callback]; } diff --git a/Source/iOS/OIDAuthorizationService+IOS.m b/Source/iOS/OIDAuthorizationService+IOS.m index d7a118223..131f60f92 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.m +++ b/Source/iOS/OIDAuthorizationService+IOS.m @@ -28,9 +28,9 @@ @implementation OIDAuthorizationService (IOS) presentAuthorizationRequest:(OIDAuthorizationRequest *)request presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthorizationCallback)callback { - OIDExternalUserAgentIOS *coordinator = [[OIDExternalUserAgentIOS alloc] + OIDExternalUserAgentIOS *externalUserAgent = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController:presentingViewController]; - return [self presentAuthorizationRequest:request UICoordinator:coordinator callback:callback]; + return [self presentAuthorizationRequest:request externalUserAgent:externalUserAgent callback:callback]; } @end diff --git a/Source/iOS/OIDExternalUserAgentIOS.h b/Source/iOS/OIDExternalUserAgentIOS.h index e09f82b3f..9f5ebdfff 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.h +++ b/Source/iOS/OIDExternalUserAgentIOS.h @@ -36,8 +36,8 @@ NS_ASSUME_NONNULL_BEGIN @end -/*! @brief An iOS specific external user-agent UI Coordinator that uses a \SFSafariViewController to - present an user-agent request. +/*! @brief An iOS specific external user-agent that uses the best possible user-agent available + depending on the version of iOS to present the request. */ @interface OIDExternalUserAgentIOS : NSObject diff --git a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m index af12d67e8..30c4aa7bd 100644 --- a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m +++ b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m @@ -72,9 +72,9 @@ + (instancetype)CustomBrowserSafari { OIDCustomBrowserURLTransformation transformNOP = ^NSURL *(NSURL *requestURL) { return requestURL; }; - OIDExternalUserAgentIOSCustomBrowser *coordinator = + OIDExternalUserAgentIOSCustomBrowser *transform = [[[self class] alloc] initWithURLTransformation:transformNOP]; - return coordinator; + return transform; } + (OIDCustomBrowserURLTransformation) diff --git a/Source/macOS/OIDAuthState+Mac.m b/Source/macOS/OIDAuthState+Mac.m index 8dac0ca96..7b99e734f 100644 --- a/Source/macOS/OIDAuthState+Mac.m +++ b/Source/macOS/OIDAuthState+Mac.m @@ -27,7 +27,7 @@ @implementation OIDAuthState (Mac) callback:(OIDAuthStateAuthorizationCallback)callback { OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] init]; return [self authStateByPresentingAuthorizationRequest:authorizationRequest - UICoordinator:externalUserAgent + externalUserAgent:externalUserAgent callback:callback]; } diff --git a/Source/macOS/OIDAuthorizationService+Mac.m b/Source/macOS/OIDAuthorizationService+Mac.m index 8187ef963..4c7892083 100644 --- a/Source/macOS/OIDAuthorizationService+Mac.m +++ b/Source/macOS/OIDAuthorizationService+Mac.m @@ -27,8 +27,8 @@ @implementation OIDAuthorizationService (Mac) + (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request callback:(OIDAuthorizationCallback)callback { - OIDExternalUserAgentMac *coordinator = [[OIDExternalUserAgentMac alloc] init]; - return [self presentAuthorizationRequest:request UICoordinator:coordinator callback:callback]; + OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] init]; + return [self presentAuthorizationRequest:request externalUserAgent:externalUserAgent callback:callback]; } @end From b420522dae9f74d536ab81dfe9c5aaee7ecb0099 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 8 Mar 2018 12:19:47 -0800 Subject: [PATCH 013/204] Add missing headers to framework includes --- AppAuth.xcodeproj/project.pbxproj | 2 +- Source/AppAuth.h | 3 ++- Source/Framework/AppAuth.h | 5 ++++- Source/OIDAuthorizationFlowSession.h | 1 + Source/OIDExternalUserAgentSession.h | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index ab708c81c..90dedfbce 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -380,7 +380,7 @@ A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; }; + A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEAB9F2018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEABA02018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEABA22018E4B60022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; diff --git a/Source/AppAuth.h b/Source/AppAuth.h index 06375affe..277fc7488 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -17,15 +17,16 @@ */ #import "OIDAuthState.h" -#import "OIDAuthorizationFlowSession.h" #import "OIDAuthStateChangeDelegate.h" #import "OIDAuthStateErrorDelegate.h" +#import "OIDAuthorizationFlowSession.h" #import "OIDAuthorizationRequest.h" #import "OIDAuthorizationResponse.h" #import "OIDAuthorizationService.h" #import "OIDError.h" #import "OIDErrorUtilities.h" #import "OIDExternalUserAgent.h" +#import "OIDExternalUserAgentRequest.h" #import "OIDExternalUserAgentSession.h" #import "OIDGrantTypes.h" #import "OIDRegistrationRequest.h" diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index 94716b73d..49879abc6 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -27,12 +27,15 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import +#import #import #import #import -#import #import #import +#import +#import +#import #import #import #import diff --git a/Source/OIDAuthorizationFlowSession.h b/Source/OIDAuthorizationFlowSession.h index 5493f0e17..50d75d066 100644 --- a/Source/OIDAuthorizationFlowSession.h +++ b/Source/OIDAuthorizationFlowSession.h @@ -17,6 +17,7 @@ */ /*! @brief Represents an in-flight authorization flow session. + @deprecated Please use the more generic OIDExternalUserAgentSession instead. */ @protocol OIDAuthorizationFlowSession diff --git a/Source/OIDExternalUserAgentSession.h b/Source/OIDExternalUserAgentSession.h index 747df8395..f0ee72320 100644 --- a/Source/OIDExternalUserAgentSession.h +++ b/Source/OIDExternalUserAgentSession.h @@ -17,7 +17,7 @@ */ -/*! @brief Represents an in-flight external user-agent flow session. +/*! @brief Represents an in-flight external user-agent session. */ @protocol OIDExternalUserAgentSession From 69025140ca51c95b24961f005979a9b865266e43 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 19 Apr 2018 14:08:28 -0700 Subject: [PATCH 014/204] Correct typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b1c6db50..aac96ca2d 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ Then run `carthage bootstrap`. You can also use AppAuth as a static library. This requires linking the library and your project and including the headers. Suggested configuration: -1. Create an XCode Workspace. +1. Create an Xcode Workspace. 2. Add `AppAuth.xcodeproj` to your Workspace. 3. Include libAppAuth as a linked library for your target (in the "General -> Linked Framework and Libraries" section of your target). From ff9ecca3b49bcef96284ac60c5d036c252b56145 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Thu, 15 Mar 2018 19:28:17 -0400 Subject: [PATCH 015/204] Trigger OIDErrorCodeUserCanceledAuthorizationFlow in safariViewControllerDidFinish: --- Source/iOS/OIDExternalUserAgentIOS.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index d87aecf07..b422290a8 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -109,12 +109,12 @@ - (BOOL)presentExternalUserAgentRequest:(id)request _authenticationVC = authenticationVC; openedSafari = [authenticationVC start]; } else if (@available(iOS 9.0, *)) { - SFSafariViewController *safariVC = - [[[self class] safariViewControllerFactory] safariViewControllerWithURL:requestURL]; - safariVC.delegate = self; - _safariVC = safariVC; - [_presentingViewController presentViewController:safariVC animated:YES completion:nil]; - openedSafari = YES; + SFSafariViewController *safariVC = + [[[self class] safariViewControllerFactory] safariViewControllerWithURL:requestURL]; + safariVC.delegate = self; + _safariVC = safariVC; + [_presentingViewController presentViewController:safariVC animated:YES completion:nil]; + openedSafari = YES; } else { openedSafari = [[UIApplication sharedApplication] openURL:requestURL]; } @@ -179,7 +179,7 @@ - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller NS_AV } id session = _session; [self cleanUp]; - NSError *error = [OIDErrorUtilities errorWithCode:OIDErrorCodeProgramCanceledAuthorizationFlow + NSError *error = [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow underlyingError:nil description:nil]; [session failExternalUserAgentFlowWithError:error]; From c16ebbe27d683d3c9055708ab44387a8c02ae391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20C=C5=93ur?= Date: Mon, 28 May 2018 13:13:06 +0800 Subject: [PATCH 016/204] Support recommended Xcode 9.3 warning: CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF (#230) * Support recommended Xcode 9.3 warning: CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF * using weak self for SFAuthenticationSession completionHandler --- AppAuth.xcodeproj/project.pbxproj | 2 ++ Source/OIDAuthState.m | 14 +++++++------- Source/iOS/OIDExternalUserAgentIOS.m | 12 ++++++++---- .../LoopbackHTTPServer/OIDLoopbackHTTPServer.m | 2 +- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 90dedfbce..f721a6391 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -1894,6 +1894,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -1947,6 +1948,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index 06a515d44..4270a066d 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -478,25 +478,25 @@ - (void)performActionWithFreshTokens:(OIDAuthStateAction)action dispatch_async(dispatch_get_main_queue(), ^() { // update OIDAuthState based on response if (response) { - _needsTokenRefresh = NO; + self->_needsTokenRefresh = NO; [self updateWithTokenResponse:response error:nil]; } else { if (error.domain == OIDOAuthTokenErrorDomain) { - _needsTokenRefresh = NO; + self->_needsTokenRefresh = NO; [self updateWithAuthorizationError:error]; } else { - if ([_errorDelegate respondsToSelector: + if ([self->_errorDelegate respondsToSelector: @selector(authState:didEncounterTransientError:)]) { - [_errorDelegate authState:self didEncounterTransientError:error]; + [self->_errorDelegate authState:self didEncounterTransientError:error]; } } } // nil the pending queue and process everything that was queued up NSArray *actionsToProcess; - @synchronized(_pendingActionsSyncObject) { - actionsToProcess = _pendingActions; - _pendingActions = nil; + @synchronized(self->_pendingActionsSyncObject) { + actionsToProcess = self->_pendingActions; + self->_pendingActions = nil; } for (OIDAuthStateAction actionToProcess in actionsToProcess) { actionToProcess(self.accessToken, self.idToken, error); diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index b422290a8..38af4025b 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -89,21 +89,25 @@ - (BOOL)presentExternalUserAgentRequest:(id)request NSURL *requestURL = [request externalUserAgentRequestURL]; if (@available(iOS 11.0, *)) { + __weak OIDExternalUserAgentIOS *weakSelf = self; NSString *redirectScheme = request.redirectScheme; - SFAuthenticationSession* authenticationVC = + SFAuthenticationSession *authenticationVC = [[SFAuthenticationSession alloc] initWithURL:requestURL callbackURLScheme:redirectScheme completionHandler:^(NSURL * _Nullable callbackURL, NSError * _Nullable error) { - _authenticationVC = nil; + __strong OIDExternalUserAgentIOS *strongSelf = weakSelf; + if (!strongSelf) + return; + strongSelf->_authenticationVC = nil; if (callbackURL) { - [_session resumeExternalUserAgentFlowWithURL:callbackURL]; + [strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL]; } else { NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow underlyingError:error description:nil]; - [_session failExternalUserAgentFlowWithError:safariError]; + [strongSelf->_session failExternalUserAgentFlowWithError:safariError]; } }]; _authenticationVC = authenticationVC; diff --git a/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m b/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m index bf66f0177..69d85e847 100644 --- a/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m +++ b/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m @@ -174,7 +174,7 @@ - (BOOL)processIncomingBytes { // the delegate may choose to stop and dealloc the listener – so we need queue the messages // and process them separately. dispatch_async(dispatch_get_main_queue(), ^() { - [delegate HTTPConnection:self didReceiveRequest:request]; + [self->delegate HTTPConnection:self didReceiveRequest:request]; }); } else { [self performDefaultRequestHandling:request]; From 11e680b2ef9dddfc23a95b27373f5e2bcb999eb5 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 5 Jan 2018 10:34:45 -0800 Subject: [PATCH 017/204] Add nonce to AuthorizationRequest --- Source/OIDAuthState.m | 4 +- Source/OIDAuthorizationRequest.h | 49 +++++++++++++++++++++++- Source/OIDAuthorizationRequest.m | 40 +++++++++++++++++++ Source/OIDAuthorizationService.h | 9 +++++ Source/OIDAuthorizationService.m | 9 +++++ UnitTests/OIDAuthorizationRequestTests.m | 11 ++++++ 6 files changed, 119 insertions(+), 3 deletions(-) diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index 4270a066d..47dca0e02 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -125,7 +125,8 @@ @implementation OIDAuthState OIDTokenRequest *tokenExchangeRequest = [authorizationResponse tokenExchangeRequest]; [OIDAuthorizationService - performTokenRequest:tokenExchangeRequest + performTokenRequest:tokenExchangeRequest + originalAuthorizationResponse:authorizationResponse callback:^(OIDTokenResponse *_Nullable tokenResponse, NSError *_Nullable tokenError) { OIDAuthState *authState; @@ -473,6 +474,7 @@ - (void)performActionWithFreshTokens:(OIDAuthStateAction)action OIDTokenRequest *tokenRefreshRequest = [self tokenRefreshRequestWithAdditionalParameters:additionalParameters]; [OIDAuthorizationService performTokenRequest:tokenRefreshRequest + originalAuthorizationResponse:_lastAuthorizationResponse callback:^(OIDTokenResponse *_Nullable response, NSError *_Nullable error) { dispatch_async(dispatch_get_main_queue(), ^() { diff --git a/Source/OIDAuthorizationRequest.h b/Source/OIDAuthorizationRequest.h index c9611ea15..5c30f2bf6 100644 --- a/Source/OIDAuthorizationRequest.h +++ b/Source/OIDAuthorizationRequest.h @@ -47,6 +47,7 @@ extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256; NSString *_scope; NSURL *_redirectURL; NSString *_state; + NSString *_nonce; NSString *_codeVerifier; NSString *_codeChallenge; NSString *_codeChallengeMethod; @@ -108,6 +109,17 @@ extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256; */ @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 @@ -160,8 +172,8 @@ extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256; responseType:(NSString *)responseType additionalParameters:(nullable NSDictionary *)additionalParameters; -/*! @brief Creates an authorization request with opinionated defaults (a secure @c state, and - PKCE with S256 as the @c code_challenge_method). +/*! @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. @@ -181,6 +193,37 @@ extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256; responseType:(NSString *)responseType additionalParameters:(nullable NSDictionary *)additionalParameters; +/*! @brief Deprecated, replaced with @c OIDAuthState.initWithConfiguration:clientId:clientSecret:scope:redirectURL:responseType:state:nonce:codeVerifier:codeChallenge:codeChallengeMethod:additionalParameters:. + @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 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 + codeVerifier:(nullable NSString *)codeVerifier + codeChallenge:(nullable NSString *)codeChallenge + codeChallengeMethod:(nullable NSString *)codeChallengeMethod + additionalParameters:(nullable NSDictionary *)additionalParameters +__deprecated_msg("Replaced with OIDAuthState.initWithConfiguration:clientId:clientSecret:scope:redirectURL:responseType:state:nonce:codeVerifier:codeChallenge:codeChallengeMethod:additionalParameters:"); + /*! @brief Designated initializer. @param configuration The service's configuration. @param clientID The client identifier. @@ -189,6 +232,7 @@ extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256; @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. @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:. @@ -206,6 +250,7 @@ extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256; 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 diff --git a/Source/OIDAuthorizationRequest.m b/Source/OIDAuthorizationRequest.m index 73052b09d..2d5e15f23 100644 --- a/Source/OIDAuthorizationRequest.m +++ b/Source/OIDAuthorizationRequest.m @@ -55,6 +55,10 @@ */ 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"; @@ -98,6 +102,7 @@ @implementation OIDAuthorizationRequest @synthesize scope = _scope; @synthesize redirectURL = _redirectURL; @synthesize state = _state; +@synthesize nonce = _nonce; @synthesize codeVerifier = _codeVerifier; @synthesize codeChallenge = _codeChallenge; @synthesize codeChallengeMethod = _codeChallengeMethod; @@ -120,6 +125,7 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration 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 @@ -142,6 +148,7 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration return nil; } _state = [state copy]; + _nonce = [nonce copy]; _codeVerifier = [codeVerifier copy]; _codeChallenge = [codeChallenge copy]; _codeChallengeMethod = [codeChallengeMethod copy]; @@ -152,6 +159,32 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration return self; } +// Deprecated +- (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 + codeVerifier:(nullable NSString *)codeVerifier + codeChallenge:(nullable NSString *)codeChallenge + codeChallengeMethod:(nullable NSString *)codeChallengeMethod + additionalParameters:(nullable NSDictionary *)additionalParameters { + return [self initWithConfiguration:configuration + clientId:clientID + clientSecret:clientSecret + scope:scope + redirectURL:redirectURL + responseType:responseType + state:state + nonce:nil + codeVerifier:codeVerifier + codeChallenge:codeChallenge + codeChallengeMethod:OIDOAuthorizationRequestCodeChallengeMethodS256 + additionalParameters:additionalParameters]; +} + - (instancetype) initWithConfiguration:(OIDServiceConfiguration *)configuration clientId:(NSString *)clientID @@ -172,6 +205,7 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration redirectURL:redirectURL responseType:responseType state:[[self class] generateState] + nonce:[[self class] generateState] codeVerifier:codeVerifier codeChallenge:codeChallenge codeChallengeMethod:OIDOAuthorizationRequestCodeChallengeMethodS256 @@ -225,6 +259,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { 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]; @@ -245,6 +280,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { redirectURL:redirectURL responseType:responseType state:state + nonce:nonce codeVerifier:codeVerifier codeChallenge:codeChallenge codeChallengeMethod:codeChallengeMethod @@ -260,6 +296,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { [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]; @@ -318,6 +355,9 @@ - (NSURL *)authorizationRequestURL { if (_state) { [query addParameter:kStateKey value:_state]; } + if (_nonce) { + [query addParameter:kNonceKey value:_nonce]; + } if (_codeChallenge) { [query addParameter:kCodeChallengeKey value:_codeChallenge]; } diff --git a/Source/OIDAuthorizationService.h b/Source/OIDAuthorizationService.h index 6ffa5e241..ef07529fc 100644 --- a/Source/OIDAuthorizationService.h +++ b/Source/OIDAuthorizationService.h @@ -131,6 +131,15 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg */ + (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. diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 473517841..b34b910ca 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -266,6 +266,15 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL #pragma mark - Token Endpoint + (void)performTokenRequest:(OIDTokenRequest *)request callback:(OIDTokenCallback)callback { + return [[self class] performTokenRequest:request + originalAuthorizationResponse:nil + callback:callback]; +} + ++ (void)performTokenRequest:(OIDTokenRequest *)request + originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse + callback:(OIDTokenCallback)callback { + NSURLRequest *URLRequest = [request URLRequest]; NSURLSession *session = [OIDURLSessionProvider session]; [[session dataTaskWithRequest:URLRequest diff --git a/UnitTests/OIDAuthorizationRequestTests.m b/UnitTests/OIDAuthorizationRequestTests.m index 9486440be..972b130ee 100644 --- a/UnitTests/OIDAuthorizationRequestTests.m +++ b/UnitTests/OIDAuthorizationRequestTests.m @@ -63,6 +63,10 @@ */ static NSString *const kTestState = @"State"; +/*! @brief Test value for the @c nonce property. + */ +static NSString *const kTestNonce = @"Nonce"; + /*! @brief Test value for the @c codeVerifier property. */ static NSString *const kTestCodeVerifier = @"code verifier"; @@ -142,6 +146,7 @@ + (OIDAuthorizationRequest *)testInstance { redirectURL:[NSURL URLWithString:kTestRedirectURL] responseType:kTestResponseType state:kTestState + nonce:kTestNonce codeVerifier:kTestCodeVerifier codeChallenge:[[self class] codeChallenge] codeChallengeMethod:[[self class] codeChallengeMethod] @@ -159,6 +164,7 @@ + (OIDAuthorizationRequest *)testInstanceCodeFlow { redirectURL:[NSURL URLWithString:kTestRedirectURL] responseType:OIDResponseTypeCode state:kTestState + nonce:kTestNonce codeVerifier:kTestCodeVerifier codeChallenge:[[self class] codeChallenge] codeChallengeMethod:[[self class] codeChallengeMethod] @@ -176,6 +182,7 @@ + (OIDAuthorizationRequest *)testInstanceCodeFlowClientAuth { redirectURL:[NSURL URLWithString:kTestRedirectURL] responseType:OIDResponseTypeCode state:kTestState + nonce:kTestNonce codeVerifier:kTestCodeVerifier codeChallenge:[[self class] codeChallenge] codeChallengeMethod:[[self class] codeChallengeMethod] @@ -240,6 +247,7 @@ - (void)testCopying { XCTAssertEqualObjects(request.clientSecret, kTestClientSecret); XCTAssertEqualObjects(request.redirectURL, [NSURL URLWithString:kTestRedirectURL]); XCTAssertEqualObjects(request.state, kTestState); + XCTAssertEqualObjects(request.nonce, kTestNonce); XCTAssertEqualObjects(request.codeVerifier, kTestCodeVerifier); XCTAssertEqualObjects(request.codeChallenge, [[self class] codeChallenge]); XCTAssertEqualObjects(request.codeChallengeMethod, [[self class] codeChallengeMethod]); @@ -435,6 +443,7 @@ - (void)testSupportedResponseTypes { redirectURL:[NSURL URLWithString:kTestRedirectURL] responseType:@"code id_token" state:kTestState + nonce:kTestNonce codeVerifier:kTestCodeVerifier codeChallenge:[[self class] codeChallenge] codeChallengeMethod:[[self class] codeChallengeMethod] @@ -449,6 +458,7 @@ - (void)testSupportedResponseTypes { redirectURL:[NSURL URLWithString:kTestRedirectURL] responseType:@"code token id_token" state:kTestState + nonce:kTestNonce codeVerifier:kTestCodeVerifier codeChallenge:[[self class] codeChallenge] codeChallengeMethod:[[self class] codeChallengeMethod] @@ -463,6 +473,7 @@ - (void)testSupportedResponseTypes { redirectURL:[NSURL URLWithString:kTestRedirectURL] responseType:@"code" state:kTestState + nonce:kTestNonce codeVerifier:kTestCodeVerifier codeChallenge:[[self class] codeChallenge] codeChallengeMethod:[[self class] codeChallengeMethod] From 89b5473f31d61b07713549e761b14a12d6ebe7c9 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 5 Jan 2018 12:25:40 -0800 Subject: [PATCH 018/204] Implement ID Token parsing and validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit – New OIDIDToken object to parse ID Tokens. – Token exchange now validates the ID Token pursuant to OpenID Connect Core Section 3.1.3.7. (see code comments for details). --- AppAuth.xcodeproj/project.pbxproj | 28 ++++++ Source/AppAuth.h | 1 + Source/Framework/AppAuth.h | 1 + Source/OIDAuthState.m | 9 +- Source/OIDAuthorizationService.m | 118 ++++++++++++++++++++++ Source/OIDError.h | 7 ++ Source/OIDIDToken.h | 101 +++++++++++++++++++ Source/OIDIDToken.m | 158 ++++++++++++++++++++++++++++++ Source/OIDServiceConfiguration.h | 23 +++++ Source/OIDServiceConfiguration.m | 39 +++++++- Source/OIDTokenResponse.h | 9 +- 11 files changed, 485 insertions(+), 9 deletions(-) create mode 100644 Source/OIDIDToken.h create mode 100644 Source/OIDIDToken.m diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index f721a6391..623f7c6a1 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -365,6 +365,18 @@ 34AF736B1FB4E4B30022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; 34AF736C1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; 34AF736D1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; + 34A663291E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34A6632A1E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34A6632B1E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34A6632C1E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34A6632D1E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 34A6632E1E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 34A6632F1E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 34A663301E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 34A663311E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 34A663321E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 34A663331E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 34A663341E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; 34D5EC451E6D1AD900814354 /* OIDSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */; }; 34FEA6AE1DB6E083005C9212 /* OIDLoopbackHTTPServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */; }; 34FEA6AF1DB6E083005C9212 /* OIDLoopbackHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FEA6AD1DB6E083005C9212 /* OIDLoopbackHTTPServer.m */; }; @@ -560,6 +572,8 @@ 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentIOSCustomBrowser.m; path = iOS/OIDExternalUserAgentIOSCustomBrowser.m; sourceTree = ""; }; 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentIOSCustomBrowser.h; path = iOS/OIDExternalUserAgentIOSCustomBrowser.h; sourceTree = ""; }; 347423F61E7F4B5600D3E6D6 /* libAppAuth-watchOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-watchOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 34A663261E871DD40060B664 /* OIDIDToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDIDToken.h; sourceTree = ""; }; + 34A663271E871DD40060B664 /* OIDIDToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDIDToken.m; sourceTree = ""; }; 34D5EC431E6D1AD900814354 /* OIDAppAuthTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OIDAppAuthTests-Bridging-Header.h"; sourceTree = ""; }; 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDSwiftTests.swift; sourceTree = ""; }; 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDLoopbackHTTPServer.h; sourceTree = ""; }; @@ -779,6 +793,8 @@ 60140F7B1DE42E1000DA0DC3 /* OIDRegistrationRequest.m */, 341741C51C5D8243000EF209 /* OIDGrantTypes.h */, 341741C61C5D8243000EF209 /* OIDGrantTypes.m */, + 34A663261E871DD40060B664 /* OIDIDToken.h */, + 34A663271E871DD40060B664 /* OIDIDToken.m */, 341741C71C5D8243000EF209 /* OIDResponseTypes.h */, 341741C81C5D8243000EF209 /* OIDResponseTypes.m */, 341741C91C5D8243000EF209 /* OIDScopes.h */, @@ -902,6 +918,7 @@ 343AAAE81E83499000F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, 345AE749202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */, 343AAA6B1E83465500F9D36E /* AppAuth.h in Headers */, + 34A663291E871DD40060B664 /* OIDIDToken.h in Headers */, 343AAAF21E83499000F9D36E /* OIDResponseTypes.h in Headers */, 343AAAF71E83499000F9D36E /* OIDTokenRequest.h in Headers */, 343AAAF41E83499000F9D36E /* OIDScopeUtilities.h in Headers */, @@ -948,6 +965,7 @@ 343AAB9B1E834A8800F9D36E /* AppAuth.h in Headers */, 343AAB001E83499100F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, 343AAB081E83499100F9D36E /* OIDRegistrationRequest.h in Headers */, + 34A6632A1E871DD40060B664 /* OIDIDToken.h in Headers */, 343AAB101E83499100F9D36E /* OIDTokenResponse.h in Headers */, 343AAAFC1E83499100F9D36E /* OIDAuthorizationResponse.h in Headers */, 343AAB0C1E83499100F9D36E /* OIDScopeUtilities.h in Headers */, @@ -979,6 +997,7 @@ 343AAB9C1E834A8900F9D36E /* AppAuth.h in Headers */, 343AAB181E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, 343AAB201E83499200F9D36E /* OIDRegistrationRequest.h in Headers */, + 34A6632B1E871DD40060B664 /* OIDIDToken.h in Headers */, 343AAB281E83499200F9D36E /* OIDTokenResponse.h in Headers */, 343AAB141E83499200F9D36E /* OIDAuthorizationResponse.h in Headers */, 343AAB241E83499200F9D36E /* OIDScopeUtilities.h in Headers */, @@ -1018,6 +1037,7 @@ A6339DAD20321AEB0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, 343AAB3E1E83499200F9D36E /* OIDServiceDiscovery.h in Headers */, A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgent.h in Headers */, + 34A6632C1E871DD40060B664 /* OIDIDToken.h in Headers */, 343AAADE1E83494400F9D36E /* OIDAuthorizationService+Mac.h in Headers */, 343AAB301E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, 343AAB381E83499200F9D36E /* OIDRegistrationRequest.h in Headers */, @@ -1470,6 +1490,7 @@ 340DAE581D5821A100EC285B /* OIDExternalUserAgentMac.m in Sources */, 340DAE5A1D5821AB00EC285B /* OIDAuthorizationRequest.m in Sources */, 347423E41E7F3C4000D3E6D6 /* OIDAuthorizationResponse.m in Sources */, + 34A6632E1E871DD40060B664 /* OIDIDToken.m in Sources */, 340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */, 34AF73671FB4E4B00022335F /* OIDURLSessionProvider.m in Sources */, 341310D01E6F944B00D5DEE5 /* OIDURLQueryComponent.m in Sources */, @@ -1484,6 +1505,7 @@ files = ( A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentIOS.m in Sources */, 341741E01C5D8243000EF209 /* OIDErrorUtilities.m in Sources */, + 34A6632D1E871DD40060B664 /* OIDIDToken.m in Sources */, 341741EA1C5D8243000EF209 /* OIDTokenUtilities.m in Sources */, 341741E21C5D8243000EF209 /* OIDGrantTypes.m in Sources */, 60140F7C1DE42E1000DA0DC3 /* OIDRegistrationRequest.m in Sources */, @@ -1585,6 +1607,7 @@ 341310D71E6F944D00D5DEE5 /* OIDRegistrationRequest.m in Sources */, 341310DD1E6F944D00D5DEE5 /* OIDServiceDiscovery.m in Sources */, 341E70991DE18796004353C1 /* OIDAuthorizationResponse.m in Sources */, + 34A6632F1E871DD40060B664 /* OIDIDToken.m in Sources */, 341310DB1E6F944D00D5DEE5 /* OIDScopeUtilities.m in Sources */, 341310D61E6F944D00D5DEE5 /* OIDRegistrationResponse.m in Sources */, 341310D31E6F944D00D5DEE5 /* OIDError.m in Sources */, @@ -1607,6 +1630,7 @@ files = ( A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentIOS.m in Sources */, 343AAA881E83478900F9D36E /* OIDFieldMapping.m in Sources */, + 34A663311E871DD40060B664 /* OIDIDToken.m in Sources */, 343AAA841E83478900F9D36E /* OIDAuthState.m in Sources */, 343AAA701E83467D00F9D36E /* OIDAuthState+IOS.m in Sources */, 343AAA921E83478900F9D36E /* OIDTokenResponse.m in Sources */, @@ -1666,6 +1690,7 @@ 343AAB741E8349B000F9D36E /* OIDRegistrationRequest.m in Sources */, 343AAB7A1E8349B000F9D36E /* OIDServiceDiscovery.m in Sources */, 343AAB6C1E8349B000F9D36E /* OIDAuthorizationResponse.m in Sources */, + 34A663321E871DD40060B664 /* OIDIDToken.m in Sources */, 343AAB781E8349B000F9D36E /* OIDScopeUtilities.m in Sources */, 343AAB731E8349B000F9D36E /* OIDRegistrationResponse.m in Sources */, 343AAB701E8349B000F9D36E /* OIDError.m in Sources */, @@ -1694,6 +1719,7 @@ 343AAB601E8349B000F9D36E /* OIDRegistrationRequest.m in Sources */, 343AAB661E8349B000F9D36E /* OIDServiceDiscovery.m in Sources */, 343AAB581E8349B000F9D36E /* OIDAuthorizationResponse.m in Sources */, + 34A663331E871DD40060B664 /* OIDIDToken.m in Sources */, 343AAB641E8349B000F9D36E /* OIDScopeUtilities.m in Sources */, 343AAB5F1E8349B000F9D36E /* OIDRegistrationResponse.m in Sources */, 343AAB5C1E8349B000F9D36E /* OIDError.m in Sources */, @@ -1756,6 +1782,7 @@ 343AAB491E8349AF00F9D36E /* OIDErrorUtilities.m in Sources */, 343AAADB1E83493D00F9D36E /* OIDExternalUserAgentMac.m in Sources */, 343AAB471E8349AF00F9D36E /* OIDClientMetadataParameters.m in Sources */, + 34A663341E871DD40060B664 /* OIDIDToken.m in Sources */, 343AAB461E8349AF00F9D36E /* OIDAuthState.m in Sources */, 34AF736D1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */, 343AAB561E8349AF00F9D36E /* OIDURLQueryComponent.m in Sources */, @@ -1797,6 +1824,7 @@ 347424081E7F4BA000D3E6D6 /* OIDRegistrationRequest.m in Sources */, 3474240E1E7F4BA000D3E6D6 /* OIDServiceDiscovery.m in Sources */, 347424001E7F4BA000D3E6D6 /* OIDAuthorizationResponse.m in Sources */, + 34A663301E871DD40060B664 /* OIDIDToken.m in Sources */, 3474240C1E7F4BA000D3E6D6 /* OIDScopeUtilities.m in Sources */, 347424071E7F4BA000D3E6D6 /* OIDRegistrationResponse.m in Sources */, 347424041E7F4BA000D3E6D6 /* OIDError.m in Sources */, diff --git a/Source/AppAuth.h b/Source/AppAuth.h index 277fc7488..dd30d9935 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -29,6 +29,7 @@ #import "OIDExternalUserAgentRequest.h" #import "OIDExternalUserAgentSession.h" #import "OIDGrantTypes.h" +#import "OIDIDToken.h" #import "OIDRegistrationRequest.h" #import "OIDRegistrationResponse.h" #import "OIDResponseTypes.h" diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index 49879abc6..37ece0202 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -37,6 +37,7 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import +#import #import #import #import diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index 47dca0e02..1902afb1d 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -124,10 +124,9 @@ @implementation OIDAuthState // code exchange OIDTokenRequest *tokenExchangeRequest = [authorizationResponse tokenExchangeRequest]; - [OIDAuthorizationService - performTokenRequest:tokenExchangeRequest - originalAuthorizationResponse:authorizationResponse - callback:^(OIDTokenResponse *_Nullable tokenResponse, + [OIDAuthorizationService performTokenRequest:tokenExchangeRequest + originalAuthorizationResponse:authorizationResponse + callback:^(OIDTokenResponse *_Nullable tokenResponse, NSError *_Nullable tokenError) { OIDAuthState *authState; if (tokenResponse) { @@ -137,7 +136,7 @@ @implementation OIDAuthState tokenResponse:tokenResponse]; } callback(authState, tokenError); - }]; + }]; } else { // implicit or hybrid flow (hybrid flow assumes code is not for this // client) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index b34b910ca..7c747b803 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -25,6 +25,7 @@ #import "OIDAuthorizationFlowSession.h" #import "OIDExternalUserAgent.h" #import "OIDExternalUserAgentSession.h" +#import "OIDIDToken.h" #import "OIDRegistrationRequest.h" #import "OIDRegistrationResponse.h" #import "OIDServiceConfiguration.h" @@ -362,6 +363,123 @@ + (void)performTokenRequest:(OIDTokenRequest *)request 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 + // Validates that the audience of the ID Token matches the client ID. + NSString *clientID = tokenResponse.request.clientID; + if (![idToken.audience containsObject: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 +/- 5 minutes on the current time. + NSTimeInterval issuedAtDifference = [idToken.issuedAt timeIntervalSinceNow]; + if (fabs(issuedAtDifference) > 300) { + NSError *invalidIDToken = + [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError + underlyingError:nil + description:@"Issued at time is more than 5 minutes before or after " + "the current time"]; + 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); diff --git a/Source/OIDError.h b/Source/OIDError.h index 229802cc0..85b71e267 100644 --- a/Source/OIDError.h +++ b/Source/OIDError.h @@ -144,6 +144,13 @@ typedef NS_ENUM(NSInteger, OIDErrorCode) { */ 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 diff --git a/Source/OIDIDToken.h b/Source/OIDIDToken.h new file mode 100644 index 000000000..d39f3802e --- /dev/null +++ b/Source/OIDIDToken.h @@ -0,0 +1,101 @@ +/*! @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 + +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 { + // property variables + NSDictionary *_header; + NSDictionary *_claims; + NSURL *_issuer; + NSString *_subject; + NSArray *_audience; + NSDate *_expiresAt; + NSDate *_issuedAt; + NSString *_nonce; +} + +/*! @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 diff --git a/Source/OIDIDToken.m b/Source/OIDIDToken.m new file mode 100644 index 000000000..94989d323 --- /dev/null +++ b/Source/OIDIDToken.m @@ -0,0 +1,158 @@ +/*! @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 + +@synthesize header = _header; +@synthesize claims = _claims; +@synthesize issuer = _issuer; +@synthesize subject = _subject; +@synthesize audience = _audience; +@synthesize expiresAt = _expiresAt; +@synthesize issuedAt = _issuedAt; +@synthesize nonce = _nonce; + +- (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 *)fieldMap { + static NSMutableDictionary *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 + + diff --git a/Source/OIDServiceConfiguration.h b/Source/OIDServiceConfiguration.h index 513e5d970..8c33698e4 100644 --- a/Source/OIDServiceConfiguration.h +++ b/Source/OIDServiceConfiguration.h @@ -36,6 +36,7 @@ typedef void (^OIDServiceConfigurationCreated) // property variables NSURL *_authorizationEndpoint; NSURL *_tokenEndpoint; + NSURL *_issuer; NSURL *_registrationEndpoint; OIDServiceDiscovery *_discoveryDocument; } @@ -48,6 +49,10 @@ typedef void (^OIDServiceConfigurationCreated) */ @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; @@ -76,6 +81,24 @@ typedef void (^OIDServiceConfigurationCreated) 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 discoveryDocument The discovery document from which to extract the required OAuth configuration. */ diff --git a/Source/OIDServiceConfiguration.m b/Source/OIDServiceConfiguration.m index d3121c28b..efc792e0b 100644 --- a/Source/OIDServiceConfiguration.m +++ b/Source/OIDServiceConfiguration.m @@ -30,6 +30,10 @@ */ 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"; @@ -44,6 +48,7 @@ @interface OIDServiceConfiguration () - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint + issuer:(nullable NSURL *)issuer registrationEndpoint:(nullable NSURL *)registrationEndpoint discoveryDocument:(nullable OIDServiceDiscovery *)discoveryDocument NS_DESIGNATED_INITIALIZER; @@ -54,18 +59,19 @@ @implementation OIDServiceConfiguration @synthesize authorizationEndpoint = _authorizationEndpoint; @synthesize tokenEndpoint = _tokenEndpoint; +@synthesize issuer = _issuer; @synthesize registrationEndpoint = _registrationEndpoint; @synthesize discoveryDocument = _discoveryDocument; - (instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector( initWithAuthorizationEndpoint: - tokenEndpoint: - registrationEndpoint:) + tokenEndpoint:) ); - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint + issuer:(nullable NSURL *)issuer registrationEndpoint:(nullable NSURL *)registrationEndpoint discoveryDocument:(nullable OIDServiceDiscovery *)discoveryDocument { @@ -73,6 +79,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint if (self) { _authorizationEndpoint = [authorizationEndpoint copy]; _tokenEndpoint = [tokenEndpoint copy]; + _issuer = [issuer copy]; _registrationEndpoint = [registrationEndpoint copy]; _discoveryDocument = [discoveryDocument copy]; } @@ -83,15 +90,38 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint { return [self initWithAuthorizationEndpoint:authorizationEndpoint tokenEndpoint:tokenEndpoint + issuer:nil + registrationEndpoint:nil + discoveryDocument:nil]; +} + +- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint + registrationEndpoint:(nullable NSURL *)registrationEndpoint { + return [self initWithAuthorizationEndpoint:authorizationEndpoint + tokenEndpoint:tokenEndpoint + issuer:nil + registrationEndpoint:registrationEndpoint + discoveryDocument:nil]; +} + +- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint + issuer:(nullable NSURL *)issuer { + return [self initWithAuthorizationEndpoint:authorizationEndpoint + tokenEndpoint:tokenEndpoint + issuer:issuer registrationEndpoint: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 discoveryDocument:nil]; } @@ -99,6 +129,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint - (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *) discoveryDocument { return [self initWithAuthorizationEndpoint:discoveryDocument.authorizationEndpoint tokenEndpoint:discoveryDocument.tokenEndpoint + issuer:discoveryDocument.issuer registrationEndpoint:discoveryDocument.registrationEndpoint discoveryDocument:discoveryDocument]; } @@ -124,6 +155,8 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { 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]; // We don't accept nil authorizationEndpoints or tokenEndpoints. @@ -136,6 +169,7 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { return [self initWithAuthorizationEndpoint:authorizationEndpoint tokenEndpoint:tokenEndpoint + issuer:issuer registrationEndpoint:registrationEndpoint discoveryDocument:discoveryDocument]; } @@ -143,6 +177,7 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { - (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]; } diff --git a/Source/OIDTokenResponse.h b/Source/OIDTokenResponse.h index dc5d9766c..fcad9dde1 100644 --- a/Source/OIDTokenResponse.h +++ b/Source/OIDTokenResponse.h @@ -67,12 +67,17 @@ NS_ASSUME_NONNULL_BEGIN /*! @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. + 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 From e30ea9dc01fbfb9dc479b7b766f00e73e2946a0b Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 5 Jan 2018 12:26:10 -0800 Subject: [PATCH 019/204] Implement OpenID Connect RP Conformance Tests for Code Flow --- AppAuth.xcodeproj/project.pbxproj | 16 + UnitTests/OIDRPProfileCode.h | 40 +++ UnitTests/OIDRPProfileCode.m | 562 ++++++++++++++++++++++++++++++ 3 files changed, 618 insertions(+) create mode 100644 UnitTests/OIDRPProfileCode.h create mode 100644 UnitTests/OIDRPProfileCode.m diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 623f7c6a1..abaefbd6e 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -377,6 +377,12 @@ 34A663321E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; 34A663331E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; 34A663341E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 34A6638B1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 34A6638C1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 34A6638D1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 34A6638E1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 34A6638F1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 34A663901E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; 34D5EC451E6D1AD900814354 /* OIDSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */; }; 34FEA6AE1DB6E083005C9212 /* OIDLoopbackHTTPServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */; }; 34FEA6AF1DB6E083005C9212 /* OIDLoopbackHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FEA6AD1DB6E083005C9212 /* OIDLoopbackHTTPServer.m */; }; @@ -574,6 +580,8 @@ 347423F61E7F4B5600D3E6D6 /* libAppAuth-watchOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-watchOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 34A663261E871DD40060B664 /* OIDIDToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDIDToken.h; sourceTree = ""; }; 34A663271E871DD40060B664 /* OIDIDToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDIDToken.m; sourceTree = ""; }; + 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRPProfileCode.m; sourceTree = ""; }; + 34A663911E886AED0060B664 /* OIDRPProfileCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRPProfileCode.h; sourceTree = ""; }; 34D5EC431E6D1AD900814354 /* OIDAppAuthTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OIDAppAuthTests-Bridging-Header.h"; sourceTree = ""; }; 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDSwiftTests.swift; sourceTree = ""; }; 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDLoopbackHTTPServer.h; sourceTree = ""; }; @@ -847,6 +855,8 @@ 60140F821DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m */, 60140F841DE43C8C00DA0DC3 /* OIDRegistrationResponseTests.h */, 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */, + 34A663911E886AED0060B664 /* OIDRPProfileCode.h */, + 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */, 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */, 34D5EC431E6D1AD900814354 /* OIDAppAuthTests-Bridging-Header.h */, 0396974C1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m */, @@ -1543,6 +1553,7 @@ 3417421E1C5D82D3000EF209 /* OIDServiceDiscoveryTests.m in Sources */, 3417421F1C5D82D3000EF209 /* OIDTokenRequestTests.m in Sources */, 341742181C5D82D3000EF209 /* OIDAuthorizationResponseTests.m in Sources */, + 34A6638B1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 341742171C5D82D3000EF209 /* OIDAuthorizationRequestTests.m in Sources */, 0396974D1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m in Sources */, 3417421A1C5D82D3000EF209 /* OIDGrantTypesTests.m in Sources */, @@ -1568,6 +1579,7 @@ 341AA50D1E7F3A9B00FCA5C6 /* OIDTokenRequestTests.m in Sources */, 341AA5091E7F3A9B00FCA5C6 /* OIDResponseTypesTests.m in Sources */, 341AA4D91E7F393500FCA5C6 /* OIDAuthorizationRequestTests.m in Sources */, + 34A6638C1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 341AA5101E7F3A9B00FCA5C6 /* OIDRegistrationRequestTests.m in Sources */, 341AA5111E7F3A9B00FCA5C6 /* OIDRegistrationResponseTests.m in Sources */, 341AA5081E7F3A9B00FCA5C6 /* OIDGrantTypesTests.m in Sources */, @@ -1588,6 +1600,7 @@ 341AA5001E7F3A9400FCA5C6 /* OIDTokenRequestTests.m in Sources */, 341AA4FC1E7F3A9400FCA5C6 /* OIDResponseTypesTests.m in Sources */, 341AA4F81E7F3A3000FCA5C6 /* OIDAuthorizationRequestTests.m in Sources */, + 34A6638D1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 341AA5031E7F3A9400FCA5C6 /* OIDRegistrationRequestTests.m in Sources */, 341AA5041E7F3A9400FCA5C6 /* OIDRegistrationResponseTests.m in Sources */, 341AA4FB1E7F3A9400FCA5C6 /* OIDGrantTypesTests.m in Sources */, @@ -1674,6 +1687,7 @@ 343AAA7F1E8346B400F9D36E /* OIDRegistrationRequestTests.m in Sources */, 343AAA731E8346B400F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAA761E8346B400F9D36E /* OIDGrantTypesTests.m in Sources */, + 34A6638E1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 343AAA741E8346B400F9D36E /* OIDAuthorizationResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1753,6 +1767,7 @@ 343AAB8B1E8349CE00F9D36E /* OIDRegistrationRequestTests.m in Sources */, 343AAB7F1E8349CE00F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAB821E8349CE00F9D36E /* OIDGrantTypesTests.m in Sources */, + 34A6638F1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 343AAB801E8349CE00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1808,6 +1823,7 @@ 343AAB991E8349CF00F9D36E /* OIDRegistrationRequestTests.m in Sources */, 343AAB8D1E8349CF00F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAB901E8349CF00F9D36E /* OIDGrantTypesTests.m in Sources */, + 34A663901E8865090060B664 /* OIDRPProfileCode.m in Sources */, 343AAB8E1E8349CF00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/UnitTests/OIDRPProfileCode.h b/UnitTests/OIDRPProfileCode.h new file mode 100644 index 000000000..07ef3e8c6 --- /dev/null +++ b/UnitTests/OIDRPProfileCode.h @@ -0,0 +1,40 @@ +/*! @file OIDRPProfileCode.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 + +#import "OIDExternalUserAgent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDAuthorizationUICoordinatorNonInteractive : NSObject { + // private variables + NSURLSession *_urlSession; + __weak id _session; +} +@end + +@interface OIDRPProfileCode : XCTestCase { + // private variables + OIDAuthorizationUICoordinatorNonInteractive *_coordinator; + FILE * _logFile; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/UnitTests/OIDRPProfileCode.m b/UnitTests/OIDRPProfileCode.m new file mode 100644 index 000000000..45db3909a --- /dev/null +++ b/UnitTests/OIDRPProfileCode.m @@ -0,0 +1,562 @@ +/*! @file OIDRPProfileCode.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 "OIDRPProfileCode.h" + +#import "OIDAuthorizationRequest.h" +#import "OIDAuthorizationResponse.h" +#import "OIDAuthorizationService.h" +#import "OIDAuthState.h" +#import "OIDExternalUserAgentSession.h" +#import "OIDIDToken.h" +#import "OIDRegistrationRequest.h" +#import "OIDRegistrationResponse.h" +#import "OIDScopes.h" +#import "OIDServiceConfiguration.h" +#import "OIDServiceDiscovery.h" +#import "OIDTokenRequest.h" +#import "OIDTokenResponse.h" + +static NSString *const kRedirectURI = @"com.example.app:/oauth2redirect/example-provider"; + +// Open ID RP Certification test server http://openid.net/certification/rp_testing/ +static NSString *const kTestURIBase = + @"https://rp.certification.openid.net:8080/appauth-ios-macos/"; + +/*! @brief A UI Coordinator for testing, has no user agent and doesn't support user interaction. + Simply performs the authorization request as a GET request, and looks for a redirect in + the response. + */ +@interface OIDAuthorizationUICoordinatorNonInteractive () +@end + +@implementation OIDAuthorizationUICoordinatorNonInteractive + +- (BOOL)presentExternalUserAgentRequest:(id )request + session:(id)session { + _session = session; + NSURL *requestURL = [request externalUserAgentRequestURL]; + NSMutableURLRequest *URLRequest = [[NSURLRequest requestWithURL:requestURL] mutableCopy]; + NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration]; + _urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; + [[_urlSession dataTaskWithRequest:URLRequest + completionHandler:^(NSData *_Nullable data, + NSURLResponse *_Nullable response, + NSError *_Nullable error) { + NSDictionary* headers = [(NSHTTPURLResponse *)response allHeaderFields]; + NSString *location = [headers objectForKey:@"Location"]; + NSURL *url = [NSURL URLWithString:location]; + [session resumeExternalUserAgentFlowWithURL:url]; + }] resume]; + + return YES; +} + +- (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(void))completion { + if (completion) completion(); +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + willPerformHTTPRedirection:(NSHTTPURLResponse *)response + newRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLRequest *))completionHandler { + // Disables HTTP redirection in the NSURLSession + completionHandler(NULL); +} +@end + +@interface OIDAuthorizationFlowSessionImplementation : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype)initWithRequest:(OIDAuthorizationRequest *)request + NS_DESIGNATED_INITIALIZER; + +@end + +@interface OIDRPProfileCode () + +typedef void (^PostRegistrationCallback)(OIDServiceConfiguration *configuration, + OIDRegistrationResponse *registrationResponse, + NSError *error + ); + +typedef void (^CodeExchangeCompletion)(OIDAuthorizationResponse *_Nullable authorizationResponse, + OIDTokenResponse *_Nullable tokenResponse, + NSError *tokenError + ); + +typedef void (^UserInfoCompletion)(OIDAuthState *_Nullable authState, + NSDictionary *_Nullable userInfoDictionary, + NSError *userInfo + ); + +@end + +@implementation OIDRPProfileCode + +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; + + [self endCertificationTest]; +} + +/*! @brief Performs client registration. + @param issuer The issuer to register the client with. + @param callback Completion block. + */ +- (void)doRegistrationWithIssuer:(NSURL *)issuer callback:(PostRegistrationCallback)callback { + NSURL *redirectURI = [NSURL URLWithString:kRedirectURI]; + + // discovers endpoints + [OIDAuthorizationService discoverServiceConfigurationForIssuer:issuer + completion:^(OIDServiceConfiguration *_Nullable configuration, NSError *_Nullable error) { + + if (!configuration) { + callback(nil, nil, error); + return; + } + + OIDRegistrationRequest *request = + [[OIDRegistrationRequest alloc] initWithConfiguration:configuration + redirectURIs:@[ redirectURI ] + responseTypes:nil + grantTypes:nil + subjectType:nil + tokenEndpointAuthMethod:@"client_secret_basic" + additionalParameters:@{@"id_token_signed_response_alg": + @"none", + @"contacts": + @"appauth@wdenniss.com"}]; + + [self certificationLog:@"Registration request: %@", request]; + + // performs registration request + [OIDAuthorizationService performRegistrationRequest:request + completion:^(OIDRegistrationResponse *_Nullable regResp, NSError *_Nullable error) { + if (regResp) { + callback(configuration, regResp, nil); + } else { + callback(nil, nil, error); + } + }]; + }]; +} + +/*! @brief Performs the code flow on the test server. + @param test The test ID used to configure the test server. + @param completion Completion block. + */ +- (void)codeFlowWithExchangeForTest:(NSString *)test completion:(CodeExchangeCompletion)completion { + [self codeFlowWithExchangeForTest:test scope:@[ OIDScopeOpenID ] completion:completion]; +} + +/*! @brief Performs the code flow on the test server. + @param test The test ID used to configure the test server. + @param scope Scope to use in the authorization request. + @param completion Completion block. + */ +- (void)codeFlowWithExchangeForTest:(NSString *)test + scope:(NSArray *)scope + completion:(CodeExchangeCompletion)completion { + + NSString *issuerString = [kTestURIBase stringByAppendingString:test]; + + XCTestExpectation *expectation = + [self expectationWithDescription:@"Discovery and registration should complete."]; + XCTestExpectation *auth_complete = + [self expectationWithDescription:@"Authorization should complete."]; + XCTestExpectation *token_exchange = + [self expectationWithDescription:@"Token Exchange should complete."]; + + NSURL *issuer = [NSURL URLWithString:issuerString]; + + [self doRegistrationWithIssuer:issuer callback:^(OIDServiceConfiguration *configuration, + OIDRegistrationResponse *registrationResponse, + NSError *error) { + [expectation fulfill]; + XCTAssertNotNil(configuration); + XCTAssertNotNil(registrationResponse); + XCTAssertNil(error); + + if (error) { + return; + } + + NSURL *redirectURI = [NSURL URLWithString:kRedirectURI]; + // builds authentication request + OIDAuthorizationRequest *request = + [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:registrationResponse.clientID + clientSecret:registrationResponse.clientSecret + scopes:scope + redirectURL:redirectURI + responseType:OIDResponseTypeCode + additionalParameters:nil]; + + _coordinator = [[OIDAuthorizationUICoordinatorNonInteractive alloc] init]; + + [self certificationLog:@"Initiating authorization request: %@", + [request authorizationRequestURL]]; + + [OIDAuthorizationService presentAuthorizationRequest:request + externalUserAgent:_coordinator + callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, + NSError *error) { + [auth_complete fulfill]; + XCTAssertNotNil(authorizationResponse); + XCTAssertNil(error); + + OIDTokenRequest *tokenExchangeRequest = [authorizationResponse tokenExchangeRequest]; + [OIDAuthorizationService performTokenRequest:tokenExchangeRequest + originalAuthorizationResponse:authorizationResponse + callback:^(OIDTokenResponse *_Nullable tokenResponse, + NSError *_Nullable tokenError) { + [token_exchange fulfill]; + completion(authorizationResponse, tokenResponse, tokenError); + }]; + }]; + }]; + [self waitForExpectationsWithTimeout:30 handler:nil]; +} + +/*! @brief Performs the code flow on the test server and expects a successful result. + @param test The test ID. + */ +- (void)codeFlowWithExchangeExpectSuccessForTest:(NSString *)test { + [self codeFlowWithExchangeForTest:test + completion:^(OIDAuthorizationResponse * _Nullable authorizationResponse, + OIDTokenResponse * _Nullable tokenResponse, + NSError *tokenError) { + XCTAssertNotNil(tokenResponse); + XCTAssertNil(tokenError); + // testRP_id_token_sig_none + XCTAssertNotNil(tokenResponse.idToken); + + [self certificationLog:@"PASS: Got token response: %@", tokenResponse]; + }]; +} + +- (void)testRP_response_type_code { + NSString *testName = @"rp-response_type-code"; + [self startCertificationTest:testName]; + [self codeFlowWithExchangeExpectSuccessForTest:testName]; +} + +- (void)testRP_id_token_sig_none { + NSString *testName = @"rp-id_token-sig-none"; + [self startCertificationTest:testName]; + [self codeFlowWithExchangeExpectSuccessForTest:testName]; +} + +- (void)testRP_token_endpoint_client_secret_basic { + NSString *testName = @"rp-token_endpoint-client_secret_basic"; + [self startCertificationTest:testName]; + + [self codeFlowWithExchangeExpectSuccessForTest:testName]; +} + +/*! @brief Performs the code flow on the test server and expects a failure result. + @param test The test ID. + */ +- (void)codeFlowWithExchangeExpectFailForTest:(NSString *)test { + [self codeFlowWithExchangeForTest:test + completion:^(OIDAuthorizationResponse * _Nullable authorizationResponse, + OIDTokenResponse * _Nullable tokenResponse, + NSError *tokenError) { + XCTAssertNil(tokenResponse); + XCTAssertNotNil(tokenError); + + if (tokenError) { + [self certificationLog:@"PASS: Token exchange failed with %@", tokenError]; + } + }]; +} + +- (void)testRP_id_token_aud { + NSString *testName = @"rp-id_token-aud"; + [self startCertificationTest:testName]; + [self codeFlowWithExchangeExpectFailForTest:testName]; +} + +- (void)testRP_id_token_iat { + NSString *testName = @"rp-id_token-iat"; + [self startCertificationTest:testName]; + [self codeFlowWithExchangeExpectFailForTest:testName]; +} + +- (void)testRP_id_token_sub { + NSString *testName = @"rp-id_token-sub"; + [self startCertificationTest:testName]; + [self codeFlowWithExchangeExpectFailForTest:testName]; +} + +- (void)testRP_id_token_issuer_mismatch { + NSString *testName = @"rp-id_token-issuer-mismatch"; + [self startCertificationTest:testName]; + [self codeFlowWithExchangeExpectFailForTest:testName]; +} + +- (void)testRP_nonce_invalid { + NSString *testName = @"rp-nonce-invalid"; + [self startCertificationTest:testName]; + [self codeFlowWithExchangeExpectFailForTest:testName]; +} + +/*! @brief Makes a UserInfo request then calls completion block. + @param test The test ID used to configure the test server. + @param completion Completion block. + */ +- (void)codeFlowThenUserInfoForTest:(NSString *)test completion:(UserInfoCompletion)completion { + + // Adds another expectation that codeFlowWithExchangeForTest will wait for. + XCTestExpectation *userinfoExpectation = + [self expectationWithDescription:@"Userinfo response."]; + + NSArray *scope = + @[ OIDScopeOpenID, OIDScopeProfile, OIDScopeEmail, OIDScopeAddress, OIDScopePhone ]; + [self codeFlowWithExchangeForTest:test + scope:scope + completion:^(OIDAuthorizationResponse * _Nullable authorizationResponse, + OIDTokenResponse * _Nullable tokenResponse, + NSError *tokenError) { + XCTAssertNotNil(tokenResponse); + XCTAssertNil(tokenError); + + [self certificationLog:@"Got access token: %@", tokenResponse.accessToken]; + + OIDAuthState *authState = + [[OIDAuthState alloc] initWithAuthorizationResponse:authorizationResponse + tokenResponse:tokenResponse]; + + NSURL *userinfoEndpoint = + authState.lastAuthorizationResponse.request.configuration.discoveryDocument.userinfoEndpoint; + XCTAssertNotNil(userinfoEndpoint); + + [authState performActionWithFreshTokens:^(NSString *_Nonnull accessToken, + NSString *_Nonnull idToken, + NSError *_Nullable error) { + XCTAssertNil(error); + + // creates request to the userinfo endpoint, with access token in the Authorization header + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint]; + NSString *authorizationHeaderValue = [NSString stringWithFormat:@"Bearer %@", accessToken]; + [request addValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"]; + + NSURLSessionConfiguration *configuration = + [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration + delegate:nil + delegateQueue:nil]; + + [self certificationLog:@"Performing UserInfo request to: %@", userinfoEndpoint]; + [self certificationLog:@"- Headers: %@", request.allHTTPHeaderFields]; + + // performs HTTP request + NSURLSessionDataTask *postDataTask = + [session dataTaskWithRequest:request + completionHandler:^(NSData *_Nullable data, + NSURLResponse *_Nullable response, + NSError *_Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^() { + [userinfoExpectation fulfill]; + XCTAssertNil(error); + XCTAssert([response isKindOfClass:[NSHTTPURLResponse class]]); + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + XCTAssert( (int)httpResponse.statusCode == 200); + id jsonDictionaryOrArray = + [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; + completion(authState, jsonDictionaryOrArray, error); + }); + }]; + + [postDataTask resume]; + }]; + }]; +} + +- (void)testRP_userinfo_bearer_header { + NSString *testName = @"rp-userinfo-bearer-header"; + [self startCertificationTest:testName]; + [self codeFlowThenUserInfoForTest:testName + completion:^(OIDAuthState * _Nullable authState, + NSDictionary * _Nullable userInfoDictionary, + NSError *userInfoError) { + XCTAssertNotNil(userInfoDictionary); + [self certificationLog:@"PASS: User info dictionary: %@", userInfoDictionary]; + }]; +} + +- (void)testRP_userinfo_bad_sub_claim { + NSString *testName = @"rp-userinfo-bad-sub-claim"; + [self startCertificationTest:testName]; + + [self codeFlowThenUserInfoForTest:testName + completion:^(OIDAuthState * _Nullable authState, + NSDictionary * _Nullable userInfoDictionary, + NSError *userInfo) { + + NSString *sub = userInfoDictionary[@"sub"]; + XCTAssertNotNil(sub); + OIDIDToken *idToken = + [[OIDIDToken alloc] initWithIDTokenString:authState.lastTokenResponse.idToken]; + XCTAssertNotNil(idToken); + XCTAssertNotEqual(sub, idToken.subject); + + if (![sub isEqualToString:idToken.subject]) { + [self certificationLog:@"PASS: UserInfo subject '%@' does not match id token subject '%@'", + sub, + idToken.subject]; + } + }]; +} + +- (void)testRP_scope_userinfo_claims { + NSString *testName = @"rp-scope-userinfo-claims"; + [self startCertificationTest:testName]; + [self codeFlowThenUserInfoForTest:testName + completion:^(OIDAuthState * _Nullable authState, + NSDictionary * _Nullable userInfoDictionary, + NSError *userInfo) { + + [self certificationLog:@"User info dictionary: %@", userInfoDictionary]; + + XCTAssertNotNil(userInfoDictionary[@"name"]); + XCTAssertNotNil(userInfoDictionary[@"email"]); + XCTAssertNotNil(userInfoDictionary[@"email_verified"]); + XCTAssertNotNil(userInfoDictionary[@"address"]); + XCTAssertNotNil(userInfoDictionary[@"phone_number"]); + if (userInfoDictionary[@"name"] + && userInfoDictionary[@"email"] + && userInfoDictionary[@"email_verified"] + && userInfoDictionary[@"address"] + && userInfoDictionary[@"phone_number"]) { + [self certificationLog:@"PASS: name, email, email_verified, address, phone_number " + "claims present"]; + } + }]; +} + +- (void)testRP_id_token_kid_absent_single_jwks { + NSString *testName = @"rp-id_token-kid-absent-single-jwks"; + [self skippedTest:testName]; +} +- (void)testRP_id_token_kid_absent_multiple_jwks { + NSString *testName = @"rp-id_token-kid-absent-multiple-jwks"; + [self skippedTest:testName]; +} +- (void)testRP_rp_id_token_bad_sig_rs256 { + NSString *testName = @"rp-id_token-bad-sig-rs256"; + [self skippedTest:testName]; +} + +- (void)testRP_id_token_sig_rs256 { + NSString *testName = @"rp-id_token-sig-rs256"; + [self skippedTest:testName]; +} + +- (void)skippedTest:(NSString *)testName { + [self startCertificationTest:testName]; + + NSString *issuerString = [kTestURIBase stringByAppendingString:testName]; + + XCTestExpectation *expectation = + [self expectationWithDescription:@"Discovery and registration should complete."]; + + NSURL *issuer = [NSURL URLWithString:issuerString]; + + [self doRegistrationWithIssuer:issuer callback:^(OIDServiceConfiguration *configuration, + OIDRegistrationResponse *registrationResponse, + NSError *error) { + [expectation fulfill]; + + XCTAssertNil(registrationResponse); + XCTAssertNotNil(error); + + if (error) { + [self certificationLog:@"Registration error: %@", error]; + [self certificationLog:@"SKIP. With id_token_signed_response_alg set to `none` in registration, error recieved and test skipped."]; + } + + }]; + [self waitForExpectationsWithTimeout:30 handler:nil]; +} + + +/*! @brief Creates a log file to record the certification logs. + @param testName The test ID used to configure the test server. + */ +- (void)startCertificationTest:(NSString *)testName { + if (_logFile) { + [self endCertificationTest]; + } + + NSString* filename = [NSString stringWithFormat:@"%@.txt", testName]; + + NSString *documentsDirectory = + NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; + NSString *codeDir = [documentsDirectory stringByAppendingPathComponent:@"code"]; + [[NSFileManager defaultManager] createDirectoryAtPath:codeDir + withIntermediateDirectories:NO + attributes:nil + error:nil]; + NSString *pathForLog = [codeDir stringByAppendingPathComponent:filename]; + + NSLog(@"Writing logs for test %@ to %@", testName, pathForLog); + _logFile = fopen([pathForLog cStringUsingEncoding:NSASCIIStringEncoding], "w"); + NSAssert(_logFile, @"Unable to create log file"); + + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; + NSString *dateString = [dateFormatter stringFromDate:[NSDate date]]; + [self certificationLog:@"# Starting test `%@` at %@ for AppAuth for iOS and macOS", + testName, + dateString]; +} + +/*! @brief Logs string to the certification log. + */ +- (void)certificationLog:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2) { + NSAssert(_logFile, @"No active log"); + + // Gets log message as a string. + va_list argp; + va_start(argp, format); + NSString *log = [[NSString alloc] initWithFormat:format arguments:argp]; + va_end(argp); + + // Logs to file. + fprintf(_logFile, "%s\n", [log UTF8String]); +} + +/*! @brief Closes the certification log file. + */ +- (void)endCertificationTest { + // Adds a newline. + [self certificationLog:@""]; + fclose(_logFile); + _logFile = NULL; +} + +@end + From c4a6277e6288453054752041cd7d0925e02c72ac Mon Sep 17 00:00:00 2001 From: Steven E Wright Date: Thu, 7 Jun 2018 17:05:26 -0700 Subject: [PATCH 020/204] Enable pedantic warnings and 'treat warnings as errors' Fixes several issues that were out of compliance with these warnings. Collaboration between Steven E Wright and William Denniss (see #62, #245). --- AppAuth.xcodeproj/project.pbxproj | 14 ++ Source/OIDAuthState.m | 6 +- Source/OIDAuthorizationFlowSession.h | 3 +- Source/OIDAuthorizationRequest.m | 4 +- Source/OIDAuthorizationResponse.m | 4 +- Source/OIDAuthorizationService.h | 5 + Source/OIDAuthorizationService.m | 20 ++- Source/OIDFieldMapping.m | 2 +- Source/OIDRegistrationRequest.m | 4 +- Source/OIDRegistrationResponse.m | 4 +- Source/OIDServiceConfiguration.m | 2 +- Source/OIDServiceDiscovery.m | 2 +- Source/OIDTokenRequest.m | 4 +- Source/OIDTokenResponse.m | 4 +- Source/OIDURLQueryComponent.m | 2 +- .../OIDLoopbackHTTPServer.h | 2 +- .../OIDLoopbackHTTPServer.m | 21 ++- UnitTests/OIDAuthStateTests.m | 102 ++++++------ UnitTests/OIDAuthorizationRequestTests.m | 147 +++++++++--------- UnitTests/OIDAuthorizationResponseTests.m | 65 ++++---- UnitTests/OIDGrantTypesTests.m | 7 + UnitTests/OIDRPProfileCode.m | 6 +- UnitTests/OIDRegistrationResponseTests.m | 62 ++++---- UnitTests/OIDResponseTypesTests.m | 13 +- UnitTests/OIDScopesTests.m | 7 + UnitTests/OIDServiceConfigurationTests.m | 43 ++--- UnitTests/OIDServiceDiscoveryTests.m | 111 ++++++------- UnitTests/OIDTokenRequestTests.m | 110 +++++++------ UnitTests/OIDTokenResponseTests.m | 55 ++++--- UnitTests/OIDURLQueryComponentTests.m | 44 +++--- UnitTests/OIDURLQueryComponentTestsIOS7.m | 7 + 31 files changed, 507 insertions(+), 375 deletions(-) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index abaefbd6e..c71b5097d 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -1932,7 +1932,9 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; @@ -1940,6 +1942,7 @@ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = NO; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; @@ -1959,8 +1962,11 @@ "DEBUG=1", "$(inherited)", ); + GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_PEDANTIC = YES; + GCC_WARN_STRICT_SELECTOR_MATCH = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; @@ -1970,6 +1976,7 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + WARNING_CFLAGS = "-Wno-gnu"; }; name = Debug; }; @@ -1986,7 +1993,9 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; @@ -1994,6 +2003,7 @@ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = NO; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; @@ -2007,8 +2017,11 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_PEDANTIC = YES; + GCC_WARN_STRICT_SELECTOR_MATCH = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; @@ -2019,6 +2032,7 @@ SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; + WARNING_CFLAGS = "-Wno-gnu"; }; name = Release; }; diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index 1902afb1d..0c9e1c091 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -154,7 +154,7 @@ @implementation OIDAuthState #pragma mark - Initializers - (nonnull instancetype)init - OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithAuthorizationResponse:tokenResponse:)); + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithAuthorizationResponse:tokenResponse:)) /*! @brief Creates an auth state from an authorization response. @param authorizationResponse The authorization response. @@ -216,7 +216,7 @@ - (NSString *)description { "lastAuthorizationResponse: %@, lastTokenResponse: %@, " "lastRegistrationResponse: %@, authorizationError: %@>", NSStringFromClass([self class]), - self, + (void *)self, (self.isAuthorized) ? @"YES" : @"NO", _refreshToken, _scope, @@ -457,7 +457,7 @@ - (void)performActionWithFreshTokens:(OIDAuthStateAction)action } // access token is expired, first refresh the token, then perform action - NSAssert(_pendingActionsSyncObject, @"_pendingActionsSyncObject cannot be nil"); + NSAssert(_pendingActionsSyncObject, @"_pendingActionsSyncObject cannot be nil", @""); @synchronized(_pendingActionsSyncObject) { // if a token is already in the process of being refreshed, adds to pending actions if (_pendingActions) { diff --git a/Source/OIDAuthorizationFlowSession.h b/Source/OIDAuthorizationFlowSession.h index 50d75d066..78903f6c7 100644 --- a/Source/OIDAuthorizationFlowSession.h +++ b/Source/OIDAuthorizationFlowSession.h @@ -17,8 +17,9 @@ */ /*! @brief Represents an in-flight authorization flow session. - @deprecated Please use the more generic OIDExternalUserAgentSession instead. + @deprecated Use the more generic OIDExternalUserAgentSession instead. */ +__attribute__((deprecated("Use the more generic OIDExternalUserAgentSession instead."))) @protocol OIDAuthorizationFlowSession /*! @brief Clients should call this method with the result of the authorization code flow if it diff --git a/Source/OIDAuthorizationRequest.m b/Source/OIDAuthorizationRequest.m index 2d5e15f23..7f6c8caf6 100644 --- a/Source/OIDAuthorizationRequest.m +++ b/Source/OIDAuthorizationRequest.m @@ -116,7 +116,7 @@ - (instancetype)init redirectURL: responseType: additionalParameters:) - ); + ) - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration clientId:(NSString *)clientID @@ -308,7 +308,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p, request: %@>", NSStringFromClass([self class]), - self, + (void *)self, self.authorizationRequestURL]; } diff --git a/Source/OIDAuthorizationResponse.m b/Source/OIDAuthorizationResponse.m index 4376f2cc4..fb66f4986 100644 --- a/Source/OIDAuthorizationResponse.m +++ b/Source/OIDAuthorizationResponse.m @@ -120,7 +120,7 @@ @implementation OIDAuthorizationResponse #pragma mark - Initializers - (instancetype)init - OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:)); + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:)) - (instancetype)initWithRequest:(OIDAuthorizationRequest *)request parameters:(NSDictionary *> *)parameters { @@ -178,7 +178,7 @@ - (NSString *)description { "idToken: \"%@\", scope: \"%@\", additionalParameters: %@, " "request: %@>", NSStringFromClass([self class]), - self, + (void *)self, _authorizationCode, _state, _accessToken, diff --git a/Source/OIDAuthorizationService.h b/Source/OIDAuthorizationService.h index ef07529fc..c61bbc880 100644 --- a/Source/OIDAuthorizationService.h +++ b/Source/OIDAuthorizationService.h @@ -111,6 +111,9 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL completion:(OIDDiscoveryCallback)completion; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + /*! @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 @@ -125,6 +128,8 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg externalUserAgent:(id)externalUserAgent callback:(OIDAuthorizationCallback)callback; +#pragma GCC diagnostic pop + /*! @brief Performs a token request. @param request The token request. @param callback The method called when the request has completed or failed. diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 7c747b803..1cb65c0b4 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -43,6 +43,9 @@ NS_ASSUME_NONNULL_BEGIN +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + @interface OIDAuthorizationFlowSessionImplementation : NSObject { // private variables OIDAuthorizationRequest *_request; @@ -50,6 +53,8 @@ @interface OIDAuthorizationFlowSessionImplementation : NSObject) presentAuthorizationRequest:(OIDAuthorizationRequest *)request externalUserAgent:(id)externalUserAgent @@ -264,12 +272,14 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL return flowSession; } +#pragma GCC diagnostic pop + #pragma mark - Token Endpoint + (void)performTokenRequest:(OIDTokenRequest *)request callback:(OIDTokenCallback)callback { - return [[self class] performTokenRequest:request - originalAuthorizationResponse:nil - callback:callback]; + [[self class] performTokenRequest:request + originalAuthorizationResponse:nil + callback:callback]; } + (void)performTokenRequest:(OIDTokenRequest *)request diff --git a/Source/OIDFieldMapping.m b/Source/OIDFieldMapping.m index bb1c81824..eaad82c78 100644 --- a/Source/OIDFieldMapping.m +++ b/Source/OIDFieldMapping.m @@ -27,7 +27,7 @@ @implementation OIDFieldMapping @synthesize conversion = _conversion; - (nonnull instancetype)init - OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithName:type:conversion:)); + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithName:type:conversion:)) - (instancetype)initWithName:(NSString *)name type:(Class)type { diff --git a/Source/OIDRegistrationRequest.m b/Source/OIDRegistrationRequest.m index e6018af37..bf3899bca 100644 --- a/Source/OIDRegistrationRequest.m +++ b/Source/OIDRegistrationRequest.m @@ -69,7 +69,7 @@ - (instancetype)init subjectType: tokenEndpointAuthMethod: additionalParameters:) - ); + ) - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration redirectURIs:(NSArray *)redirectURIs @@ -159,7 +159,7 @@ - (NSString *)description { encoding:NSUTF8StringEncoding]; return [NSString stringWithFormat:@"<%@: %p, request: >", NSStringFromClass([self class]), - self, + (void *)self, request.URL, requestBody]; } diff --git a/Source/OIDRegistrationResponse.m b/Source/OIDRegistrationResponse.m index 251a99188..71dc91005 100644 --- a/Source/OIDRegistrationResponse.m +++ b/Source/OIDRegistrationResponse.m @@ -89,7 +89,7 @@ @implementation OIDRegistrationResponse #pragma mark - Initializers - (nonnull instancetype)init - OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:)); + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:)) - (instancetype)initWithRequest:(OIDRegistrationRequest *)request parameters:(NSDictionary *> *)parameters { @@ -159,7 +159,7 @@ - (NSString *)description { "registrationClientURI: \"%@\", " "additionalParameters: %@, request: %@>", NSStringFromClass([self class]), - self, + (void *)self, _clientID, _clientIDIssuedAt, _clientSecret, diff --git a/Source/OIDServiceConfiguration.m b/Source/OIDServiceConfiguration.m index efc792e0b..d858dc9f8 100644 --- a/Source/OIDServiceConfiguration.m +++ b/Source/OIDServiceConfiguration.m @@ -67,7 +67,7 @@ - (instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector( initWithAuthorizationEndpoint: tokenEndpoint:) - ); + ) - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint diff --git a/Source/OIDServiceDiscovery.m b/Source/OIDServiceDiscovery.m index 565010577..da0fe7711 100644 --- a/Source/OIDServiceDiscovery.m +++ b/Source/OIDServiceDiscovery.m @@ -73,7 +73,7 @@ @implementation OIDServiceDiscovery -- (nonnull instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithDictionary:error:)); +- (nonnull instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithDictionary:error:)) - (nullable instancetype)initWithJSON:(NSString *)serviceDiscoveryJSON error:(NSError **)error { NSData *jsonData = [serviceDiscoveryJSON dataUsingEncoding:NSUTF8StringEncoding]; diff --git a/Source/OIDTokenRequest.m b/Source/OIDTokenRequest.m index d76d87a76..eeb210ba8 100644 --- a/Source/OIDTokenRequest.m +++ b/Source/OIDTokenRequest.m @@ -90,7 +90,7 @@ - (instancetype)init refreshToken: codeVerifier: additionalParameters:) - ); + ) - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration grantType:(NSString *)grantType @@ -210,7 +210,7 @@ - (NSString *)description { [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding]; return [NSString stringWithFormat:@"<%@: %p, request: >", NSStringFromClass([self class]), - self, + (void *)self, request.URL, requestBody]; } diff --git a/Source/OIDTokenResponse.m b/Source/OIDTokenResponse.m index 537d3878d..6db60bb73 100644 --- a/Source/OIDTokenResponse.m +++ b/Source/OIDTokenResponse.m @@ -98,7 +98,7 @@ @implementation OIDTokenResponse #pragma mark - Initializers - (instancetype)init - OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:)); + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:)) - (instancetype)initWithRequest:(OIDTokenRequest *)request parameters:(NSDictionary *> *)parameters { @@ -155,7 +155,7 @@ - (NSString *)description { "tokenType: %@, idToken: \"%@\", refreshToken: \"%@\", " "scope: \"%@\", additionalParameters: %@, request: %@>", NSStringFromClass([self class]), - self, + (void *)self, _accessToken, _accessTokenExpirationDate, _tokenType, diff --git a/Source/OIDURLQueryComponent.m b/Source/OIDURLQueryComponent.m index 0a94ec27a..97fb1e295 100644 --- a/Source/OIDURLQueryComponent.m +++ b/Source/OIDURLQueryComponent.m @@ -198,7 +198,7 @@ - (NSURL *)URLByReplacingQueryInURL:(NSURL *)URL { - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p, parameters: %@>", NSStringFromClass([self class]), - self, + (void *)self, _parameters]; } diff --git a/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h b/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h index f60803c11..b78bfa51d 100644 --- a/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h +++ b/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h @@ -112,7 +112,7 @@ typedef enum { __weak id delegate; NSData *peerAddress; __weak HTTPServer *server; - NSMutableArray *requests; + NSMutableArray *requests; NSInputStream *istream; NSOutputStream *ostream; NSMutableData *ibuffer; diff --git a/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m b/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m index 69d85e847..5c9d23bef 100644 --- a/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m +++ b/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m @@ -21,6 +21,10 @@ #include #include +// We'll ignore the pointer arithmetic warnings for now. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-arith" + @implementation HTTPServer - (id)init { @@ -99,14 +103,12 @@ - (HTTPServer *)server { } - (HTTPServerRequest *)nextRequest { - NSUInteger idx, cnt = requests ? [requests count] : 0; - for (idx = 0; idx < cnt; idx++) { - id obj = [requests objectAtIndex:idx]; - if ([obj response] == nil) { - return obj; - } + for (HTTPServerRequest *request in requests) { + if (![request response]) { + return request; } - return nil; + } + return nil; } - (BOOL)isValid { @@ -173,8 +175,9 @@ - (BOOL)processIncomingBytes { // directly as this method is called in a loop in order to process multiple messages, and // the delegate may choose to stop and dealloc the listener – so we need queue the messages // and process them separately. + id myDelegate = delegate; dispatch_async(dispatch_get_main_queue(), ^() { - [self->delegate HTTPConnection:self didReceiveRequest:request]; + [myDelegate HTTPConnection:self didReceiveRequest:request]; }); } else { [self performDefaultRequestHandling:request]; @@ -605,3 +608,5 @@ - (BOOL)hasIPv6Socket { } @end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDAuthStateTests.m b/UnitTests/OIDAuthStateTests.m index 9b4c66a82..e159cf0b3 100644 --- a/UnitTests/OIDAuthStateTests.m +++ b/UnitTests/OIDAuthStateTests.m @@ -28,6 +28,11 @@ #import "Source/OIDTokenResponse.h" #import "OIDTokenRequestTests.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + @interface OIDAuthState (Testing) // expose private method for simple testing - (BOOL)isTokenFresh; @@ -83,7 +88,7 @@ + (NSError *)OAuthTokenInvalidClientError { - (void)didChangeState:(OIDAuthState *)state { // in this test, this method should only be called when we expect it - XCTAssertNotNil(_didChangeStateExpectation); + XCTAssertNotNil(_didChangeStateExpectation, @""); [_didChangeStateExpectation fulfill]; } @@ -92,7 +97,7 @@ - (void)didChangeState:(OIDAuthState *)state { - (void)authState:(OIDAuthState *)state didEncounterAuthorizationError:(NSError *)error { // in this test, this method should only be called when we expect it - XCTAssertNotNil(_didEncounterAuthorizationErrorExpectation); + XCTAssertNotNil(_didEncounterAuthorizationErrorExpectation, @""); [_didEncounterAuthorizationErrorExpectation fulfill]; } @@ -113,16 +118,16 @@ - (void)testErrorState { OIDAuthState *authstate = [[self class] testInstance]; // starting state should be authorized - XCTAssert([authstate isAuthorized]); - XCTAssertFalse([authstate authorizationError]); + XCTAssert([authstate isAuthorized], @""); + XCTAssertFalse([authstate authorizationError], @""); NSError *oauthError = [[self class] OAuthTokenInvalidGrantErrorWithUnderlyingError:nil]; [authstate updateWithAuthorizationError:oauthError]; // after updating with an error, should no longer be authorized - XCTAssertFalse([authstate isAuthorized]); - XCTAssert([authstate authorizationError]); + XCTAssertFalse([authstate isAuthorized], @""); + XCTAssert([authstate authorizationError], @""); } /*! @brief Tests that the didChangeState delegate is called. @@ -179,7 +184,7 @@ - (void)testNonCompliantNSCodingNSErrors { NSError *oauthError = [[self class] OAuthTokenInvalidGrantErrorWithUnderlyingError:nonCompliantError]; [authstate updateWithAuthorizationError:oauthError]; - XCTAssertNoThrow([NSKeyedArchiver archivedDataWithRootObject:authstate]); + XCTAssertNoThrow([NSKeyedArchiver archivedDataWithRootObject:authstate], @""); } /*! @brief Tests @c OIDAuthState.updateWithAuthorizationResponse:error: with a success response. @@ -189,8 +194,8 @@ - (void)testUpdateWithAuthorizationResponseSuccess { OIDAuthorizationResponse *authorizationResponse = [OIDAuthorizationResponseTests testInstanceCodeFlow]; [authState updateWithAuthorizationResponse:authorizationResponse error:nil]; - XCTAssertEqual(authState.lastAuthorizationResponse, authorizationResponse); - XCTAssertNil(authState.authorizationError); + XCTAssertEqual(authState.lastAuthorizationResponse, authorizationResponse, @""); + XCTAssertNil(authState.authorizationError, @""); } /*! @brief Tests @c OIDAuthState.updateWithAuthorizationResponse:error: with an authorization @@ -200,7 +205,7 @@ - (void)testUpdateWithAuthorizationResponseOAuthError { OIDAuthState *authState = [[self class] testInstance]; NSError *oauthError = [[self class] OAuthAuthorizationError]; [authState updateWithAuthorizationResponse:nil error:oauthError]; - XCTAssertNotNil(authState.authorizationError); + XCTAssertNotNil(authState.authorizationError, @""); } /*! @brief Tests @c OIDAuthState.updateWithAuthorizationResponse:error: with a transient @@ -210,7 +215,7 @@ - (void)testUpdateWithAuthorizationResponseTransientError { OIDAuthState *authState = [[self class] testInstance]; NSError *transientError = [[NSError alloc] init]; [authState updateWithAuthorizationResponse:nil error:transientError]; - XCTAssertNil(authState.authorizationError); + XCTAssertNil(authState.authorizationError, @""); } /*! @brief Tests @c OIDAuthState.updateWithAuthorizationResponse:error: with both a success @@ -222,7 +227,7 @@ - (void)testUpdateWithAuthorizationResponseBothSuccessAndError { [OIDAuthorizationResponseTests testInstanceCodeFlow]; NSError *oauthError = [[self class] OAuthAuthorizationError]; [authState updateWithAuthorizationResponse:authorizationResponse error:oauthError]; - XCTAssertNotNil(authState.authorizationError); + XCTAssertNotNil(authState.authorizationError, @""); } /*! @brief Tests @c OIDAuthState.updateWithRegistrationResponse: with a success response. @@ -245,10 +250,10 @@ - (void)testUpdateWithTokenResponseSuccess { OIDAuthState *authState = [[self class] testInstance]; OIDTokenResponse *tokenResponse = [OIDTokenResponseTests testInstanceRefresh]; [authState updateWithTokenResponse:tokenResponse error:nil]; - XCTAssertEqual(authState.lastTokenResponse, tokenResponse); - XCTAssertNotNil(authState.refreshToken); - XCTAssertTrue(authState.isAuthorized); - XCTAssertNil(authState.authorizationError); + XCTAssertEqual(authState.lastTokenResponse, tokenResponse, @""); + XCTAssertNotNil(authState.refreshToken, @""); + XCTAssertTrue(authState.isAuthorized, @""); + XCTAssertNil(authState.authorizationError, @""); } /*! @brief Tests @c OIDAuthState.updateWithTokenResponse:error: with an authorization error. @@ -257,8 +262,8 @@ - (void)testUpdateWithTokenResponseOAuthError { OIDAuthState *authState = [[self class] testInstance]; NSError *oauthError = [[self class] OAuthTokenInvalidGrantErrorWithUnderlyingError:nil]; [authState updateWithTokenResponse:nil error:oauthError]; - XCTAssertFalse(authState.isAuthorized); - XCTAssertNotNil(authState.authorizationError); + XCTAssertFalse(authState.isAuthorized, @""); + XCTAssertNotNil(authState.authorizationError, @""); } /*! @brief Tests @c OIDAuthState.updateWithTokenResponse:error: with a transient (non-OAuth) error. @@ -267,10 +272,10 @@ - (void)testUpdateWithTokenResponseTransientError { OIDAuthState *authState = [[self class] testInstance]; NSError *transientError = [[NSError alloc] init]; [authState updateWithTokenResponse:nil error:transientError]; - XCTAssertNotNil(authState.lastTokenResponse); - XCTAssertNotNil(authState.refreshToken); - XCTAssertTrue(authState.isAuthorized); - XCTAssertNil(authState.authorizationError); + XCTAssertNotNil(authState.lastTokenResponse, @""); + XCTAssertNotNil(authState.refreshToken, @""); + XCTAssertTrue(authState.isAuthorized, @""); + XCTAssertNil(authState.authorizationError, @""); } /*! @brief Tests @c OIDAuthState.updateWithTokenResponse:error: with both a success response @@ -281,8 +286,8 @@ - (void)testUpdateWithTokenResponseBothSuccessAndError { OIDTokenResponse *tokenResponse = [OIDTokenResponseTests testInstanceRefresh]; NSError *oauthError = [[self class] OAuthTokenInvalidGrantErrorWithUnderlyingError:nil]; [authState updateWithTokenResponse:tokenResponse error:oauthError]; - XCTAssertFalse(authState.isAuthorized); - XCTAssertNotNil(authState.authorizationError); + XCTAssertFalse(authState.isAuthorized, @""); + XCTAssertNotNil(authState.authorizationError, @""); } /*! @brief Full lifecycle test of the code flow from code exchange, refresh, error and re-auth. @@ -294,68 +299,68 @@ - (void)testCodeFlowLifecycle { // initializes from code flow authorization response OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:authorizationResponse]; - XCTAssertEqual(authState.lastAuthorizationResponse, authorizationResponse); + XCTAssertEqual(authState.lastAuthorizationResponse, authorizationResponse, @""); XCTAssertFalse(authState.isAuthorized, @"Shouldn't be authorized as the code needs to be exchanged"); // updates with result from token exchange OIDTokenResponse *tokenResponseCodeExchange = [OIDTokenResponseTests testInstanceCodeExchange]; [authState updateWithTokenResponse:tokenResponseCodeExchange error:nil]; - XCTAssertEqual(authState.lastTokenResponse, tokenResponseCodeExchange); - XCTAssertTrue(authState.isAuthorized); + XCTAssertEqual(authState.lastTokenResponse, tokenResponseCodeExchange, @""); + XCTAssertTrue(authState.isAuthorized, @""); // updates with code refresh OIDTokenResponse *tokenResponseRefresh = [OIDTokenResponseTests testInstanceRefresh]; [authState updateWithTokenResponse:tokenResponseRefresh error:nil]; - XCTAssertEqual(authState.lastTokenResponse, tokenResponseRefresh); - XCTAssertTrue(authState.isAuthorized); + XCTAssertEqual(authState.lastTokenResponse, tokenResponseRefresh, @""); + XCTAssertTrue(authState.isAuthorized, @""); // simulates token error (invalid_grant, token revoked) NSError *oauthError = [[self class] OAuthTokenInvalidGrantErrorWithUnderlyingError:nil]; [authState updateWithTokenResponse:nil error:oauthError]; - XCTAssertFalse(authState.isAuthorized); - XCTAssertNotNil(authState.authorizationError); + XCTAssertFalse(authState.isAuthorized, @""); + XCTAssertNotNil(authState.authorizationError, @""); // simulates successful re-auth response [authState updateWithAuthorizationResponse:authorizationResponse error:nil]; - XCTAssertEqual(authState.lastAuthorizationResponse, authorizationResponse); + XCTAssertEqual(authState.lastAuthorizationResponse, authorizationResponse, @""); XCTAssertNil(authState.authorizationError, @"Error should be nil now."); XCTAssertFalse(authState.isAuthorized, @"Since this is the code flow, AuthState should still not be isAuthorized."); // updates with result from token exchange [authState updateWithTokenResponse:tokenResponseCodeExchange error:nil]; - XCTAssertEqual(authState.lastTokenResponse, tokenResponseCodeExchange); + XCTAssertEqual(authState.lastTokenResponse, tokenResponseCodeExchange, @""); XCTAssertTrue(authState.isAuthorized, @"Should be in an authorized state now"); } - (void)testSecureCoding { - XCTAssert([OIDAuthState supportsSecureCoding]); + XCTAssert([OIDAuthState supportsSecureCoding], @""); OIDAuthState *authState = [[self class] testInstance]; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:authState]; OIDAuthState *authStateCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - XCTAssertEqualObjects(authStateCopy.refreshToken, authState.refreshToken); - XCTAssertEqualObjects(authStateCopy.scope, authState.scope); + XCTAssertEqualObjects(authStateCopy.refreshToken, authState.refreshToken, @""); + XCTAssertEqualObjects(authStateCopy.scope, authState.scope, @""); XCTAssertEqualObjects(authStateCopy.lastAuthorizationResponse.authorizationCode, - authState.lastAuthorizationResponse.authorizationCode); + authState.lastAuthorizationResponse.authorizationCode, @""); XCTAssertEqualObjects(authStateCopy.lastTokenResponse.refreshToken, - authState.lastTokenResponse.refreshToken); + authState.lastTokenResponse.refreshToken, @""); XCTAssertEqualObjects(authStateCopy.authorizationError.domain, - authState.authorizationError.domain); - XCTAssertEqual(authStateCopy.authorizationError.code, authState.authorizationError.code); - XCTAssertEqual(authStateCopy.isAuthorized, authState.isAuthorized); + authState.authorizationError.domain, @""); + XCTAssertEqual(authStateCopy.authorizationError.code, authState.authorizationError.code, @""); + XCTAssertEqual(authStateCopy.isAuthorized, authState.isAuthorized, @""); // Verify the error object is indeed restored. NSError *oauthError = [[self class] OAuthTokenInvalidGrantErrorWithUnderlyingError:nil]; [authState updateWithTokenResponse:nil error:oauthError]; data = [NSKeyedArchiver archivedDataWithRootObject:authState]; - XCTAssertNotNil(authState.authorizationError); + XCTAssertNotNil(authState.authorizationError, @""); authStateCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; XCTAssertEqualObjects(authStateCopy.authorizationError.domain, - authState.authorizationError.domain); - XCTAssertEqual(authStateCopy.authorizationError.code, authState.authorizationError.code); + authState.authorizationError.domain, @""); + XCTAssertEqual(authStateCopy.authorizationError.code, authState.authorizationError.code, @""); } - (void)testIsTokenFreshWithFreshToken { @@ -371,7 +376,7 @@ - (void)testIsTokenFreshWithFreshToken { OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:authorizationResponse tokenResponse:tokenResponse]; - XCTAssertEqual([authState isTokenFresh], YES); + XCTAssertEqual([authState isTokenFresh], YES, @""); } - (void)testIsTokenFreshWithExpiredToken { @@ -387,13 +392,13 @@ - (void)testIsTokenFreshWithExpiredToken { OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:authorizationResponse tokenResponse:tokenResponse]; - XCTAssertEqual([authState isTokenFresh], NO); + XCTAssertEqual([authState isTokenFresh], NO, @""); } - (void)testIsTokenFreshRespectsTokenRefreshOverride { OIDAuthState *authState = [[self class] testInstance]; [authState setNeedsTokenRefresh]; - XCTAssertEqual([authState isTokenFresh], NO); + XCTAssertEqual([authState isTokenFresh], NO, @""); } - (void)testIsTokenFreshHandlesTokenWithoutExpirationTime { @@ -407,8 +412,9 @@ - (void)testIsTokenFreshHandlesTokenWithoutExpirationTime { OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:authorizationResponse tokenResponse:tokenResponse]; - XCTAssertEqual([authState isTokenFresh], YES); + XCTAssertEqual([authState isTokenFresh], YES, @""); } @end +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDAuthorizationRequestTests.m b/UnitTests/OIDAuthorizationRequestTests.m index 972b130ee..1d4af083a 100644 --- a/UnitTests/OIDAuthorizationRequestTests.m +++ b/UnitTests/OIDAuthorizationRequestTests.m @@ -23,6 +23,11 @@ #import "Source/OIDScopeUtilities.h" #import "Source/OIDServiceConfiguration.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + /*! @brief Test value for the @c responseType property. */ static NSString *const kTestResponseType = @"code"; @@ -204,13 +209,13 @@ - (void)testScopeInitializerWithManyScopesAndNoClientSecret { responseType:OIDResponseTypeCode additionalParameters:additionalParameters]; - XCTAssertEqualObjects(request.responseType, @"code"); - XCTAssertEqualObjects(request.scope, kTestScopesMerged); - XCTAssertEqualObjects(request.clientID, kTestClientID); - XCTAssertEqualObjects(request.clientSecret, nil); - XCTAssertEqualObjects(request.redirectURL, [NSURL URLWithString:kTestRedirectURL]); + XCTAssertEqualObjects(request.responseType, @"code", @""); + XCTAssertEqualObjects(request.scope, kTestScopesMerged, @""); + XCTAssertEqualObjects(request.clientID, kTestClientID, @""); + XCTAssertEqualObjects(request.clientSecret, nil, @""); + XCTAssertEqualObjects(request.redirectURL, [NSURL URLWithString:kTestRedirectURL], @""); XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); } - (void)testScopeInitializerWithManyScopesAndClientSecret { @@ -226,13 +231,13 @@ - (void)testScopeInitializerWithManyScopesAndClientSecret { responseType:OIDResponseTypeCode additionalParameters:additionalParameters]; - XCTAssertEqualObjects(request.responseType, @"code"); - XCTAssertEqualObjects(request.scope, kTestScopesMerged); - XCTAssertEqualObjects(request.clientID, kTestClientID); - XCTAssertEqualObjects(request.clientSecret, kTestClientSecret); - XCTAssertEqualObjects(request.redirectURL, [NSURL URLWithString:kTestRedirectURL]); + XCTAssertEqualObjects(request.responseType, @"code", @""); + XCTAssertEqualObjects(request.scope, kTestScopesMerged, @""); + XCTAssertEqualObjects(request.clientID, kTestClientID, @""); + XCTAssertEqualObjects(request.clientSecret, kTestClientSecret, @""); + XCTAssertEqualObjects(request.redirectURL, [NSURL URLWithString:kTestRedirectURL], @""); XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); } /*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying @@ -241,34 +246,34 @@ - (void)testScopeInitializerWithManyScopesAndClientSecret { - (void)testCopying { OIDAuthorizationRequest *request = [[self class] testInstance]; - XCTAssertEqualObjects(request.responseType, kTestResponseType); - XCTAssertEqualObjects(request.scope, kTestScopesMerged); - XCTAssertEqualObjects(request.clientID, kTestClientID); - XCTAssertEqualObjects(request.clientSecret, kTestClientSecret); - XCTAssertEqualObjects(request.redirectURL, [NSURL URLWithString:kTestRedirectURL]); - XCTAssertEqualObjects(request.state, kTestState); - XCTAssertEqualObjects(request.nonce, kTestNonce); - XCTAssertEqualObjects(request.codeVerifier, kTestCodeVerifier); - XCTAssertEqualObjects(request.codeChallenge, [[self class] codeChallenge]); - XCTAssertEqualObjects(request.codeChallengeMethod, [[self class] codeChallengeMethod]); + XCTAssertEqualObjects(request.responseType, kTestResponseType, @""); + XCTAssertEqualObjects(request.scope, kTestScopesMerged, @""); + XCTAssertEqualObjects(request.clientID, kTestClientID, @""); + XCTAssertEqualObjects(request.clientSecret, kTestClientSecret, @""); + XCTAssertEqualObjects(request.redirectURL, [NSURL URLWithString:kTestRedirectURL], @""); + XCTAssertEqualObjects(request.state, kTestState, @""); + XCTAssertEqualObjects(request.nonce, kTestNonce, @""); + XCTAssertEqualObjects(request.codeVerifier, kTestCodeVerifier, @""); + XCTAssertEqualObjects(request.codeChallenge, [[self class] codeChallenge], @""); + XCTAssertEqualObjects(request.codeChallengeMethod, [[self class] codeChallengeMethod], @""); XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); OIDAuthorizationRequest *requestCopy = [request copy]; - XCTAssertNotNil(requestCopy.configuration); - XCTAssertEqualObjects(requestCopy.configuration, request.configuration); - XCTAssertEqualObjects(requestCopy.responseType, request.responseType); - XCTAssertEqualObjects(requestCopy.scope, request.scope); - XCTAssertEqualObjects(requestCopy.clientID, request.clientID); - XCTAssertEqualObjects(requestCopy.clientSecret, request.clientSecret); - XCTAssertEqualObjects(requestCopy.redirectURL, request.redirectURL); - XCTAssertEqualObjects(requestCopy.state, request.state); - XCTAssertEqualObjects(requestCopy.codeVerifier, request.codeVerifier); - XCTAssertEqualObjects(requestCopy.codeChallenge, request.codeChallenge); - XCTAssertEqualObjects(requestCopy.codeChallengeMethod, request.codeChallengeMethod); + XCTAssertNotNil(requestCopy.configuration, @""); + XCTAssertEqualObjects(requestCopy.configuration, request.configuration, @""); + XCTAssertEqualObjects(requestCopy.responseType, request.responseType, @""); + XCTAssertEqualObjects(requestCopy.scope, request.scope, @""); + XCTAssertEqualObjects(requestCopy.clientID, request.clientID, @""); + XCTAssertEqualObjects(requestCopy.clientSecret, request.clientSecret, @""); + XCTAssertEqualObjects(requestCopy.redirectURL, request.redirectURL, @""); + XCTAssertEqualObjects(requestCopy.state, request.state, @""); + XCTAssertEqualObjects(requestCopy.codeVerifier, request.codeVerifier, @""); + XCTAssertEqualObjects(requestCopy.codeChallenge, request.codeChallenge, @""); + XCTAssertEqualObjects(requestCopy.codeChallengeMethod, request.codeChallengeMethod, @""); XCTAssertEqualObjects(requestCopy.additionalParameters, - request.additionalParameters); + request.additionalParameters, @""); } /*! @brief Tests the @c NSSecureCoding by round-tripping an instance through the coding process and @@ -277,17 +282,17 @@ - (void)testCopying { - (void)testSecureCoding { OIDAuthorizationRequest *request = [[self class] testInstance]; - XCTAssertEqualObjects(request.responseType, kTestResponseType); - XCTAssertEqualObjects(request.scope, kTestScopesMerged); - XCTAssertEqualObjects(request.clientID, kTestClientID); - XCTAssertEqualObjects(request.clientSecret, kTestClientSecret); - XCTAssertEqualObjects(request.redirectURL, [NSURL URLWithString:kTestRedirectURL]); - XCTAssertEqualObjects(request.state, kTestState); - XCTAssertEqualObjects(request.codeVerifier, kTestCodeVerifier); - XCTAssertEqualObjects(request.codeChallenge, [[self class] codeChallenge]); - XCTAssertEqualObjects(request.codeChallengeMethod, [[self class] codeChallengeMethod]); + XCTAssertEqualObjects(request.responseType, kTestResponseType, @""); + XCTAssertEqualObjects(request.scope, kTestScopesMerged, @""); + XCTAssertEqualObjects(request.clientID, kTestClientID, @""); + XCTAssertEqualObjects(request.clientSecret, kTestClientSecret, @""); + XCTAssertEqualObjects(request.redirectURL, [NSURL URLWithString:kTestRedirectURL], @""); + XCTAssertEqualObjects(request.state, kTestState, @""); + XCTAssertEqualObjects(request.codeVerifier, kTestCodeVerifier, @""); + XCTAssertEqualObjects(request.codeChallenge, [[self class] codeChallenge], @""); + XCTAssertEqualObjects(request.codeChallengeMethod, [[self class] codeChallengeMethod], @""); XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; OIDAuthorizationRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; @@ -296,20 +301,20 @@ - (void)testSecureCoding { // to make sure the configuration IS actually getting serialized and deserialized in the // NSSecureCoding implementation. We'll leave it up to the OIDServiceConfiguration tests to make // sure the NSSecureCoding implementation of that class is correct. - XCTAssertNotNil(requestCopy.configuration); + XCTAssertNotNil(requestCopy.configuration, @""); XCTAssertEqualObjects(requestCopy.configuration.authorizationEndpoint, - request.configuration.authorizationEndpoint); - - XCTAssertEqualObjects(requestCopy.responseType, kTestResponseType); - XCTAssertEqualObjects(requestCopy.scope, kTestScopesMerged); - XCTAssertEqualObjects(requestCopy.clientID, kTestClientID); - XCTAssertEqualObjects(requestCopy.redirectURL, [NSURL URLWithString:kTestRedirectURL]); - XCTAssertEqualObjects(requestCopy.state, kTestState); - XCTAssertEqualObjects(requestCopy.codeVerifier, kTestCodeVerifier); - XCTAssertEqualObjects(requestCopy.codeChallenge, [[self class] codeChallenge]); - XCTAssertEqualObjects(requestCopy.codeChallengeMethod, [[self class] codeChallengeMethod]); + request.configuration.authorizationEndpoint, @""); + + XCTAssertEqualObjects(requestCopy.responseType, kTestResponseType, @""); + XCTAssertEqualObjects(requestCopy.scope, kTestScopesMerged, @""); + XCTAssertEqualObjects(requestCopy.clientID, kTestClientID, @""); + XCTAssertEqualObjects(requestCopy.redirectURL, [NSURL URLWithString:kTestRedirectURL], @""); + XCTAssertEqualObjects(requestCopy.state, kTestState, @""); + XCTAssertEqualObjects(requestCopy.codeVerifier, kTestCodeVerifier, @""); + XCTAssertEqualObjects(requestCopy.codeChallenge, [[self class] codeChallenge], @""); + XCTAssertEqualObjects(requestCopy.codeChallengeMethod, [[self class] codeChallengeMethod], @""); XCTAssertEqualObjects(requestCopy.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); } /*! @brief Tests the scope string logic to make sure the disallowed characters are properly @@ -324,63 +329,63 @@ - (void)testDisallowedCharactersInScopes { scopes:@[ kTestInvalidScope1 ] redirectURL:redirectURL responseType:OIDResponseTypeCode - additionalParameters:nil]); + additionalParameters:nil], @""); XCTAssertThrows( [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID scopes:@[ kTestInvalidScope2 ] redirectURL:redirectURL responseType:OIDResponseTypeCode - additionalParameters:nil]); + additionalParameters:nil], @""); XCTAssertThrows( [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID scopes:@[ kTestInvalidScope3 ] redirectURL:redirectURL responseType:OIDResponseTypeCode - additionalParameters:nil]); + additionalParameters:nil], @""); XCTAssertThrows( [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID scopes:@[ kTestInvalidScope4 ] redirectURL:redirectURL responseType:OIDResponseTypeCode - additionalParameters:nil]); + additionalParameters:nil], @""); XCTAssertNoThrow( [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID scopes:@[ kTestValidScope1 ] redirectURL:redirectURL responseType:OIDResponseTypeCode - additionalParameters:nil]); + additionalParameters:nil], @""); XCTAssertNoThrow( [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID scopes:@[ kTestValidScope2 ] redirectURL:redirectURL responseType:OIDResponseTypeCode - additionalParameters:nil]); + additionalParameters:nil], @""); XCTAssertNoThrow( [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID scopes:@[ kTestValidScope3 ] redirectURL:redirectURL responseType:OIDResponseTypeCode - additionalParameters:nil]); + additionalParameters:nil], @""); XCTAssertNoThrow( [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID scopes:@[ kTestValidScope4 ] redirectURL:redirectURL responseType:OIDResponseTypeCode - additionalParameters:nil]); + additionalParameters:nil], @""); XCTAssertNoThrow( [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID scopes:@[ kTestValidScope5 ] redirectURL:redirectURL responseType:OIDResponseTypeCode - additionalParameters:nil]); + additionalParameters:nil], @""); } /*! @brief Returns a character set with all legal PKCE characters for the codeVerifier. @return Character set representing all legal codeVerifier characters. @@ -403,11 +408,11 @@ - (void)testPKCEVerifierCompliance { // as this test involves random numbers, repeats multiple times for (int i = 0; i < 1000; i++) { NSString *codeVerifier = [OIDAuthorizationRequest generateCodeVerifier]; - XCTAssertNotNil(codeVerifier); + XCTAssertNotNil(codeVerifier, @""); // tests that the code verifier is within the specified size bounds - XCTAssertGreaterThanOrEqual(codeVerifier.length, kCodeVerifierMinLength); - XCTAssertLessThanOrEqual(codeVerifier.length, kCodeVerifierMaxLength); + XCTAssertGreaterThanOrEqual(codeVerifier.length, kCodeVerifierMinLength, @""); + XCTAssertLessThanOrEqual(codeVerifier.length, kCodeVerifierMaxLength, @""); // tests that the code verifier uses legal characters NSCharacterSet *legalChars = [[self class] legalPKCECharacters]; @@ -422,7 +427,7 @@ - (void)testPKCEVerifierCompliance { */ - (void)testPKCEVerifierRecommendations { NSString *codeVerifier = [OIDAuthorizationRequest generateCodeVerifier]; - XCTAssertNotNil(codeVerifier); + XCTAssertNotNil(codeVerifier, @""); XCTAssertEqual(codeVerifier.length, kCodeVerifierRecommendedLength, @"The spec RECOMMENDS a '43-octet URL safe string'"); @@ -489,3 +494,5 @@ - (void)testExternalUserAgentMethods { } @end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDAuthorizationResponseTests.m b/UnitTests/OIDAuthorizationResponseTests.m index f0a437555..6206ccab8 100644 --- a/UnitTests/OIDAuthorizationResponseTests.m +++ b/UnitTests/OIDAuthorizationResponseTests.m @@ -23,6 +23,11 @@ #import "Source/OIDAuthorizationResponse.h" #import "Source/OIDGrantTypes.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + /*! @brief Test value for the @c authorizationCode property. */ static NSString *const kTestAuthorizationCode = @"Code"; @@ -113,33 +118,34 @@ + (OIDAuthorizationResponse *)testInstanceCodeFlowClientAuth { */ - (void)testCopying { OIDAuthorizationResponse *response = [[self class] testInstance]; - XCTAssertEqualObjects(response.authorizationCode, kTestAuthorizationCode); - XCTAssertEqualObjects(response.state, kTestState); - XCTAssertEqualObjects(response.accessToken, kTestAccessToken); - XCTAssertEqualObjects(response.idToken, kTestIDToken); - XCTAssertEqualObjects(response.tokenType, kTestTokenType); - XCTAssertEqualObjects(response.scope, kTestScope); + XCTAssertEqualObjects(response.authorizationCode, kTestAuthorizationCode, @""); + XCTAssertEqualObjects(response.state, kTestState, @""); + XCTAssertEqualObjects(response.accessToken, kTestAccessToken, @""); + XCTAssertEqualObjects(response.idToken, kTestIDToken, @""); + XCTAssertEqualObjects(response.tokenType, kTestTokenType, @""); + XCTAssertEqualObjects(response.scope, kTestScope, @""); XCTAssertEqualObjects(response.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); // Should be ~ kTestExpirationSeconds seconds. Avoiding swizzling NSDate here for certainty // to keep dependencies down, and simply making an assumption that this check will be executed // relatively quickly after the initialization above (less than 5 seconds.) NSTimeInterval expiration = [response.accessTokenExpirationDate timeIntervalSinceNow]; - XCTAssert(expiration > kTestExpirationSeconds - 5 && expiration <= kTestExpirationSeconds); + XCTAssert(expiration > kTestExpirationSeconds - 5 && expiration <= kTestExpirationSeconds, @""); OIDAuthorizationResponse *responseCopy = [response copy]; - XCTAssertEqualObjects(responseCopy.request, response.request); - XCTAssertEqualObjects(responseCopy.authorizationCode, response.authorizationCode); - XCTAssertEqualObjects(responseCopy.state, response.state); - XCTAssertEqualObjects(responseCopy.accessToken, response.accessToken); - XCTAssertEqualObjects(responseCopy.accessTokenExpirationDate, response.accessTokenExpirationDate); - XCTAssertEqualObjects(responseCopy.idToken, response.idToken); - XCTAssertEqualObjects(responseCopy.tokenType, response.tokenType); - XCTAssertEqualObjects(responseCopy.scope, response.scope); + XCTAssertEqualObjects(responseCopy.request, response.request, @""); + XCTAssertEqualObjects(responseCopy.authorizationCode, response.authorizationCode, @""); + XCTAssertEqualObjects(responseCopy.state, response.state, @""); + XCTAssertEqualObjects(responseCopy.accessToken, response.accessToken, @""); + XCTAssertEqualObjects(responseCopy.accessTokenExpirationDate, + response.accessTokenExpirationDate, @""); + XCTAssertEqualObjects(responseCopy.idToken, response.idToken, @""); + XCTAssertEqualObjects(responseCopy.tokenType, response.tokenType, @""); + XCTAssertEqualObjects(responseCopy.scope, response.scope, @""); XCTAssertEqualObjects(responseCopy.additionalParameters, - response.additionalParameters); + response.additionalParameters, @""); } /*! @brief Tests the @c NSSecureCoding by round-tripping an instance through the coding process and @@ -154,18 +160,21 @@ - (void)testSecureCoding { // to make sure the request IS actually getting serialized and deserialized in the // NSSecureCoding implementation. We'll leave it up to the OIDAuthorizationRequest tests to make // sure the NSSecureCoding implementation of that class is correct. - XCTAssertNotNil(responseCopy.request); - XCTAssertEqualObjects(responseCopy.request.clientID, response.request.clientID); - - XCTAssertEqualObjects(responseCopy.authorizationCode, kTestAuthorizationCode); - XCTAssertEqualObjects(responseCopy.state, kTestState); - XCTAssertEqualObjects(responseCopy.accessToken, kTestAccessToken); - XCTAssertEqualObjects(responseCopy.idToken, kTestIDToken); - XCTAssertEqualObjects(responseCopy.tokenType, kTestTokenType); - XCTAssertEqualObjects(responseCopy.scope, kTestScope); - XCTAssertEqualObjects(responseCopy.accessTokenExpirationDate, response.accessTokenExpirationDate); + XCTAssertNotNil(responseCopy.request, @""); + XCTAssertEqualObjects(responseCopy.request.clientID, response.request.clientID, @""); + + XCTAssertEqualObjects(responseCopy.authorizationCode, kTestAuthorizationCode, @""); + XCTAssertEqualObjects(responseCopy.state, kTestState, @""); + XCTAssertEqualObjects(responseCopy.accessToken, kTestAccessToken, @""); + XCTAssertEqualObjects(responseCopy.idToken, kTestIDToken, @""); + XCTAssertEqualObjects(responseCopy.tokenType, kTestTokenType, @""); + XCTAssertEqualObjects(responseCopy.scope, kTestScope, @""); + XCTAssertEqualObjects(responseCopy.accessTokenExpirationDate, response.accessTokenExpirationDate, + @""); XCTAssertEqualObjects(responseCopy.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); } @end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDGrantTypesTests.m b/UnitTests/OIDGrantTypesTests.m index a9e12e413..fb4c9e3fd 100644 --- a/UnitTests/OIDGrantTypesTests.m +++ b/UnitTests/OIDGrantTypesTests.m @@ -20,6 +20,11 @@ #import "Source/OIDGrantTypes.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + /*! @brief Unit tests for constants in @c OIDGrantTypes.m. @remarks Arguably not worth tests for this file, but adding them for consistency, and so that any future enhancements have a place to add tests if need be. @@ -45,3 +50,5 @@ - (void)testClientCredentials { } @end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDRPProfileCode.m b/UnitTests/OIDRPProfileCode.m index 45db3909a..1cf4a127d 100644 --- a/UnitTests/OIDRPProfileCode.m +++ b/UnitTests/OIDRPProfileCode.m @@ -190,7 +190,7 @@ - (void)codeFlowWithExchangeForTest:(NSString *)test [self expectationWithDescription:@"Token Exchange should complete."]; NSURL *issuer = [NSURL URLWithString:issuerString]; - + [self doRegistrationWithIssuer:issuer callback:^(OIDServiceConfiguration *configuration, OIDRegistrationResponse *registrationResponse, NSError *error) { @@ -214,13 +214,13 @@ - (void)codeFlowWithExchangeForTest:(NSString *)test responseType:OIDResponseTypeCode additionalParameters:nil]; - _coordinator = [[OIDAuthorizationUICoordinatorNonInteractive alloc] init]; + self->_coordinator = [[OIDAuthorizationUICoordinatorNonInteractive alloc] init]; [self certificationLog:@"Initiating authorization request: %@", [request authorizationRequestURL]]; [OIDAuthorizationService presentAuthorizationRequest:request - externalUserAgent:_coordinator + externalUserAgent:self->_coordinator callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *error) { [auth_complete fulfill]; diff --git a/UnitTests/OIDRegistrationResponseTests.m b/UnitTests/OIDRegistrationResponseTests.m index 1b7befd16..f679e1adf 100644 --- a/UnitTests/OIDRegistrationResponseTests.m +++ b/UnitTests/OIDRegistrationResponseTests.m @@ -81,32 +81,32 @@ + (OIDRegistrationResponse *)testInstance { */ - (void)testCopying { OIDRegistrationResponse *response = [[self class] testInstance]; - XCTAssertNotNil(response.request); - XCTAssertEqualObjects(response.clientID, kClientIDTestValue); + XCTAssertNotNil(response.request, @""); + XCTAssertEqualObjects(response.clientID, kClientIDTestValue, @""); XCTAssertEqualObjects(response.clientIDIssuedAt, - [NSDate dateWithTimeIntervalSince1970:kClientIDIssuedAtTestValue]); - XCTAssertEqualObjects(response.clientSecret, kClientSecretTestValue); + [NSDate dateWithTimeIntervalSince1970:kClientIDIssuedAtTestValue], @""); + XCTAssertEqualObjects(response.clientSecret, kClientSecretTestValue, @""); XCTAssertEqualObjects(response.clientSecretExpiresAt, - [NSDate dateWithTimeIntervalSince1970:kClientSecretExpiresAtTestValue]); - XCTAssertEqualObjects(response.registrationAccessToken, kClientRegistrationAccessTokenTestValue); + [NSDate dateWithTimeIntervalSince1970:kClientSecretExpiresAtTestValue], @""); + XCTAssertEqualObjects(response.registrationAccessToken, kClientRegistrationAccessTokenTestValue, @""); XCTAssertEqualObjects(response.registrationClientURI, - [NSURL URLWithString:kRegistrationClientURITestValue]); + [NSURL URLWithString:kRegistrationClientURITestValue], @""); XCTAssertEqualObjects(response.tokenEndpointAuthenticationMethod, - kTokenEndpointAuthMethodTestValue); + kTokenEndpointAuthMethodTestValue, @""); XCTAssertEqualObjects(response.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); OIDRegistrationResponse *responseCopy = [response copy]; - XCTAssertNotNil(responseCopy.request); - XCTAssertEqualObjects(responseCopy.clientID, response.clientID); - XCTAssertEqualObjects(responseCopy.clientIDIssuedAt, response.clientIDIssuedAt); - XCTAssertEqualObjects(responseCopy.clientSecret, response.clientSecret); - XCTAssertEqualObjects(responseCopy.clientSecretExpiresAt, response.clientSecretExpiresAt); - XCTAssertEqualObjects(responseCopy.registrationAccessToken, response.registrationAccessToken); - XCTAssertEqualObjects(responseCopy.registrationClientURI, response.registrationClientURI); + XCTAssertNotNil(responseCopy.request, @""); + XCTAssertEqualObjects(responseCopy.clientID, response.clientID, @""); + XCTAssertEqualObjects(responseCopy.clientIDIssuedAt, response.clientIDIssuedAt, @""); + XCTAssertEqualObjects(responseCopy.clientSecret, response.clientSecret, @""); + XCTAssertEqualObjects(responseCopy.clientSecretExpiresAt, response.clientSecretExpiresAt, @""); + XCTAssertEqualObjects(responseCopy.registrationAccessToken, response.registrationAccessToken, @""); + XCTAssertEqualObjects(responseCopy.registrationClientURI, response.registrationClientURI, @""); XCTAssertEqualObjects(responseCopy.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); } /*! @brief Tests the @c NSSecureCoding by round-tripping an instance through the coding process and @@ -121,19 +121,19 @@ - (void)testSecureCoding { // to make sure the request IS actually getting serialized and deserialized in the // NSSecureCoding implementation. We'll leave it up to the OIDAuthorizationRequest tests to make // sure the NSSecureCoding implementation of that class is correct. - XCTAssertNotNil(responseCopy.request); - XCTAssertEqualObjects(responseCopy.request.applicationType, response.request.applicationType); - - XCTAssertEqualObjects(responseCopy.clientID, response.clientID); - XCTAssertEqualObjects(responseCopy.clientIDIssuedAt, response.clientIDIssuedAt); - XCTAssertEqualObjects(responseCopy.clientSecret, response.clientSecret); - XCTAssertEqualObjects(responseCopy.clientSecretExpiresAt, response.clientSecretExpiresAt); - XCTAssertEqualObjects(responseCopy.registrationAccessToken, response.registrationAccessToken); - XCTAssertEqualObjects(responseCopy.registrationClientURI, response.registrationClientURI); + XCTAssertNotNil(responseCopy.request, @""); + XCTAssertEqualObjects(responseCopy.request.applicationType, response.request.applicationType, @""); + + XCTAssertEqualObjects(responseCopy.clientID, response.clientID, @""); + XCTAssertEqualObjects(responseCopy.clientIDIssuedAt, response.clientIDIssuedAt, @""); + XCTAssertEqualObjects(responseCopy.clientSecret, response.clientSecret, @""); + XCTAssertEqualObjects(responseCopy.clientSecretExpiresAt, response.clientSecretExpiresAt, @""); + XCTAssertEqualObjects(responseCopy.registrationAccessToken, response.registrationAccessToken, @""); + XCTAssertEqualObjects(responseCopy.registrationClientURI, response.registrationClientURI, @""); XCTAssertEqualObjects(responseCopy.tokenEndpointAuthenticationMethod, - response.tokenEndpointAuthenticationMethod); + response.tokenEndpointAuthenticationMethod, @""); XCTAssertEqualObjects(responseCopy.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); } /*! @brief Make sure the registration response is verified to ensure the 'client_secret_expires_at' @@ -147,7 +147,7 @@ - (void)testMissingClientSecretExpiresAtWithClientSecret { OIDClientIDParam : kClientIDTestValue, OIDClientSecretParam : kClientSecretTestValue, }]; - XCTAssertNil(response); + XCTAssertNil(response, @""); } /*! @brief Make sure the registration response missing 'registration_access_token' is detected when @@ -161,7 +161,7 @@ - (void)testMissingRegistrationAccessTokenWithRegistrationClientURI { OIDClientIDParam : kClientIDTestValue, OIDRegistrationClientURIParam : [NSURL URLWithString:kRegistrationClientURITestValue] }]; - XCTAssertNil(response); + XCTAssertNil(response, @""); } /*! @brief Make sure the registration response missing 'client_registration_uri' is detected when @@ -175,7 +175,7 @@ - (void)testMissingRegistrationClientURIWithRegistrationAccessToken { OIDClientIDParam : kClientIDTestValue, OIDRegistrationAccessTokenParam : kClientRegistrationAccessTokenTestValue }]; - XCTAssertNil(response); + XCTAssertNil(response, @""); } @end diff --git a/UnitTests/OIDResponseTypesTests.m b/UnitTests/OIDResponseTypesTests.m index 9d2b72221..b55863afc 100644 --- a/UnitTests/OIDResponseTypesTests.m +++ b/UnitTests/OIDResponseTypesTests.m @@ -20,6 +20,11 @@ #import "Source/OIDResponseTypes.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + /*! @brief Unit tests for constants in @c OIDResponseTypes.m. @remarks Arguably not worth tests for this file, but adding them for consistency, and so that any future enhancements have a place to add tests if need be. @@ -29,15 +34,17 @@ @interface OIDResponseTypesTests : XCTestCase @implementation OIDResponseTypesTests - (void)testCode { - XCTAssertEqualObjects(OIDResponseTypeCode, @"code"); + XCTAssertEqualObjects(OIDResponseTypeCode, @"code", @""); } - (void)testToken { - XCTAssertEqualObjects(OIDResponseTypeToken, @"token"); + XCTAssertEqualObjects(OIDResponseTypeToken, @"token", @""); } - (void)testIDToken { - XCTAssertEqualObjects(OIDResponseTypeIDToken, @"id_token"); + XCTAssertEqualObjects(OIDResponseTypeIDToken, @"id_token", @""); } @end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDScopesTests.m b/UnitTests/OIDScopesTests.m index f28a9731a..f6ffb9a6f 100644 --- a/UnitTests/OIDScopesTests.m +++ b/UnitTests/OIDScopesTests.m @@ -20,6 +20,11 @@ #import "Source/OIDScopes.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + /*! @brief Unit tests for constants in @c OIDScopes.m. @remarks Arguably not worth tests for this file, but adding them for consistency, and so that any future enhancements have a place to add tests if need be. @@ -45,3 +50,5 @@ - (void)testProfile { } @end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDServiceConfigurationTests.m b/UnitTests/OIDServiceConfigurationTests.m index 6605fe5bf..a15cbd6b8 100644 --- a/UnitTests/OIDServiceConfigurationTests.m +++ b/UnitTests/OIDServiceConfigurationTests.m @@ -26,6 +26,11 @@ #import "Source/OIDServiceConfiguration.h" #import "Source/OIDServiceDiscovery.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + /*! @brief The callback signature for @c NSURLSession 's @c dataTaskWithURL:completionHandler: method, which we swizzle in @c testFetcher to fake the network response with an OpenID Connect Discovery document. @@ -143,11 +148,11 @@ - (void)replaceClassMethodForClass:(Class)class selector:(SEL)selector withBlock - (void)testInitializer { OIDServiceConfiguration *configuration = [[self class] testInstance]; XCTAssertEqualObjects(configuration.authorizationEndpoint.absoluteString, - kInitializerTestAuthEndpoint); + kInitializerTestAuthEndpoint, @""); XCTAssertEqualObjects(configuration.tokenEndpoint.absoluteString, kInitializerTestTokenEndpoint); XCTAssertEqualObjects(configuration.registrationEndpoint.absoluteString, - kInitializerTestRegistrationEndpoint); + kInitializerTestRegistrationEndpoint, @""); } - (void)testIssuer { @@ -224,12 +229,12 @@ - (void)testFetcher { ^(OIDServiceConfiguration *_Nullable serviceConfiguration, NSError *_Nullable error) { [expectation fulfill]; - XCTAssertNil(error); - XCTAssertNotNil(serviceConfiguration); + XCTAssertNil(error, @""); + XCTAssertNotNil(serviceConfiguration, @""); XCTAssertEqualObjects(serviceConfiguration.tokenEndpoint, - expectedValues.tokenEndpoint); + expectedValues.tokenEndpoint, @""); XCTAssertEqualObjects(serviceConfiguration.authorizationEndpoint, - expectedValues.authorizationEndpoint); + expectedValues.authorizationEndpoint, @""); }; [OIDAuthorizationService discoverServiceConfigurationForDiscoveryURL:url completion:callback]; @@ -259,8 +264,8 @@ - (void)testFetcherWithNetworkError { ^(OIDServiceConfiguration *_Nullable serviceConfiguration, NSError *_Nullable error) { [expectation fulfill]; - XCTAssertNotNil(error); - XCTAssertNil(serviceConfiguration); + XCTAssertNotNil(error, @""); + XCTAssertNil(serviceConfiguration, @""); }; [OIDAuthorizationService discoverServiceConfigurationForDiscoveryURL:url completion:callback]; @@ -300,8 +305,8 @@ - (void)testFetcherWithErrorCode { ^(OIDServiceConfiguration *_Nullable serviceConfiguration, NSError *_Nullable error) { [expectation fulfill]; - XCTAssertNotNil(error); - XCTAssertNil(serviceConfiguration); + XCTAssertNotNil(error, @""); + XCTAssertNil(serviceConfiguration, @""); }; [OIDAuthorizationService discoverServiceConfigurationForDiscoveryURL:url completion:callback]; @@ -336,8 +341,8 @@ - (void)testFetcherWithBadJSON { ^(OIDServiceConfiguration *_Nullable serviceConfiguration, NSError *_Nullable error) { [expectation fulfill]; - XCTAssertNotNil(error); - XCTAssertNil(serviceConfiguration); + XCTAssertNotNil(error, @""); + XCTAssertNil(serviceConfiguration, @""); }; [OIDAuthorizationService discoverServiceConfigurationForDiscoveryURL:url completion:callback]; @@ -352,9 +357,9 @@ - (void)testSecureCoding { NSData *data = [NSKeyedArchiver archivedDataWithRootObject:configuration]; OIDServiceConfiguration *unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - XCTAssertEqualObjects(configuration.authorizationEndpoint, unarchived.authorizationEndpoint); - XCTAssertEqualObjects(configuration.tokenEndpoint, unarchived.tokenEndpoint); - XCTAssertEqualObjects(configuration.registrationEndpoint, unarchived.registrationEndpoint); + XCTAssertEqualObjects(configuration.authorizationEndpoint, unarchived.authorizationEndpoint, @""); + XCTAssertEqualObjects(configuration.tokenEndpoint, unarchived.tokenEndpoint, @""); + XCTAssertEqualObjects(configuration.registrationEndpoint, unarchived.registrationEndpoint, @""); } /*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying @@ -365,9 +370,11 @@ - (void)testCopying { OIDServiceConfiguration *configuration = [[self class] testInstance]; OIDServiceConfiguration *unarchived = [configuration copy]; - XCTAssertEqualObjects(configuration.authorizationEndpoint, unarchived.authorizationEndpoint); - XCTAssertEqualObjects(configuration.tokenEndpoint, unarchived.tokenEndpoint); - XCTAssertEqualObjects(configuration.registrationEndpoint, unarchived.registrationEndpoint); + XCTAssertEqualObjects(configuration.authorizationEndpoint, unarchived.authorizationEndpoint, @""); + XCTAssertEqualObjects(configuration.tokenEndpoint, unarchived.tokenEndpoint, @""); + XCTAssertEqualObjects(configuration.registrationEndpoint, unarchived.registrationEndpoint, @""); } @end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDServiceDiscoveryTests.m b/UnitTests/OIDServiceDiscoveryTests.m index 4352c0b9c..3610c1e78 100644 --- a/UnitTests/OIDServiceDiscoveryTests.m +++ b/UnitTests/OIDServiceDiscoveryTests.m @@ -21,6 +21,11 @@ #import "Source/OIDError.h" #import "Source/OIDServiceDiscovery.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + /*! Testing URL used when testing URL conversions. */ static NSString *const kTestURL = @"http://www.google.com/"; @@ -202,7 +207,7 @@ - (void)testURLs { NSURL *testPolicyURL = [NSURL URLWithString:kTestURL]; - XCTAssertEqualObjects(discovery.OPPolicyURI, testPolicyURL); + XCTAssertEqualObjects(discovery.OPPolicyURI, testPolicyURL, @""); } /*! @brief Tests that we get an error when the document is not valid JSON. @@ -213,8 +218,8 @@ - (void)testErrorWhenBadFormat { XCTAssertNil(discovery, @"When initializing a discovery document, it should not return an " "instance if it is not valid JSON."); XCTAssertNotNil(error, @"There should be an error indicating we received bad JSON."); - XCTAssertEqualObjects(error.domain, OIDGeneralErrorDomain); - XCTAssertEqual(error.code, OIDErrorCodeJSONDeserializationError); + XCTAssertEqualObjects(error.domain, OIDGeneralErrorDomain, @""); + XCTAssertEqual(error.code, OIDErrorCodeJSONDeserializationError, @""); } /*! @brief Tests that we get an error when the required fields aren't in the source dictionary. @@ -226,8 +231,8 @@ - (void)testErrorWhenMissingFields { XCTAssertNil(discovery, @"When initializing a discovery document, it should not return an " "instance if there are missing required fields."); XCTAssertNotNil(error, @"There should be an error indicating we are missing required fields."); - XCTAssertEqualObjects(error.domain, OIDGeneralErrorDomain); - XCTAssertEqual(error.code, OIDErrorCodeInvalidDiscoveryDocument); + XCTAssertEqualObjects(error.domain, OIDGeneralErrorDomain, @""); + XCTAssertEqual(error.code, OIDErrorCodeInvalidDiscoveryDocument, @""); } /*! @brief Tests that we get an error when the required fields aren't in the source dictionary. @@ -239,8 +244,8 @@ - (void)testErrorWhenMissingFieldsJSON { XCTAssertNil(discovery, @"When initializing a discovery document with JSON, it should not return" " an instance if there are missing required fields."); XCTAssertNotNil(error, @"There should be an error indicating we are missing required fields."); - XCTAssertEqualObjects(error.domain, OIDGeneralErrorDomain); - XCTAssertEqual(error.code, OIDErrorCodeInvalidDiscoveryDocument); + XCTAssertEqualObjects(error.domain, OIDGeneralErrorDomain, @""); + XCTAssertEqual(error.code, OIDErrorCodeInvalidDiscoveryDocument, @""); } /*! @brief Tests that we do not get an error, and we do get an instance of @@ -276,8 +281,8 @@ - (void)pendingTestErrorWhenMalformedFields { @"When initializing a discovery document, it should not return an instance if there" " are missing required fields."); XCTAssertNotNil(error, @"There should be an error indicating we are missing required fields."); - XCTAssertEqualObjects(error.domain, OIDGeneralErrorDomain); - XCTAssertEqual(error.code, OIDErrorCodeInvalidDiscoveryDocument); + XCTAssertEqualObjects(error.domain, OIDGeneralErrorDomain, @""); + XCTAssertEqual(error.code, OIDErrorCodeInvalidDiscoveryDocument, @""); } /*! @brief Tests that we get an error when null is passed in through JSON. @@ -290,8 +295,8 @@ - (void)pendingTestErrorWhenJSONNullField { XCTAssertNil(discovery, @"When initializing a discovery document, it should not return an " "instance if there are missing required fields."); XCTAssertNotNil(error, @"There should be an error indicating we are missing required fields."); - XCTAssertEqualObjects(error.domain, OIDGeneralErrorDomain); - XCTAssertEqual(error.code, OIDErrorCodeInvalidDiscoveryDocument); + XCTAssertEqualObjects(error.domain, OIDGeneralErrorDomain, @""); + XCTAssertEqual(error.code, OIDErrorCodeInvalidDiscoveryDocument, @""); } /*! @brief Tests that the JSON String version results in a valid object. @@ -304,7 +309,7 @@ - (void)testJSONStringDecoding { "valid object"); XCTAssertNil(error, @"There should not be any errors."); XCTAssertEqualObjects(discovery.authorizationEndpoint, - [[self class] googleDiscoveryAuthorizationEndpoint]); + [[self class] googleDiscoveryAuthorizationEndpoint], @""); } /*! @brief Tests that the JSON NSData version results in a valid object. @@ -318,7 +323,7 @@ - (void)testJSONDataDecoding { "valid object"); XCTAssertNil(error, @"There should not be any errors."); XCTAssertEqualObjects(discovery.authorizationEndpoint, - [[self class] googleDiscoveryAuthorizationEndpoint]); + [[self class] googleDiscoveryAuthorizationEndpoint], @""); } /*! @brief Tests that initialising with the dictionary initialiser and the JSON initialiser result @@ -344,8 +349,8 @@ - (void)pendingTestJSONEqualsDictionary { XCTAssertNotNil(discovery2, @"Discovery document should initialize."); XCTAssertNil(error, @"There should not be any errors."); - XCTAssertEqualObjects(discovery.discoveryDictionary, discovery2.discoveryDictionary); - XCTAssertEqualObjects(discovery, discovery2); + XCTAssertEqualObjects(discovery.discoveryDictionary, discovery2.discoveryDictionary, @""); + XCTAssertEqualObjects(discovery, discovery2, @""); } @@ -374,7 +379,7 @@ - (void)testSecureCoding { NSData *data = [NSKeyedArchiver archivedDataWithRootObject:discovery]; OIDServiceDiscovery *unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary); + XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary, @""); } /*! @brief Tests the NSCopying implementation by round-tripping an instance through the copying @@ -390,7 +395,7 @@ - (void)testCopying { OIDServiceDiscovery *unarchived = [discovery copy]; - XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary); + XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary, @""); } #pragma mark - Field Mappings @@ -460,62 +465,64 @@ - (void)testField_##_field_ { XCTAssertEqualObjects(discovery._field_, [NSURL URLWithString:_test_value_]); \ } -TestURLFieldBackedBy(issuer, kIssuerKey, kTestURL); -TestURLFieldBackedBy(authorizationEndpoint, kAuthorizationEndpointKey, kTestURL); -TestURLFieldBackedBy(tokenEndpoint, kTokenEndpointKey, kTestURL); -TestURLFieldBackedBy(userinfoEndpoint, kUserinfoEndpointKey, kTestURL); -TestURLFieldBackedBy(jwksURL, kJWKSURLKey, kTestURL); -TestURLFieldBackedBy(registrationEndpoint, kRegistrationEndpointKey, kTestURL); -TestFieldBackedBy(scopesSupported, kScopesSupportedKey, @"Scopes Supported"); -TestFieldBackedBy(responseTypesSupported, kResponseTypesSupportedKey, @"Response Types Supported"); -TestFieldBackedBy(responseModesSupported, kResponseModesSupportedKey, @"Response Modes Supported"); -TestFieldBackedBy(grantTypesSupported, kGrantTypesSupportedKey, @"Grant Types Supported"); -TestFieldBackedBy(acrValuesSupported, kACRValuesSupportedKey, @"ACR Values Supported"); -TestFieldBackedBy(subjectTypesSupported, kSubjectTypesSupportedKey, @"Subject Types Supported"); +TestURLFieldBackedBy(issuer, kIssuerKey, kTestURL) +TestURLFieldBackedBy(authorizationEndpoint, kAuthorizationEndpointKey, kTestURL) +TestURLFieldBackedBy(tokenEndpoint, kTokenEndpointKey, kTestURL) +TestURLFieldBackedBy(userinfoEndpoint, kUserinfoEndpointKey, kTestURL) +TestURLFieldBackedBy(jwksURL, kJWKSURLKey, kTestURL) +TestURLFieldBackedBy(registrationEndpoint, kRegistrationEndpointKey, kTestURL) +TestFieldBackedBy(scopesSupported, kScopesSupportedKey, @"Scopes Supported") +TestFieldBackedBy(responseTypesSupported, kResponseTypesSupportedKey, @"Response Types Supported") +TestFieldBackedBy(responseModesSupported, kResponseModesSupportedKey, @"Response Modes Supported") +TestFieldBackedBy(grantTypesSupported, kGrantTypesSupportedKey, @"Grant Types Supported") +TestFieldBackedBy(acrValuesSupported, kACRValuesSupportedKey, @"ACR Values Supported") +TestFieldBackedBy(subjectTypesSupported, kSubjectTypesSupportedKey, @"Subject Types Supported") TestFieldBackedBy(IDTokenSigningAlgorithmValuesSupported, kIDTokenSigningAlgorithmValuesSupportedKey, - @"Token Signing Algorithm Values Supported"); + @"Token Signing Algorithm Values Supported") TestFieldBackedBy(IDTokenEncryptionAlgorithmValuesSupported, kIDTokenEncryptionAlgorithmValuesSupportedKey, - @"Token Encryption Algorithm Values Supported"); + @"Token Encryption Algorithm Values Supported") TestFieldBackedBy(IDTokenEncryptionEncodingValuesSupported, kIDTokenEncryptionEncodingValuesSupportedKey, - @"token Encryption Encoding Values Supported"); + @"token Encryption Encoding Values Supported") TestFieldBackedBy(userinfoSigningAlgorithmValuesSupported, kUserinfoSigningAlgorithmValuesSupportedKey, - @"User Info Signing Algorithm Values Supported"); + @"User Info Signing Algorithm Values Supported") TestFieldBackedBy(userinfoEncryptionAlgorithmValuesSupported, kUserinfoEncryptionAlgorithmValuesSupportedKey, - @"User Info Encryption Algorithm Values Supported"); + @"User Info Encryption Algorithm Values Supported") TestFieldBackedBy(userinfoEncryptionEncodingValuesSupported, kUserinfoEncryptionEncodingValuesSupportedKey, - @"User Info Encryption Encoding Values Supported"); + @"User Info Encryption Encoding Values Supported") TestFieldBackedBy(requestObjectSigningAlgorithmValuesSupported, kRequestObjectSigningAlgorithmValuesSupportedKey, - @"Request Object Signing Algorithm Values Supported"); + @"Request Object Signing Algorithm Values Supported") TestFieldBackedBy(requestObjectEncryptionAlgorithmValuesSupported, kRequestObjectEncryptionAlgorithmValuesSupportedKey, - @"Reqest Object Encryption Algorithm Values Supported"); + @"Reqest Object Encryption Algorithm Values Supported") TestFieldBackedBy(requestObjectEncryptionEncodingValuesSupported, kRequestObjectEncryptionEncodingValuesSupported, - @"Request Object Encryption Encoding Values Supported"); + @"Request Object Encryption Encoding Values Supported") TestFieldBackedBy(tokenEndpointAuthMethodsSupported, kTokenEndpointAuthMethodsSupportedKey, - @"Token Endpoint Auth Methods Supported"); + @"Token Endpoint Auth Methods Supported") TestFieldBackedBy(tokenEndpointAuthSigningAlgorithmValuesSupported, kTokenEndpointAuthSigningAlgorithmValuesSupportedKey, - @"Token Endpoint Auth Signing Algorithm Values Supported"); -TestFieldBackedBy(displayValuesSupported, kDisplayValuesSupportedKey, @"Display Values Supported"); -TestFieldBackedBy(claimTypesSupported, kClaimTypesSupportedKey, @"Claim Types Supported"); -TestFieldBackedBy(claimsSupported, kClaimsSupportedKey, @"Claims Supported"); -TestURLFieldBackedBy(serviceDocumentation, kServiceDocumentationKey, kTestURL); -TestFieldBackedBy(claimsLocalesSupported, kClaimsLocalesSupportedKey, @"Claims Locales Supported"); -TestFieldBackedBy(UILocalesSupported, kUILocalesSupportedKey, @"UI Locales Supported"); -TestBooleanFieldBackedBy(claimsParameterSupported, kClaimsParameterSupportedKey, YES); -TestBooleanFieldBackedBy(requestParameterSupported, kRequestParameterSupportedKey, YES); -TestBooleanFieldBackedBy(requestURIParameterSupported, kRequestURIParameterSupportedKey, NO); -TestBooleanFieldBackedBy(requireRequestURIRegistration, kRequireRequestURIRegistrationKey, YES); -TestURLFieldBackedBy(OPPolicyURI, kOPPolicyURIKey, kTestURL); -TestURLFieldBackedBy(OPTosURI, kOPTosURIKey, kTestURL); + @"Token Endpoint Auth Signing Algorithm Values Supported") +TestFieldBackedBy(displayValuesSupported, kDisplayValuesSupportedKey, @"Display Values Supported") +TestFieldBackedBy(claimTypesSupported, kClaimTypesSupportedKey, @"Claim Types Supported") +TestFieldBackedBy(claimsSupported, kClaimsSupportedKey, @"Claims Supported") +TestURLFieldBackedBy(serviceDocumentation, kServiceDocumentationKey, kTestURL) +TestFieldBackedBy(claimsLocalesSupported, kClaimsLocalesSupportedKey, @"Claims Locales Supported") +TestFieldBackedBy(UILocalesSupported, kUILocalesSupportedKey, @"UI Locales Supported") +TestBooleanFieldBackedBy(claimsParameterSupported, kClaimsParameterSupportedKey, YES) +TestBooleanFieldBackedBy(requestParameterSupported, kRequestParameterSupportedKey, YES) +TestBooleanFieldBackedBy(requestURIParameterSupported, kRequestURIParameterSupportedKey, NO) +TestBooleanFieldBackedBy(requireRequestURIRegistration, kRequireRequestURIRegistrationKey, YES) +TestURLFieldBackedBy(OPPolicyURI, kOPPolicyURIKey, kTestURL) +TestURLFieldBackedBy(OPTosURI, kOPTosURIKey, kTestURL) @end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDTokenRequestTests.m b/UnitTests/OIDTokenRequestTests.m index 3897de81c..10ed05cde 100644 --- a/UnitTests/OIDTokenRequestTests.m +++ b/UnitTests/OIDTokenRequestTests.m @@ -26,6 +26,11 @@ #import "Source/OIDServiceConfiguration.h" #import "Source/OIDTokenRequest.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + /*! @brief Test value for the @c refreshToken property. */ static NSString *const kRefreshTokenTestValue = @"refresh_token"; @@ -128,37 +133,49 @@ - (void)testCopying { OIDTokenRequest *request = [[self class] testInstance]; XCTAssertEqualObjects(request.configuration.authorizationEndpoint, - authResponse.request.configuration.authorizationEndpoint); - XCTAssertEqualObjects(request.grantType, OIDGrantTypeAuthorizationCode); - XCTAssertEqualObjects(request.authorizationCode, authResponse.authorizationCode); - XCTAssertEqualObjects(request.redirectURL, authResponse.request.redirectURL); - XCTAssertEqualObjects(request.clientID, authResponse.request.clientID); - XCTAssertEqualObjects(request.clientSecret, authResponse.request.clientSecret); - XCTAssertEqualObjects(request.scope, authResponse.request.scope); - XCTAssertEqualObjects(request.refreshToken, kRefreshTokenTestValue); - XCTAssertEqualObjects(request.codeVerifier, authResponse.request.codeVerifier); - XCTAssertNotNil(request.additionalParameters); + authResponse.request.configuration.authorizationEndpoint, + @"Request and response authorization endpoints should be equal."); + XCTAssertEqualObjects(request.grantType, OIDGrantTypeAuthorizationCode, + @"Request grant type should be OIDGrantTypeAuthorizationCode."); + XCTAssertEqualObjects(request.authorizationCode, authResponse.authorizationCode, + @"Request and response authorization codes should be equal."); + XCTAssertEqualObjects(request.redirectURL, authResponse.request.redirectURL, + @"Request and response redirectURLs should be equal."); + XCTAssertEqualObjects(request.clientID, authResponse.request.clientID, + @"Request and response clientIDs should be equal."); + XCTAssertEqualObjects(request.clientSecret, authResponse.request.clientSecret, + @"Request and response clientSecrets should be equal."); + XCTAssertEqualObjects(request.scope, authResponse.request.scope, + @"Request and response scope values should be equal."); + XCTAssertEqualObjects(request.refreshToken, kRefreshTokenTestValue, + @"Request refreshToken should be equal to kRefreshTokenTestValue."); + XCTAssertEqualObjects(request.codeVerifier, authResponse.request.codeVerifier, + @"Request and response codeVerifiers should be equal."); + XCTAssertNotNil(request.additionalParameters, + @"Request's additionalParameters field should not be nil."); XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, + @"The request's kTestAdditionalParameterKey additional parameter should " + "be equal to kTestAdditionalParameterValue."); OIDTokenRequest *requestCopy = [request copy]; // Not a full test of the configuration deserialization, but should be sufficient as a smoke test // to make sure the configuration IS actually getting carried along in the copy implementation. XCTAssertEqualObjects(requestCopy.configuration.authorizationEndpoint, - request.configuration.authorizationEndpoint); - - XCTAssertEqualObjects(requestCopy.grantType, request.grantType); - XCTAssertEqualObjects(requestCopy.authorizationCode, request.authorizationCode); - XCTAssertEqualObjects(requestCopy.redirectURL, request.redirectURL); - XCTAssertEqualObjects(requestCopy.clientID, request.clientID); - XCTAssertEqualObjects(requestCopy.clientSecret, request.clientSecret); - XCTAssertEqualObjects(requestCopy.scope, authResponse.request.scope); - XCTAssertEqualObjects(requestCopy.refreshToken, kRefreshTokenTestValue); - XCTAssertEqualObjects(requestCopy.codeVerifier, authResponse.request.codeVerifier); - XCTAssertNotNil(requestCopy.additionalParameters); + request.configuration.authorizationEndpoint, @""); + + XCTAssertEqualObjects(requestCopy.grantType, request.grantType, @""); + XCTAssertEqualObjects(requestCopy.authorizationCode, request.authorizationCode, @""); + XCTAssertEqualObjects(requestCopy.redirectURL, request.redirectURL, @""); + XCTAssertEqualObjects(requestCopy.clientID, request.clientID, @""); + XCTAssertEqualObjects(requestCopy.clientSecret, request.clientSecret, @""); + XCTAssertEqualObjects(requestCopy.scope, authResponse.request.scope, @""); + XCTAssertEqualObjects(requestCopy.refreshToken, kRefreshTokenTestValue, @""); + XCTAssertEqualObjects(requestCopy.codeVerifier, authResponse.request.codeVerifier, @""); + XCTAssertNotNil(requestCopy.additionalParameters, @""); XCTAssertEqualObjects(requestCopy.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); } /*! @brief Tests the @c NSSecureCoding by round-tripping an instance through the coding process and @@ -169,18 +186,18 @@ - (void)testSecureCoding { OIDTokenRequest *request = [[self class] testInstance]; XCTAssertEqualObjects(request.configuration.authorizationEndpoint, - authResponse.request.configuration.authorizationEndpoint); - XCTAssertEqualObjects(request.grantType, OIDGrantTypeAuthorizationCode); - XCTAssertEqualObjects(request.authorizationCode, authResponse.authorizationCode); - XCTAssertEqualObjects(request.redirectURL, authResponse.request.redirectURL); - XCTAssertEqualObjects(request.clientID, authResponse.request.clientID); - XCTAssertEqualObjects(request.clientSecret, authResponse.request.clientSecret); - XCTAssertEqualObjects(request.scope, authResponse.request.scope); - XCTAssertEqualObjects(request.refreshToken, kRefreshTokenTestValue); - XCTAssertEqualObjects(request.codeVerifier, authResponse.request.codeVerifier); - XCTAssertNotNil(request.additionalParameters); + authResponse.request.configuration.authorizationEndpoint, @""); + XCTAssertEqualObjects(request.grantType, OIDGrantTypeAuthorizationCode, @""); + XCTAssertEqualObjects(request.authorizationCode, authResponse.authorizationCode, @""); + XCTAssertEqualObjects(request.redirectURL, authResponse.request.redirectURL, @""); + XCTAssertEqualObjects(request.clientID, authResponse.request.clientID, @""); + XCTAssertEqualObjects(request.clientSecret, authResponse.request.clientSecret, @""); + XCTAssertEqualObjects(request.scope, authResponse.request.scope, @""); + XCTAssertEqualObjects(request.refreshToken, kRefreshTokenTestValue, @""); + XCTAssertEqualObjects(request.codeVerifier, authResponse.request.codeVerifier, @""); + XCTAssertNotNil(request.additionalParameters, @""); XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; OIDTokenRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; @@ -190,19 +207,18 @@ - (void)testSecureCoding { // NSSecureCoding implementation. We'll leave it up to the OIDServiceConfiguration tests to make // sure the NSSecureCoding implementation of that class is correct. XCTAssertEqualObjects(requestCopy.configuration.authorizationEndpoint, - request.configuration.authorizationEndpoint); - - XCTAssertEqualObjects(requestCopy.grantType, request.grantType); - XCTAssertEqualObjects(requestCopy.authorizationCode, request.authorizationCode); - XCTAssertEqualObjects(requestCopy.redirectURL, request.redirectURL); - XCTAssertEqualObjects(requestCopy.clientID, request.clientID); - XCTAssertEqualObjects(requestCopy.clientSecret, request.clientSecret); - XCTAssertEqualObjects(requestCopy.scope, authResponse.request.scope); - XCTAssertEqualObjects(requestCopy.refreshToken, kRefreshTokenTestValue); - XCTAssertEqualObjects(requestCopy.codeVerifier, authResponse.request.codeVerifier); - XCTAssertNotNil(requestCopy.additionalParameters); + request.configuration.authorizationEndpoint, @""); + XCTAssertEqualObjects(requestCopy.grantType, request.grantType, @""); + XCTAssertEqualObjects(requestCopy.authorizationCode, request.authorizationCode, @""); + XCTAssertEqualObjects(requestCopy.redirectURL, request.redirectURL, @""); + XCTAssertEqualObjects(requestCopy.clientID, request.clientID, @""); + XCTAssertEqualObjects(requestCopy.clientSecret, request.clientSecret, @""); + XCTAssertEqualObjects(requestCopy.scope, authResponse.request.scope, @""); + XCTAssertEqualObjects(requestCopy.refreshToken, kRefreshTokenTestValue, @""); + XCTAssertEqualObjects(requestCopy.codeVerifier, authResponse.request.codeVerifier, @""); + XCTAssertNotNil(requestCopy.additionalParameters, @""); XCTAssertEqualObjects(requestCopy.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); } - (void)testURLRequestNoClientAuth { @@ -222,3 +238,5 @@ - (void)testURLRequestBasicClientAuth { } @end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDTokenResponseTests.m b/UnitTests/OIDTokenResponseTests.m index 57a96de85..1b4051f5f 100644 --- a/UnitTests/OIDTokenResponseTests.m +++ b/UnitTests/OIDTokenResponseTests.m @@ -22,6 +22,11 @@ #import "Source/OIDTokenRequest.h" #import "Source/OIDTokenResponse.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + /*! @brief The key for the @c accessToken property in the incoming parameters and for @c NSSecureCoding. */ @@ -133,31 +138,31 @@ + (OIDTokenResponse *)testInstanceRefresh { */ - (void)testCopying { OIDTokenResponse *response = [[self class] testInstance]; - XCTAssertNotNil(response.request); - XCTAssertEqualObjects(response.accessToken, kAccessTokenTestValue); - XCTAssertEqualObjects(response.tokenType, kTokenTypeTestValue); - XCTAssertEqualObjects(response.idToken, kIDTokenTestValue); - XCTAssertEqualObjects(response.refreshToken, kRefreshTokenTestValue); - XCTAssertEqualObjects(response.scope, kScopesTestValue); + XCTAssertNotNil(response.request, @""); + XCTAssertEqualObjects(response.accessToken, kAccessTokenTestValue, @""); + XCTAssertEqualObjects(response.tokenType, kTokenTypeTestValue, @""); + XCTAssertEqualObjects(response.idToken, kIDTokenTestValue, @""); + XCTAssertEqualObjects(response.refreshToken, kRefreshTokenTestValue, @""); + XCTAssertEqualObjects(response.scope, kScopesTestValue, @""); XCTAssertEqualObjects(response.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); // Should be ~ kTestExpirationSeconds seconds. Avoiding swizzling NSDate here for certainty // to keep dependencies down, and simply making an assumption that this check will be executed // relatively quickly after the initialization above (less than 5 seconds.) NSTimeInterval expiration = [response.accessTokenExpirationDate timeIntervalSinceNow]; - XCTAssert(expiration > kExpiresInTestValue - 5 && expiration <= kExpiresInTestValue); + XCTAssert(expiration > kExpiresInTestValue - 5 && expiration <= kExpiresInTestValue, @""); OIDTokenResponse *responseCopy = [response copy]; - XCTAssertNotNil(responseCopy.request); - XCTAssertEqualObjects(responseCopy.accessToken, kAccessTokenTestValue); - XCTAssertEqualObjects(responseCopy.tokenType, kTokenTypeTestValue); - XCTAssertEqualObjects(responseCopy.idToken, kIDTokenTestValue); - XCTAssertEqualObjects(responseCopy.refreshToken, kRefreshTokenTestValue); - XCTAssertEqualObjects(responseCopy.scope, kScopesTestValue); + XCTAssertNotNil(responseCopy.request, @""); + XCTAssertEqualObjects(responseCopy.accessToken, kAccessTokenTestValue, @""); + XCTAssertEqualObjects(responseCopy.tokenType, kTokenTypeTestValue, @""); + XCTAssertEqualObjects(responseCopy.idToken, kIDTokenTestValue, @""); + XCTAssertEqualObjects(responseCopy.refreshToken, kRefreshTokenTestValue, @""); + XCTAssertEqualObjects(responseCopy.scope, kScopesTestValue, @""); XCTAssertEqualObjects(responseCopy.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); } /*! @brief Tests the @c NSSecureCoding by round-tripping an instance through the coding process and @@ -172,16 +177,18 @@ - (void)testSecureCoding { // to make sure the request IS actually getting serialized and deserialized in the // NSSecureCoding implementation. We'll leave it up to the OIDAuthorizationRequest tests to make // sure the NSSecureCoding implementation of that class is correct. - XCTAssertNotNil(responseCopy.request); - XCTAssertEqualObjects(responseCopy.request.clientID, response.request.clientID); - - XCTAssertEqualObjects(responseCopy.accessToken, kAccessTokenTestValue); - XCTAssertEqualObjects(responseCopy.tokenType, kTokenTypeTestValue); - XCTAssertEqualObjects(responseCopy.idToken, kIDTokenTestValue); - XCTAssertEqualObjects(responseCopy.refreshToken, kRefreshTokenTestValue); - XCTAssertEqualObjects(responseCopy.scope, kScopesTestValue); + XCTAssertNotNil(responseCopy.request, @""); + XCTAssertEqualObjects(responseCopy.request.clientID, response.request.clientID, @""); + + XCTAssertEqualObjects(responseCopy.accessToken, kAccessTokenTestValue, @""); + XCTAssertEqualObjects(responseCopy.tokenType, kTokenTypeTestValue, @""); + XCTAssertEqualObjects(responseCopy.idToken, kIDTokenTestValue, @""); + XCTAssertEqualObjects(responseCopy.refreshToken, kRefreshTokenTestValue, @""); + XCTAssertEqualObjects(responseCopy.scope, kScopesTestValue, @""); XCTAssertEqualObjects(responseCopy.additionalParameters[kTestAdditionalParameterKey], - kTestAdditionalParameterValue); + kTestAdditionalParameterValue, @""); } @end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDURLQueryComponentTests.m b/UnitTests/OIDURLQueryComponentTests.m index d854226d6..e496897a8 100644 --- a/UnitTests/OIDURLQueryComponentTests.m +++ b/UnitTests/OIDURLQueryComponentTests.m @@ -20,6 +20,11 @@ #import "Source/OIDURLQueryComponent.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + /*! @brief A testing parameter name. */ static NSString *const kTestParameterName = @"ParameterName"; @@ -72,21 +77,21 @@ - (void)testAddingParameter { OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init]; [query addParameter:kTestParameterName value:kTestParameterValue]; XCTAssertEqualObjects([query valuesForParameter:kTestParameterName].firstObject, - kTestParameterValue); + kTestParameterValue, @""); } - (void)testAddingTwoParameters { OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init]; [query addParameter:kTestParameterName value:kTestParameterValue]; XCTAssertEqualObjects([query valuesForParameter:kTestParameterName].firstObject, - kTestParameterValue); + kTestParameterValue, @""); [query addParameter:kTestParameterName value:kTestParameterValue2]; NSArray *values = [query valuesForParameter:kTestParameterName]; - XCTAssertNotNil(values); - XCTAssertEqual(values.count, 2); - XCTAssertEqualObjects(values.firstObject, kTestParameterValue); - XCTAssertEqualObjects(values[1], kTestParameterValue2); + XCTAssertNotNil(values, @""); + XCTAssertEqual(values.count, 2, @""); + XCTAssertEqualObjects(values.firstObject, kTestParameterValue, @""); + XCTAssertEqualObjects(values[1], kTestParameterValue2, @""); } /* @brief Tests the application/x-www-form-urlencoded encoding. @@ -100,34 +105,34 @@ - (void)testURLEncodedParameters { // Tests the URLEncodedParameters method NSString *encodedParams = [query URLEncodedParameters]; NSString *expected = [NSString stringWithFormat:@"%@=%@", kTestParameterName, kEncodingTestEncoded]; - XCTAssertEqualObjects(encodedParams, expected); + XCTAssertEqualObjects(encodedParams, expected, @""); // Tests that params are correctly encoded when using URLByReplacingQueryInURL NSURL *url = [query URLByReplacingQueryInURL:baseURL]; NSString* expectedURL = [NSString stringWithFormat:@"%@?%@", kTestURLRoot, expected]; - XCTAssertEqualObjects([url absoluteString], expectedURL); + XCTAssertEqualObjects([url absoluteString], expectedURL, @""); } - (void)testAddingThreeParameters { OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init]; [query addParameter:kTestParameterName value:kTestParameterValue]; XCTAssertEqualObjects([query valuesForParameter:kTestParameterName].firstObject, - kTestParameterValue); + kTestParameterValue, @""); [query addParameter:kTestParameterName value:kTestParameterValue2]; [query addParameter:kTestParameterName value:kTestParameterValue]; NSArray *values = [query valuesForParameter:kTestParameterName]; - XCTAssertNotNil(values); - XCTAssertEqual(values.count, 3); - XCTAssertEqualObjects(values.firstObject, kTestParameterValue); - XCTAssertEqualObjects(values[1], kTestParameterValue2); - XCTAssertEqualObjects(values[2], kTestParameterValue); + XCTAssertNotNil(values, @""); + XCTAssertEqual(values.count, 3, @""); + XCTAssertEqualObjects(values.firstObject, kTestParameterValue, @""); + XCTAssertEqualObjects(values[1], kTestParameterValue2, @""); + XCTAssertEqualObjects(values[2], kTestParameterValue, @""); NSDictionary *> *parametersAsDictionary = @{ kTestParameterName : @[ kTestParameterValue, kTestParameterValue2, kTestParameterValue ] }; - XCTAssertEqualObjects(query.dictionaryValue, parametersAsDictionary); + XCTAssertEqualObjects(query.dictionaryValue, parametersAsDictionary, @""); } - (void)testBuildingParameterStringWithSimpleParameters { @@ -143,12 +148,13 @@ - (void)testBuildingParameterStringWithSimpleParameters { NSURL *rootURLWithParameters = [query URLByReplacingQueryInURL:rootURL]; XCTAssert([rootURLWithParameters.query isEqualToString:kTestSimpleParameterStringEncoded] - || [rootURLWithParameters.query isEqualToString:kTestSimpleParameterStringEncodedRev]); + || [rootURLWithParameters.query isEqualToString:kTestSimpleParameterStringEncodedRev], + @""); OIDURLQueryComponent *parsedParameters = [[OIDURLQueryComponent alloc] initWithURL:rootURLWithParameters]; - XCTAssertEqualObjects(parsedParameters.dictionaryValue, parameters); + XCTAssertEqualObjects(parsedParameters.dictionaryValue, parameters, @""); } - (void)testParsingQueryString { @@ -163,7 +169,9 @@ - (void)testParsingQueryString { kTestParameterName2 : kTestParameterValue2 }; - XCTAssertEqualObjects(query.dictionaryValue, parameters); + XCTAssertEqualObjects(query.dictionaryValue, parameters, @""); } @end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDURLQueryComponentTestsIOS7.m b/UnitTests/OIDURLQueryComponentTestsIOS7.m index 5f4d8ae4e..707ba5fbb 100644 --- a/UnitTests/OIDURLQueryComponentTestsIOS7.m +++ b/UnitTests/OIDURLQueryComponentTestsIOS7.m @@ -20,6 +20,11 @@ #import "Source/OIDURLQueryComponent.h" +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + @interface OIDURLQueryComponentTestsIOS7 : OIDURLQueryComponentTests @end @@ -36,3 +41,5 @@ - (void)tearDown { } @end + +#pragma GCC diagnostic pop From c2d381e8fb692ed4193534b52ded46448d54ce23 Mon Sep 17 00:00:00 2001 From: Steven E Wright Date: Mon, 18 Jun 2018 09:44:54 -0700 Subject: [PATCH 021/204] Create issue templates for bugs and feature requests. (#253) The only change to the defaults is a question about iOS 12 (since we've gotten 4 or so issue requests about it by now). --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..a598b7fbf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +**Are you filing an issue about iOS 12?** +You can find preliminary support for iOS 12 in the (ios12beta branch)[https://github.com/openid/AppAuth-iOS/tree/ios12beta] and background information in (the associated pull request)[https://github.com/openid/AppAuth-iOS/pull/246]. Please review these materials before filing iOS 12 issues. Thanks. + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..5ad6b46bf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Are you filing an issue about iOS 12?** +You can find preliminary support for iOS 12 in the (ios12beta branch)[https://github.com/openid/AppAuth-iOS/tree/ios12beta] and background information in (the associated pull request)[https://github.com/openid/AppAuth-iOS/pull/246]. Please review these materials before filing iOS 12 issues. Thanks. + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From a19bfb6ac307145001b804ede49bc9ca5706b920 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 7 Jun 2018 15:47:08 -0700 Subject: [PATCH 022/204] Add support for 4xx errors 401 is expected per RFC 6749 Section 5.2 for the invalid_client code. Extensions to OAuth may use other error codes (presumably still in the 4xx range). --- Source/OIDAuthorizationService.m | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 1cb65c0b4..23e57fa61 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -311,16 +311,14 @@ + (void)performTokenRequest:(OIDTokenRequest *)request NSError *serverError = [OIDErrorUtilities HTTPErrorWithHTTPResponse:HTTPURLResponse data:data]; - // HTTP 400 may indicate an RFC6749 Section 5.2 error response. - // HTTP 429 may occur during polling for device-flow requests for the slow_down error - // https://tools.ietf.org/html/draft-ietf-oauth-device-flow-03#section-3.5 - if (statusCode == 400 || statusCode == 429) { + // HTTP 4xx may indicate an RFC6749 Section 5.2 error response, attempts to parse as such. + if (statusCode >= 400 && statusCode < 500) { NSError *jsonDeserializationError; NSDictionary *> *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 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 From a869c4eac793f4f26932a7cac568f7d902fadd01 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Tue, 26 Jun 2018 12:33:31 -0700 Subject: [PATCH 023/204] Suppress warnings related to our own deprecations --- Source/iOS/OIDAuthState+IOS.h | 5 +++++ Source/iOS/OIDAuthorizationService+IOS.h | 5 +++++ Source/macOS/OIDAuthState+Mac.h | 6 ++++++ Source/macOS/OIDAuthorizationService+Mac.h | 6 ++++++ Source/macOS/OIDRedirectHTTPHandler.h | 5 +++++ 5 files changed, 27 insertions(+) diff --git a/Source/iOS/OIDAuthState+IOS.h b/Source/iOS/OIDAuthState+IOS.h index 3ebc9b028..5b8891c7f 100644 --- a/Source/iOS/OIDAuthState+IOS.h +++ b/Source/iOS/OIDAuthState+IOS.h @@ -26,6 +26,9 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDAuthState (IOS) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + /*! @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. @param authorizationRequest The authorization request to present. @@ -41,6 +44,8 @@ NS_ASSUME_NONNULL_BEGIN presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthStateAuthorizationCallback)callback; +#pragma GCC diagnostic pop + @end NS_ASSUME_NONNULL_END diff --git a/Source/iOS/OIDAuthorizationService+IOS.h b/Source/iOS/OIDAuthorizationService+IOS.h index 162b20311..b41729054 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.h +++ b/Source/iOS/OIDAuthorizationService+IOS.h @@ -27,6 +27,9 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDAuthorizationService (IOS) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + /*! @brief Perform an authorization flow using \SFSafariViewController. @param request The authorization request. @param presentingViewController The view controller from which to present the @@ -42,4 +45,6 @@ NS_ASSUME_NONNULL_BEGIN callback:(OIDAuthorizationCallback)callback; @end +#pragma GCC diagnostic pop + NS_ASSUME_NONNULL_END diff --git a/Source/macOS/OIDAuthState+Mac.h b/Source/macOS/OIDAuthState+Mac.h index 7de9f7324..4e94f1f20 100644 --- a/Source/macOS/OIDAuthState+Mac.h +++ b/Source/macOS/OIDAuthState+Mac.h @@ -24,6 +24,9 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDAuthState (Mac) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + /*! @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. @param authorizationRequest The authorization request to present. @@ -35,6 +38,9 @@ NS_ASSUME_NONNULL_BEGIN + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest callback:(OIDAuthStateAuthorizationCallback)callback; + +#pragma GCC diagnostic pop + @end NS_ASSUME_NONNULL_END diff --git a/Source/macOS/OIDAuthorizationService+Mac.h b/Source/macOS/OIDAuthorizationService+Mac.h index 9ef0c76ee..95231e0e9 100644 --- a/Source/macOS/OIDAuthorizationService+Mac.h +++ b/Source/macOS/OIDAuthorizationService+Mac.h @@ -24,6 +24,9 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDAuthorizationService (Mac) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + /*! @brief Perform an authorization flow using the default browser. @param request The authorization request. @param callback The method called when the request has completed or failed. @@ -34,6 +37,9 @@ NS_ASSUME_NONNULL_BEGIN + (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request callback:(OIDAuthorizationCallback)callback; + +#pragma GCC diagnostic pop + @end NS_ASSUME_NONNULL_END diff --git a/Source/macOS/OIDRedirectHTTPHandler.h b/Source/macOS/OIDRedirectHTTPHandler.h index 25fd6b4c8..bd1e977c5 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.h +++ b/Source/macOS/OIDRedirectHTTPHandler.h @@ -24,6 +24,9 @@ NS_ASSUME_NONNULL_BEGIN @protocol OIDAuthorizationFlowSession; @protocol OIDExternalUserAgentSession; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + /*! @brief Start a HTTP server on the loopback interface (i.e. @c 127.0.0.1) to receive the OAuth response redirects on macOS. */ @@ -44,6 +47,8 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, strong, nullable) id currentAuthorizationFlow; +#pragma GCC diagnostic pop + /*! @brief Creates an a loopback HTTP redirect URI handler with the given success URL. @param successURL The URL that the user is redirected to after the external user-agent request flow completes either with a result of success or error. The contents of this page should instruct the user From 8e4d27c88e88f502163ad4c8c640dc643aaf29a9 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Tue, 26 Jun 2018 12:33:47 -0700 Subject: [PATCH 024/204] Version bump -> 0.93.0 --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 1cc66b91d..43441e937 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "0.92.0" + s.version = "0.93.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC From 1dae3a1df4de33b844284dce545c71ff0d3582ad Mon Sep 17 00:00:00 2001 From: Jarrod Moldrich Date: Thu, 28 Jun 2018 23:29:34 +1000 Subject: [PATCH 025/204] Expose OIDURLSessionProvider to framework (#257) --- AppAuth.xcodeproj/project.pbxproj | 22 +++++++++++++++------- Source/Framework/AppAuth.h | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index c71b5097d..cae0dfed5 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -358,13 +358,6 @@ 347424101E7F4BA000D3E6D6 /* OIDTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D41C5D8243000EF209 /* OIDTokenResponse.m */; }; 347424111E7F4BA000D3E6D6 /* OIDTokenUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D61C5D8243000EF209 /* OIDTokenUtilities.m */; }; 347424121E7F4BA000D3E6D6 /* OIDURLQueryComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D81C5D8243000EF209 /* OIDURLQueryComponent.m */; }; - 34AF73671FB4E4B00022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; - 34AF73681FB4E4B10022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; - 34AF73691FB4E4B20022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; - 34AF736A1FB4E4B30022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; - 34AF736B1FB4E4B30022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; - 34AF736C1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; - 34AF736D1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; 34A663291E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34A6632A1E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34A6632B1E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -383,9 +376,20 @@ 34A6638E1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; 34A6638F1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; 34A663901E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 34AF73671FB4E4B00022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; + 34AF73681FB4E4B10022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; + 34AF73691FB4E4B20022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; + 34AF736A1FB4E4B30022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; + 34AF736B1FB4E4B30022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; + 34AF736C1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; + 34AF736D1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; 34D5EC451E6D1AD900814354 /* OIDSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */; }; 34FEA6AE1DB6E083005C9212 /* OIDLoopbackHTTPServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */; }; 34FEA6AF1DB6E083005C9212 /* OIDLoopbackHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FEA6AD1DB6E083005C9212 /* OIDLoopbackHTTPServer.m */; }; + 55A094CF20DFBB10000045D1 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 55A094D020DFBB11000045D1 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 55A094D120DFBB12000045D1 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 55A094D220DFBB12000045D1 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; 60140F7A1DE4276800DA0DC3 /* OIDClientMetadataParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F791DE4276800DA0DC3 /* OIDClientMetadataParameters.m */; }; 60140F7C1DE42E1000DA0DC3 /* OIDRegistrationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7B1DE42E1000DA0DC3 /* OIDRegistrationRequest.m */; }; 60140F801DE4344200DA0DC3 /* OIDRegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */; }; @@ -935,6 +939,7 @@ 343AAAF81E83499000F9D36E /* OIDTokenResponse.h in Headers */, 343AAAF61E83499000F9D36E /* OIDServiceDiscovery.h in Headers */, 343AAAF11E83499000F9D36E /* OIDGrantTypes.h in Headers */, + 55A094CF20DFBB10000045D1 /* OIDURLSessionProvider.h in Headers */, 343AAA6D1E83466B00F9D36E /* OIDAuthState+IOS.h in Headers */, 343AAAEF1E83499000F9D36E /* OIDRegistrationResponse.h in Headers */, A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */, @@ -979,6 +984,7 @@ 343AAB101E83499100F9D36E /* OIDTokenResponse.h in Headers */, 343AAAFC1E83499100F9D36E /* OIDAuthorizationResponse.h in Headers */, 343AAB0C1E83499100F9D36E /* OIDScopeUtilities.h in Headers */, + 55A094D020DFBB11000045D1 /* OIDURLSessionProvider.h in Headers */, A6339DAB20321AE50043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, 343AAB011E83499100F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAAFB1E83499100F9D36E /* OIDAuthorizationRequest.h in Headers */, @@ -1011,6 +1017,7 @@ 343AAB281E83499200F9D36E /* OIDTokenResponse.h in Headers */, 343AAB141E83499200F9D36E /* OIDAuthorizationResponse.h in Headers */, 343AAB241E83499200F9D36E /* OIDScopeUtilities.h in Headers */, + 55A094D120DFBB12000045D1 /* OIDURLSessionProvider.h in Headers */, A6339DAC20321AE70043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, 343AAB191E83499200F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAB131E83499200F9D36E /* OIDAuthorizationRequest.h in Headers */, @@ -1029,6 +1036,7 @@ 343AAADD1E83494400F9D36E /* OIDRedirectHTTPHandler.h in Headers */, 343AAB3C1E83499200F9D36E /* OIDScopeUtilities.h in Headers */, 343AAB3F1E83499200F9D36E /* OIDTokenRequest.h in Headers */, + 55A094D220DFBB12000045D1 /* OIDURLSessionProvider.h in Headers */, 343AAB411E83499200F9D36E /* OIDTokenUtilities.h in Headers */, A6DEABA32018E4B70022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAB371E83499200F9D36E /* OIDRegistrationResponse.h in Headers */, diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index 37ece0202..7cba488ca 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -48,6 +48,7 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import +#import #if TARGET_OS_TV #elif TARGET_OS_WATCH From a79e572fd952e679055f680522310a54408c6a27 Mon Sep 17 00:00:00 2001 From: Craig Lane Date: Thu, 28 Jun 2018 21:48:13 -0500 Subject: [PATCH 026/204] Redact auth tokens in description methods Resolves #77 --- AppAuth.xcodeproj/project.pbxproj | 14 ++++++++++ Source/OIDAuthState.m | 7 +++-- Source/OIDAuthorizationResponse.m | 5 ++-- Source/OIDRegistrationResponse.m | 5 ++-- Source/OIDTokenResponse.m | 7 +++-- Source/OIDTokenUtilities.h | 6 ++++ Source/OIDTokenUtilities.m | 15 ++++++++++ UnitTests/OIDTokenUtilitiesTests.m | 44 ++++++++++++++++++++++++++++++ 8 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 UnitTests/OIDTokenUtilitiesTests.m diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index cae0dfed5..baf27bf61 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -395,6 +395,12 @@ 60140F801DE4344200DA0DC3 /* OIDRegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */; }; 60140F831DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F821DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m */; }; 60140F861DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */; }; + A5EEF29720D821120044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; + A5EEF29820D8211A0044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; + A5EEF29920D8211B0044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; + A5EEF29A20D821960044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; + A5EEF29B20D821970044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; + A5EEF29C20D821970044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; A6339DAA20321ADD0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6339DAB20321AE50043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6339DAC20321AE70043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -600,6 +606,7 @@ 60140F821DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRegistrationRequestTests.m; sourceTree = ""; }; 60140F841DE43C8C00DA0DC3 /* OIDRegistrationResponseTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRegistrationResponseTests.h; sourceTree = ""; }; 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRegistrationResponseTests.m; sourceTree = ""; }; + A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDTokenUtilitiesTests.m; sourceTree = ""; }; A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDAuthorizationFlowSession.h; sourceTree = ""; }; A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgent.h; sourceTree = ""; }; A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentSession.h; sourceTree = ""; }; @@ -853,6 +860,7 @@ 3417420E1C5D82D3000EF209 /* OIDTokenRequestTests.m */, 3417420F1C5D82D3000EF209 /* OIDTokenResponseTests.h */, 341742101C5D82D3000EF209 /* OIDTokenResponseTests.m */, + A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */, 341742111C5D82D3000EF209 /* OIDURLQueryComponentTests.h */, 341742121C5D82D3000EF209 /* OIDURLQueryComponentTests.m */, 341742131C5D82D3000EF209 /* OIDURLQueryComponentTestsIOS7.m */, @@ -1567,6 +1575,7 @@ 3417421A1C5D82D3000EF209 /* OIDGrantTypesTests.m in Sources */, 3417421B1C5D82D3000EF209 /* OIDResponseTypesTests.m in Sources */, 60140F831DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m in Sources */, + A5EEF29A20D821960044F470 /* OIDTokenUtilitiesTests.m in Sources */, 60140F861DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m in Sources */, 341742191C5D82D3000EF209 /* OIDAuthStateTests.m in Sources */, 3417421D1C5D82D3000EF209 /* OIDServiceConfigurationTests.m in Sources */, @@ -1579,6 +1588,7 @@ buildActionMask = 2147483647; files = ( 341AA50A1E7F3A9B00FCA5C6 /* OIDScopesTests.m in Sources */, + A5EEF29B20D821970044F470 /* OIDTokenUtilitiesTests.m in Sources */, 341AA50F1E7F3A9B00FCA5C6 /* OIDURLQueryComponentTests.m in Sources */, 341AA50B1E7F3A9B00FCA5C6 /* OIDServiceConfigurationTests.m in Sources */, 341AA50C1E7F3A9B00FCA5C6 /* OIDServiceDiscoveryTests.m in Sources */, @@ -1600,6 +1610,7 @@ buildActionMask = 2147483647; files = ( 341AA4FD1E7F3A9400FCA5C6 /* OIDScopesTests.m in Sources */, + A5EEF29C20D821970044F470 /* OIDTokenUtilitiesTests.m in Sources */, 341AA5021E7F3A9400FCA5C6 /* OIDURLQueryComponentTests.m in Sources */, 341AA4FE1E7F3A9400FCA5C6 /* OIDServiceConfigurationTests.m in Sources */, 341AA4FF1E7F3A9400FCA5C6 /* OIDServiceDiscoveryTests.m in Sources */, @@ -1697,6 +1708,7 @@ 343AAA761E8346B400F9D36E /* OIDGrantTypesTests.m in Sources */, 34A6638E1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 343AAA741E8346B400F9D36E /* OIDAuthorizationResponseTests.m in Sources */, + A5EEF29720D821120044F470 /* OIDTokenUtilitiesTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1777,6 +1789,7 @@ 343AAB821E8349CE00F9D36E /* OIDGrantTypesTests.m in Sources */, 34A6638F1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 343AAB801E8349CE00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, + A5EEF29820D8211A0044F470 /* OIDTokenUtilitiesTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1833,6 +1846,7 @@ 343AAB901E8349CF00F9D36E /* OIDGrantTypesTests.m in Sources */, 34A663901E8865090060B664 /* OIDRPProfileCode.m in Sources */, 343AAB8E1E8349CF00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, + A5EEF29920D8211B0044F470 /* OIDTokenUtilitiesTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index 0c9e1c091..171b419d5 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -29,6 +29,7 @@ #import "OIDRegistrationResponse.h" #import "OIDTokenRequest.h" #import "OIDTokenResponse.h" +#import "OIDTokenUtilities.h" /*! @brief Key used to encode the @c refreshToken property for @c NSSecureCoding. */ @@ -218,11 +219,11 @@ - (NSString *)description { NSStringFromClass([self class]), (void *)self, (self.isAuthorized) ? @"YES" : @"NO", - _refreshToken, + [OIDTokenUtilities redact:_refreshToken], _scope, - self.accessToken, + [OIDTokenUtilities redact:self.accessToken], self.accessTokenExpirationDate, - self.idToken, + [OIDTokenUtilities redact:self.idToken], _lastAuthorizationResponse, _lastTokenResponse, _lastRegistrationResponse, diff --git a/Source/OIDAuthorizationResponse.m b/Source/OIDAuthorizationResponse.m index fb66f4986..3ba07502e 100644 --- a/Source/OIDAuthorizationResponse.m +++ b/Source/OIDAuthorizationResponse.m @@ -23,6 +23,7 @@ #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. @@ -181,10 +182,10 @@ - (NSString *)description { (void *)self, _authorizationCode, _state, - _accessToken, + [OIDTokenUtilities redact:_accessToken], _accessTokenExpirationDate, _tokenType, - _idToken, + [OIDTokenUtilities redact:_idToken], _scope, _additionalParameters, _request]; diff --git a/Source/OIDRegistrationResponse.m b/Source/OIDRegistrationResponse.m index 71dc91005..c9aca6971 100644 --- a/Source/OIDRegistrationResponse.m +++ b/Source/OIDRegistrationResponse.m @@ -22,6 +22,7 @@ #import "OIDDefines.h" #import "OIDFieldMapping.h" #import "OIDRegistrationRequest.h" +#import "OIDTokenUtilities.h" NSString *const OIDClientIDParam = @"client_id"; NSString *const OIDClientIDIssuedAtParam = @"client_id_issued_at"; @@ -162,9 +163,9 @@ - (NSString *)description { (void *)self, _clientID, _clientIDIssuedAt, - _clientSecret, + [OIDTokenUtilities redact:_clientSecret], _clientSecretExpiresAt, - _registrationAccessToken, + [OIDTokenUtilities redact:_registrationAccessToken], _registrationClientURI, _additionalParameters, _request]; diff --git a/Source/OIDTokenResponse.m b/Source/OIDTokenResponse.m index 6db60bb73..1e4eeef91 100644 --- a/Source/OIDTokenResponse.m +++ b/Source/OIDTokenResponse.m @@ -21,6 +21,7 @@ #import "OIDDefines.h" #import "OIDFieldMapping.h" #import "OIDTokenRequest.h" +#import "OIDTokenUtilities.h" /*! @brief Key used to encode the @c request property for @c NSSecureCoding */ @@ -156,11 +157,11 @@ - (NSString *)description { "scope: \"%@\", additionalParameters: %@, request: %@>", NSStringFromClass([self class]), (void *)self, - _accessToken, + [OIDTokenUtilities redact:_accessToken], _accessTokenExpirationDate, _tokenType, - _idToken, - _refreshToken, + [OIDTokenUtilities redact:_idToken], + [OIDTokenUtilities redact:_refreshToken], _scope, _additionalParameters, _request]; diff --git a/Source/OIDTokenUtilities.h b/Source/OIDTokenUtilities.h index f13d937b0..598be2aed 100644 --- a/Source/OIDTokenUtilities.h +++ b/Source/OIDTokenUtilities.h @@ -50,6 +50,12 @@ NS_ASSUME_NONNULL_BEGIN */ + (NSData *)sha265:(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; + @end NS_ASSUME_NONNULL_END diff --git a/Source/OIDTokenUtilities.m b/Source/OIDTokenUtilities.m index fa08ddcb5..cd42bfd7f 100644 --- a/Source/OIDTokenUtilities.m +++ b/Source/OIDTokenUtilities.m @@ -48,4 +48,19 @@ + (NSData *)sha265:(NSString *)inputString { 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]"]; + } +} + @end diff --git a/UnitTests/OIDTokenUtilitiesTests.m b/UnitTests/OIDTokenUtilitiesTests.m new file mode 100644 index 000000000..689ea00cf --- /dev/null +++ b/UnitTests/OIDTokenUtilitiesTests.m @@ -0,0 +1,44 @@ +/*! @file OIDTokenUtilities.m + @brief AppAuth iOS SDK + @copyright + Copyright 2018 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 + +#import "Source/OIDTokenUtilities.h" + + +@interface OIDTokenUtilitiesTests : XCTestCase +@end +@implementation OIDTokenUtilitiesTests + +- (void)testRedact { + XCTAssertEqualObjects([OIDTokenUtilities redact:@"0123456789"], @"012345...[redacted]", @""); +} + +- (void)testRedactWithNilParamater { + XCTAssertEqualObjects([OIDTokenUtilities redact:nil], nil, @""); +} + +- (void)testRedactWithEmptyString { + XCTAssertEqualObjects([OIDTokenUtilities redact:@""], @"", @""); +} + +- (void)testRedactWithShortInput { + XCTAssertEqualObjects([OIDTokenUtilities redact:@"01234"], @"[redacted]", @""); +} + +@end From b9d3ce7f02d817beea002241c3de45dbe9504d16 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 28 Jun 2018 22:12:51 -0700 Subject: [PATCH 027/204] Add a description to errors, even if they have an underlyingError with a description --- Source/OIDAuthorizationService.m | 69 +++++++++++++++++++++------- Source/OIDServiceDiscovery.m | 2 +- Source/iOS/OIDExternalUserAgentIOS.m | 4 +- 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 23e57fa61..5155c69a9 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -88,10 +88,9 @@ - (void)presentAuthorizationWithExternalUserAgent:(id)exte - (void)cancel { [_externalUserAgent dismissExternalUserAgentAnimated:YES completion:^{ - NSError *error = [OIDErrorUtilities - errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow - underlyingError:nil - description:nil]; + NSError *error = [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow + underlyingError:nil + description:@"Authorization flow was cancelled."]; [self didFinishWithResponse:nil error:error]; }]; } @@ -209,9 +208,13 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)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:error.localizedDescription]; + description:errorDescription]; dispatch_async(dispatch_get_main_queue(), ^{ completion(nil, error); }); @@ -225,9 +228,14 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL 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:nil]; + description:errorDescription]; dispatch_async(dispatch_get_main_queue(), ^{ completion(nil, error); }); @@ -238,9 +246,13 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL 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:nil]; + description:errorDescription]; dispatch_async(dispatch_get_main_queue(), ^{ completion(nil, error); }); @@ -294,10 +306,14 @@ + (void)performTokenRequest:(OIDTokenRequest *)request 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:nil]; + description:errorDescription]; dispatch_async(dispatch_get_main_queue(), ^{ callback(nil, returnedError); }); @@ -331,11 +347,15 @@ + (void)performTokenRequest:(OIDTokenRequest *)request } } - // not an OAuth error, just a generic server error + // 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:nil]; + description:errorDescription]; dispatch_async(dispatch_get_main_queue(), ^{ callback(nil, returnedError); }); @@ -347,10 +367,13 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [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:nil]; + description:errorDescription]; dispatch_async(dispatch_get_main_queue(), ^{ callback(nil, returnedError); }); @@ -364,7 +387,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request NSError *returnedError = [OIDErrorUtilities errorWithCode:OIDErrorCodeTokenResponseConstructionError underlyingError:jsonDeserializationError - description:nil]; + description:@"Token response invalid."]; dispatch_async(dispatch_get_main_queue(), ^{ callback(nil, returnedError); }); @@ -520,9 +543,13 @@ + (void)performRegistrationRequest:(OIDRegistrationRequest *)request 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:nil]; + description:errorDescription]; dispatch_async(dispatch_get_main_queue(), ^{ completion(nil, returnedError); }); @@ -558,9 +585,14 @@ + (void)performRegistrationRequest:(OIDRegistrationRequest *)request } // 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:nil]; + description:errorDescription]; dispatch_async(dispatch_get_main_queue(), ^{ completion(nil, returnedError); }); @@ -572,9 +604,12 @@ + (void)performRegistrationRequest:(OIDRegistrationRequest *)request [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:nil]; + description:errorDescription]; dispatch_async(dispatch_get_main_queue(), ^{ completion(nil, returnedError); }); @@ -588,8 +623,8 @@ + (void)performRegistrationRequest:(OIDRegistrationRequest *)request // A problem occurred constructing the registration response from the JSON. NSError *returnedError = [OIDErrorUtilities errorWithCode:OIDErrorCodeRegistrationResponseConstructionError - underlyingError:jsonDeserializationError - description:nil]; + underlyingError:nil + description:@"Registration response invalid."]; dispatch_async(dispatch_get_main_queue(), ^{ completion(nil, returnedError); }); diff --git a/Source/OIDServiceDiscovery.m b/Source/OIDServiceDiscovery.m index da0fe7711..195c072e1 100644 --- a/Source/OIDServiceDiscovery.m +++ b/Source/OIDServiceDiscovery.m @@ -88,7 +88,7 @@ - (nullable instancetype)initWithJSONData:(NSData *)serviceDiscoveryJSONData if (!json || jsonError) { *error = [OIDErrorUtilities errorWithCode:OIDErrorCodeJSONDeserializationError underlyingError:jsonError - description:nil]; + description:jsonError.localizedDescription]; return nil; } return [self initWithDictionary:json error:error]; diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index 38af4025b..0a88cb64a 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -106,7 +106,7 @@ - (BOOL)presentExternalUserAgentRequest:(id)request NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow underlyingError:error - description:nil]; + description:@"User cancelled."]; [strongSelf->_session failExternalUserAgentFlowWithError:safariError]; } }]; @@ -185,7 +185,7 @@ - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller NS_AV [self cleanUp]; NSError *error = [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow underlyingError:nil - description:nil]; + description:@"No external user agent flow in progress."]; [session failExternalUserAgentFlowWithError:error]; } From d6ff5a162c40b05dfc28877ce46d20adee7d97e5 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 6 Jul 2018 16:32:48 -0700 Subject: [PATCH 028/204] Increase ID Token acceptable `iat` clock skew to +/- 10min --- Source/OIDAuthorizationService.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 5155c69a9..6af3dfb50 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -473,9 +473,9 @@ + (void)performTokenRequest:(OIDTokenRequest *)request } // OpenID Connect Core Section 3.1.3.7. rule #10 - // Validates that the issued at time is not more than +/- 5 minutes on the current time. + // Validates that the issued at time is not more than +/- 10 minutes on the current time. NSTimeInterval issuedAtDifference = [idToken.issuedAt timeIntervalSinceNow]; - if (fabs(issuedAtDifference) > 300) { + if (fabs(issuedAtDifference) > 600) { NSError *invalidIDToken = [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil From 67716e00f6edb1aefb4927ca41e7f0812f80339d Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Wed, 11 Jul 2018 14:58:02 -0400 Subject: [PATCH 029/204] form-urlencode client ID and client secret in Authorization header As required by https://tools.ietf.org/html/rfc6749#section-2.3.1, encodes client ID & secret. Uses HTML5 specification (https://www.w3.org/TR/html5/sec-forms.html#application-x-www-form-urlencoded-encoding-algorithm) application/x-www-form-urlencoded encoding rules (https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer). Resolves #233 client_secret_basic header value invalid when special characters are used --- Source/OIDTokenRequest.m | 10 +++++++++- Source/OIDTokenUtilities.h | 6 ++++++ Source/OIDTokenUtilities.m | 23 +++++++++++++++++++++++ UnitTests/OIDTokenUtilitiesTests.m | 9 +++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Source/OIDTokenRequest.m b/Source/OIDTokenRequest.m index eeb210ba8..02eb5104a 100644 --- a/Source/OIDTokenRequest.m +++ b/Source/OIDTokenRequest.m @@ -22,6 +22,7 @@ #import "OIDScopeUtilities.h" #import "OIDServiceConfiguration.h" #import "OIDURLQueryComponent.h" +#import "OIDTokenUtilities.h" /*! @brief The key for the @c configuration property for @c NSSecureCoding */ @@ -274,7 +275,14 @@ - (NSURLRequest *)URLRequest { NSMutableDictionary *httpHeaders = [[NSMutableDictionary alloc] init]; if (_clientSecret) { - NSString *credentials = [NSString stringWithFormat:@"%@:%@", _clientID, _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]; diff --git a/Source/OIDTokenUtilities.h b/Source/OIDTokenUtilities.h index 598be2aed..cc54f6888 100644 --- a/Source/OIDTokenUtilities.h +++ b/Source/OIDTokenUtilities.h @@ -56,6 +56,12 @@ NS_ASSUME_NONNULL_BEGIN */ + (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 diff --git a/Source/OIDTokenUtilities.m b/Source/OIDTokenUtilities.m index cd42bfd7f..3ca46a5ee 100644 --- a/Source/OIDTokenUtilities.m +++ b/Source/OIDTokenUtilities.m @@ -20,6 +20,12 @@ #import +/*! @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 { @@ -63,4 +69,21 @@ + (NSString *)redact:(NSString *)inputString { } } ++ (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 diff --git a/UnitTests/OIDTokenUtilitiesTests.m b/UnitTests/OIDTokenUtilitiesTests.m index 689ea00cf..e4da624de 100644 --- a/UnitTests/OIDTokenUtilitiesTests.m +++ b/UnitTests/OIDTokenUtilitiesTests.m @@ -41,4 +41,13 @@ - (void)testRedactWithShortInput { XCTAssertEqualObjects([OIDTokenUtilities redact:@"01234"], @"[redacted]", @""); } +- (void)testFormUrlEncode { + XCTAssertEqualObjects([OIDTokenUtilities formUrlEncode:@"t _9V-F*I+Z1Lk.u7:2/8L+w="], + @"t+_9V-F*I%2BZ1Lk.u7%3A2%2F8L%2Bw%3D", @""); +} + +- (void)testFormUrlEncodeEmptyString { + XCTAssertEqualObjects([OIDTokenUtilities formUrlEncode:@""], @"", @""); +} + @end From 66a5a12bad373f9bff7d9718b6cf878e6cc7883d Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 11 Jul 2018 13:32:38 -0700 Subject: [PATCH 030/204] Add request trace logging enabled via the preprocessor define _APPAUTHTRACE Useful when debugging IdP issues. --- Source/OIDAuthorizationService.m | 15 +++++++++++++++ Source/OIDDefines.h | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 6af3dfb50..7381a90bc 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -112,6 +112,9 @@ - (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL { if (![self shouldHandleURL:URL]) { return NO; } + + AppAuthRequestTrace(@"Authorization Response: %@", URL); + // checks for an invalid state if (!_pendingauthorizationFlowCallback) { [NSException raise:OIDOAuthExceptionInvalidAuthorizationFlow @@ -278,6 +281,9 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL presentAuthorizationRequest:(OIDAuthorizationRequest *)request externalUserAgent:(id)externalUserAgent callback:(OIDAuthorizationCallback)callback { + + AppAuthRequestTrace(@"Authorization Request: %@", request); + OIDAuthorizationFlowSessionImplementation *flowSession = [[OIDAuthorizationFlowSessionImplementation alloc] initWithRequest:request]; [flowSession presentAuthorizationWithExternalUserAgent:externalUserAgent callback:callback]; @@ -299,6 +305,12 @@ + (void)performTokenRequest:(OIDTokenRequest *)request callback:(OIDTokenCallback)callback { NSURLRequest *URLRequest = [request URLRequest]; + + AppAuthRequestTrace(@"Token Request: %@\nHTTPBody: %@", + URLRequest.URL, + [[NSString alloc] initWithData:URLRequest.HTTPBody + encoding:NSUTF8StringEncoding]); + NSURLSession *session = [OIDURLSessionProvider session]; [[session dataTaskWithRequest:URLRequest completionHandler:^(NSData *_Nullable data, @@ -322,6 +334,9 @@ + (void)performTokenRequest:(OIDTokenRequest *)request 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 = diff --git a/Source/OIDDefines.h b/Source/OIDDefines.h index 0dccf842a..8ff4f19ba 100644 --- a/Source/OIDDefines.h +++ b/Source/OIDDefines.h @@ -42,3 +42,10 @@ reason:reason \ userInfo:nil]; \ } + +#ifdef _APPAUTHTRACE +# define AppAuthRequestTrace(fmt, ...) NSLog(fmt, ##__VA_ARGS__); +#else // _APPAUTHTRACE +# define AppAuthRequestTrace(...) +#endif // _APPAUTHTRACE + From b8a98da8dcd72882964c84ae2ac0d1063c6ec70e Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 7 Mar 2018 16:53:56 -0800 Subject: [PATCH 031/204] Remove the redirect_uri parameter from token refresh On close inspection of RFC 6749 Section 6, the refresh_uri isn't required and may cause issues on servers that are not expecting it. RedirectURL is now nullable on the TokenRequest. To avoid inconsistencies, added a runtime check for nonnull values for supported grant types that require it. --- Source/OIDAuthState.m | 2 +- Source/OIDError.h | 5 +++++ Source/OIDError.m | 3 +++ Source/OIDTokenRequest.h | 6 +++--- Source/OIDTokenRequest.m | 15 +++++++++++++-- UnitTests/OIDTokenRequestTests.m | 20 +++++++++++++++++++- 6 files changed, 44 insertions(+), 7 deletions(-) diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index 171b419d5..8b141a667 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -411,7 +411,7 @@ - (OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: initWithConfiguration:_lastAuthorizationResponse.request.configuration grantType:OIDGrantTypeRefreshToken authorizationCode:nil - redirectURL:_lastAuthorizationResponse.request.redirectURL + redirectURL:nil clientID:_lastAuthorizationResponse.request.clientID clientSecret:_lastAuthorizationResponse.request.clientSecret scope:_lastAuthorizationResponse.request.scope diff --git a/Source/OIDError.h b/Source/OIDError.h index 85b71e267..e48813936 100644 --- a/Source/OIDError.h +++ b/Source/OIDError.h @@ -385,4 +385,9 @@ typedef NS_ENUM(NSInteger, OIDErrorCodeOAuthRegistration) { */ 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 diff --git a/Source/OIDError.m b/Source/OIDError.m index 697e63ac9..2d23d84bf 100644 --- a/Source/OIDError.m +++ b/Source/OIDError.m @@ -33,6 +33,9 @@ NSString *const OIDOAuthExceptionInvalidAuthorizationFlow = @"An OAuth redirect was sent to a " "OIDAuthorizationFlowSession 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"; diff --git a/Source/OIDTokenRequest.h b/Source/OIDTokenRequest.h index c0261382f..85958c6f1 100644 --- a/Source/OIDTokenRequest.h +++ b/Source/OIDTokenRequest.h @@ -69,7 +69,7 @@ NS_ASSUME_NONNULL_BEGIN @remarks redirect_uri @see https://tools.ietf.org/html/rfc6749#section-4.1.3 */ -@property(nonatomic, readonly) NSURL *redirectURL; +@property(nonatomic, readonly, nullable) NSURL *redirectURL; /*! @brief The client identifier. @remarks client_id @@ -127,7 +127,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration grantType:(NSString *)grantType authorizationCode:(nullable NSString *)code - redirectURL:(NSURL *)redirectURL + redirectURL:(nullable NSURL *)redirectURL clientID:(NSString *)clientID clientSecret:(nullable NSString *)clientSecret scopes:(nullable NSArray *)scopes @@ -151,7 +151,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration grantType:(NSString *)grantType authorizationCode:(nullable NSString *)code - redirectURL:(NSURL *)redirectURL + redirectURL:(nullable NSURL *)redirectURL clientID:(NSString *)clientID clientSecret:(nullable NSString *)clientSecret scope:(nullable NSString *)scope diff --git a/Source/OIDTokenRequest.m b/Source/OIDTokenRequest.m index 02eb5104a..e188555c3 100644 --- a/Source/OIDTokenRequest.m +++ b/Source/OIDTokenRequest.m @@ -19,6 +19,7 @@ #import "OIDTokenRequest.h" #import "OIDDefines.h" +#import "OIDError.h" #import "OIDScopeUtilities.h" #import "OIDServiceConfiguration.h" #import "OIDURLQueryComponent.h" @@ -96,7 +97,7 @@ - (instancetype)init - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration grantType:(NSString *)grantType authorizationCode:(nullable NSString *)code - redirectURL:(NSURL *)redirectURL + redirectURL:(nullable NSURL *)redirectURL clientID:(NSString *)clientID clientSecret:(nullable NSString *)clientSecret scopes:(nullable NSArray *)scopes @@ -118,7 +119,7 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration grantType:(NSString *)grantType authorizationCode:(nullable NSString *)code - redirectURL:(NSURL *)redirectURL + redirectURL:(nullable NSURL *)redirectURL clientID:(NSString *)clientID clientSecret:(nullable NSString *)clientSecret scope:(nullable NSString *)scope @@ -138,6 +139,16 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration _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; } diff --git a/UnitTests/OIDTokenRequestTests.m b/UnitTests/OIDTokenRequestTests.m index 10ed05cde..3913b8325 100644 --- a/UnitTests/OIDTokenRequestTests.m +++ b/UnitTests/OIDTokenRequestTests.m @@ -115,7 +115,7 @@ + (OIDTokenRequest *)testInstanceRefresh { [[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration grantType:OIDGrantTypeAuthorizationCode authorizationCode:authResponse.authorizationCode - redirectURL:authResponse.request.redirectURL + redirectURL:nil clientID:authResponse.request.clientID clientSecret:authResponse.request.clientSecret scopes:scopesArray @@ -237,6 +237,24 @@ - (void)testURLRequestBasicClientAuth { XCTAssertNotNil(authorization); } +- (void)testAuthorizationCodeNullRedirectURL { + OIDAuthorizationResponse *authResponse = [OIDAuthorizationResponseTests testInstance]; + NSArray *scopesArray = + [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; + NSDictionary *additionalParameters = + @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + XCTAssertThrows([[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration + grantType:OIDGrantTypeAuthorizationCode + authorizationCode:authResponse.authorizationCode + redirectURL:nil + clientID:authResponse.request.clientID + clientSecret:authResponse.request.clientSecret + scopes:scopesArray + refreshToken:kRefreshTokenTestValue + codeVerifier:authResponse.request.codeVerifier + additionalParameters:additionalParameters], @""); +} + @end #pragma GCC diagnostic pop From 67a56f75e448734da139fbddf35347921d672b46 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 12 Jul 2018 17:45:46 -0700 Subject: [PATCH 032/204] AppAuth iOS app icon for the iOS samples --- .../AppIcon.appiconset/AppAuth_Icon_1024.png | Bin 0 -> 82647 bytes .../AppIcon.appiconset/AppAuth_Icon_120.png | Bin 0 -> 8785 bytes .../AppIcon.appiconset/AppAuth_Icon_152.png | Bin 0 -> 11459 bytes .../AppIcon.appiconset/AppAuth_Icon_167.png | Bin 0 -> 12235 bytes .../AppIcon.appiconset/AppAuth_Icon_180.png | Bin 0 -> 13692 bytes .../AppIcon.appiconset/AppAuth_Icon_76.png | Bin 0 -> 5671 bytes .../AppIcon.appiconset/Contents.json | 41 +++++++++++++++--- .../AppIcon.appiconset/AppAuth_Icon_1024.png | Bin 0 -> 82647 bytes .../AppIcon.appiconset/AppAuth_Icon_120.png | Bin 0 -> 8785 bytes .../AppIcon.appiconset/AppAuth_Icon_152.png | Bin 0 -> 11459 bytes .../AppIcon.appiconset/AppAuth_Icon_167.png | Bin 0 -> 12235 bytes .../AppIcon.appiconset/AppAuth_Icon_180.png | Bin 0 -> 13692 bytes .../AppIcon.appiconset/AppAuth_Icon_76.png | Bin 0 -> 5671 bytes .../AppIcon.appiconset/Contents.json | 21 ++++++--- .../AppIcon.appiconset/AppAuth_Icon_1024.png | Bin 0 -> 82647 bytes .../AppIcon.appiconset/AppAuth_Icon_120.png | Bin 0 -> 8785 bytes .../AppIcon.appiconset/AppAuth_Icon_152.png | Bin 0 -> 11459 bytes .../AppIcon.appiconset/AppAuth_Icon_167.png | Bin 0 -> 12235 bytes .../AppIcon.appiconset/AppAuth_Icon_180.png | Bin 0 -> 13692 bytes .../AppIcon.appiconset/AppAuth_Icon_76.png | Bin 0 -> 5671 bytes .../AppIcon.appiconset/Contents.json | 18 +++++--- 21 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_1024.png create mode 100644 Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_120.png create mode 100644 Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_152.png create mode 100644 Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_167.png create mode 100644 Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_180.png create mode 100644 Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_76.png create mode 100644 Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_1024.png create mode 100644 Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_120.png create mode 100644 Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_152.png create mode 100644 Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_167.png create mode 100644 Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_180.png create mode 100644 Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_76.png create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_1024.png create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_120.png create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_152.png create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_167.png create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_180.png create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_76.png diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_1024.png b/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..9d74d6575edc240dfc83467c6793b4bdc57e582b GIT binary patch literal 82647 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¨%Ba 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%0Dr{Hl+BxqnJ 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 literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_120.png b/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_120.png new file mode 100644 index 0000000000000000000000000000000000000000..f73edc78afe0b7b73758d4f0611d5813038e0ee7 GIT binary patch literal 8785 zcmb7qWmMHq)b8)Vp+Qnoy89p?-G>&WJEgl*x=TrEN$Kt`LAq1=ARyh{m;Za;b-&-4 zHMM8fv*W|;^{kmlB}HirRAN*B05D`_penDU=YI_u?zL}3%nbzqki3$tXbhUD@r}z)k__KqXs}L0x`+rK<|5wYad+h(u zzt_e8i;$W9Ygn9L{b!YcHUR(yAPW^!^UOT*wMfNNU%GZtbko!v*c;;G>;utIQy?bL zXmaFKRz)4}S613U#qe;M{;6{uRz|8DE`aj_P?@OSGsy-98dLRdrDg5yt>~Zmh#-a? z@0T8@nGb{esbLm*@cxMK>&n9XgD^=6QF$rd_&FmtQ-nJu|K?UKJOF?Uu8xan zJMnAp&xGyX?BD*wCyOBj0sz8OTr-d&|6K1rhaySd37Zhz4%==`Rq)FvQV*tqS&~dL zsDu66r6o@Y_-ir%xg9#6F_#k*is;-4AZMTvvF_u>Sb1=JEFn~`aw5ihvMZ~u`X*rg z!)$V+0O|2bI8W=eje6F8;%FZs5wupZfOb%%&e+0X&RmzH-Ly;vxe{eEEc~QKGz=)bQ~!hDb4U6m!=7Hx zZ)TKDuY5V;b2S~^%Aj0BH-GxG0ngTDG}_=oUqq|>>D&d{Q$ln9wLy2mr)y_~+z(!) zOkSQV?UGdeD&ys1^72fgO_3Ld6D{HqL}9d-IfopFD}DMd5B=ycVujb(ot-RsGVKn*7`82IK0GJ$NI381v6<+oo?0odT zzYg^d6V}Eo33<*ymsD=u+`D0GzbmPreMoeknSP%b0d3WiLUyZnjmhLK&oBc#iva{QOhP=*3RfxG6YxFEPKVS_3!kI#hao)rXa7+G%c86c7=Vnw4fcCg*qX<0`+(Z&%6Ag&3*L@Uzd`1FM}t6jBnn)5L9wE5Om9mX2K&ZEcbS2f)3O z^U6sScz$Ew;>k42#ozIK+(hWpN)xfyVkK%*)p1auPVP5@G*SL$eY^hSEErKFvk#C^^?D!KQ`*y7-vmeQW`+~bFbZFQ3D49>mgx-)BxgAUq^p4`O?XjiRaSoTWRdk*y z#~v$uoo4%KPjFSmUhs~l_&T!=RzW-(d2waAJb5ZLYTn}oms-)tCjSbT#eCn_mu8oq z`Wj-UkeLyW`xU|XFK&V$(j~6Bp91(Sb4HWOma1Tdhj9&`TWJ#vGz;>Ff87lWK>Ihv8zMLLUvx1Vzdkbdy51w zu9a-_Y9Nw`*XJI|9alB(W+4#MC4xN8)QrwW+~f8{`^&g&D#?S^lqSo=PFRDkYWOy8 z3no(PFV<~0z80(X_Qu=Vy-hs!Z0)~S)nM0LXME(z-Q^N<{v`jHoZ#T>H{o~mI(?03 z=A7JoGPC+du1leu^jf$qhPHo>u?u*kIr@Ge8M*2N<_SODwzY8fTU=YcD`kKCw4Z(= z@_d>Rl6yxHT8l1=Z*19#w@;7Q}o(qX0LrxmnIqNz5_xwY0y zZWDK=3KR~1WRTHHW0&_5f{EOk7i8+QT3c`aTb4n3CIDMBTH}ow9=ojZBx^9CWAynB z1eT2dJR>!yz8-Wj@YsGY_EE;gH$#f#{Eeit+{8!wGMkRAHO*PJSdg#O3t_%XW72Za+^&3kjdvDwz=*$#`%dhmw1;2fjT5Z{N=%{dA4M@;ow;Xx}TZ<3^j zoxidSvuSs%m`-x@G>sfK45AIeyU2B}w(KXeVjoX3Ut&z%bagg<_RzMrZ?#nyw(ZG9ZN1c+>2Q#(!qw}({d7d#5P{2gzM_}krh7kiKGqaK(0*d^d>&l zcfV=7wIQX)q@)Czc-g|qK1aSE+=eGIf(-h+gUbn|XUbRO{GqRRx}I`kZudi2grHp| z1v#=VwR>XibZdXmW0&$iV3+nq4=urzYd1)NQ)+PUG=Rp=`)Q3gU-+C`k$ zajNG;!{=dKjWEGH8_29TJ6p+bU!;F*HeBu4-(29|^-EiwePlBtFEuW^^D2W>*ZsRB zsf1c4!&HVpU-S0z50pEi_xY4?x8Lg9FJXDccSP|gN_)xk8Z}YQS*cY5^s+sIAW(fo0x{hKEVo&=F31ZT`HEa|TM^SPa(dl{(v%I?KO(J4&Y44BK$>A&JLc?qs zqH5>36tYFO8%Nk8yu-h|0SC}hY$gISr$3|Wu)l%m1lghyV;N$9=oFJStb8J!5&#S9 zdokNhw`H!uN}vfca^}vzzXexS-F1djEBB&cKiy$=9lexrF$zecJ7;3Uh0mDCRQp`s zjwzo^;!|WV)Ec2&{OC||Prq*8A{%N!o4_yuM9H)z0UR&E^a8|ymCL??XZMz-MwAV# zDT+wZgsa7uEKA#ykO)5;6BJ0j6h7y#KZfhx3k`LfbNc`6%garB=ow8B_^h_88ItwG zToSnQoh==iX}}-e{rl#bPms0N;^;j-NTn+;?Whulm5}<*&^O0?W&+8Zn~dl)ee0p) zMeCRVOWIJ+6kmu^_*nc0hMT~H9cV{zFg1J9-CMXv14fa=PJZ1oPjyka2AoTE>Pyjx zM|)y0XzyEuup2?Wj`iGMk*FW`+A31f*hZJ5NEvx+lyINv@2?tkd$zeJDP6IEwPWww ze{gLY4+%d#4#4?b9#(N9NpSVZ zNKLOmGBY(x_PGS5#+1gt&UgK}G_2S|Ng@XN0KXEh>uxoZ_;a#as%RubFV$^EQZ;`q zFa`FV3IC(>@E(V%C8+`-0&MDqCbU!L?FiVd=zPkT=Ix}c|~CGDn2#b1RBblgD`&~>p4Z^N=<&uYM&3A4t~JHapWFQ)k-wJi(8I35@@+2h)%2=hTzlt( zMF}=|DcJe8KSY{UVJ=2MXOR^U*;*m0Jx-R@u7uA^Trm&a4*rE7KA$ymeKA7xG0vN( z9AfO83vNv5@!GS%81V?z!K47!r`vsD&Nrxx(28JN)coBbfRSsw0u0PpMb7j~O;A-? zZRzTMKUcRuV-HS+{;17KL1<$DcZ>U~kSe**95YU9CnQ1bx<4f&cu(i>wla_ib{ee* z14?IwLfdYF&cuxot4It4@BE34NXI4K~ z=Wj2US>%WBHZcQDQnx5r#KG`1l9)$k$|e2Da#UrIcWd(XZ}a%%>`aU`@=PW=yynXzV z^Bo66gD8Szvoo>?@0J9vAH=B6C-6{qColFXe-<%m8~xf^K_JF1x|OP^El|vva@)wG z1Elm;2PMayL8A8FR3Q^zh`-L@%J=G5MPA=(Cliz?S!Cf#Mi0fM8$~*9T)Us`=-{(# zI46&fOkr>_$C}zvuj3!*x#o|=D>b#qBQlt^Uf>fzsf^IB1V>{#5{lqm-;yYo>n?;BusI$km?sy9C8)M`(MFuuY1j!VsepQY=+?yZfj2)@|}$ z*_TW+8zZXCtck3L+jg00-4Ad84B_hX;M|#(uMT`Wd7DWlupH{9F#N$I0A+$rsdm=9 z^k*`wmFC+9jWQ6Et-YvBjFoh<0d7S`dJrPhsk}(WtUas}_$W z%60KTRdCZE+KFMhW3;!*pJbr2{p%M}fn?*S#!C$ox>&Ad;~6U(9trWIMFhC=FQV zbcbXU7gVL;-dyt)MwaND%E7S2Xgo>)~Z8M#!RcWko`I%0^c}GwC6@A>8my+%FpmRN^b%4~-c-9JN|BG7i!*+j} z|E;@8JWX_o$KD;=W%}+cSiXG`3nnpg2!RvPRbHnPxV!3QItmi5g|2#xPv;w2XygC8 zIwnak9GLiF=VMVWW>?zE7CZiNA&9=5tVi5eX=F%s9hk$g%fZBDXV;=;Bbc4E$=hV@ zKTHC%&05g|q8mcn(x&txUJSs!BOk5=Tmw{S;zvQ_n?q1J*Zbm|Z&fVwBNF6*DD^m{ zAX#gRu6%gip$r->n*rP+;~57!OR_X^Y9gvZ_G9r-wXdODwd|cV?eY{pKDsy{d={9C z{#GEZW;6Yrop-uQa9)~sA&U{FH&R#9yz6X(miP!Kt$24%w2ne?gcg~Hr!n0rk)3}fREHbmKfOtXQ6o}>wH5~eA$D)(l?P^_n7M35waH-KcdJ)w%?fv`hXNgj@4)nV7Vc4* zQl`2!;fYO$iw_Q;fa4xoV8BN0M_CMI=cM&p%=5B!k#-CWU*98&&yG*VBk0}f7r$(# z^x|vTt2CJskBP4ua=+Ln_z9LgA!9@XNa-w+10r=eXz*`#<0O3cMRSZgF7C+%XF8oG z*7mhsLN@yhs>0mxSV)E$B6MPxb#51egI+E3_8aa#R17aV=t8D+e4xW8=!1Ahe6^G+ z5;`fT%H6b_Nl3x%{`vAniEkiX>RCw$sSt8-i0wc$N>etE%V6Sy>%aG5BCbkL-rf74 zgNjDQsLznpnEh!@zq+Tvr>d(42_dFOORq10kA+ecxU?Js`QM<5l@(NGa7NnL9_+Vb zp@4J*F8+Lgc1XskPB-UzbzcxtqMCT<9~2NMj!t|$em;0I^Yc1j29Uvo?Vh5aDqQH9 zJM_o0E-JqKYdYqhW3dFvXQ$Fhz($4L;@sZ92izY}dH7%2dz(5ciiWI+d-HsBj(0!- zAc5uEo1lH{V=dkvLo&`SPXNr-%LQpW(`CM!%r6R^14qbYMJb7JXB&ZiBv|pGPl3SW z#VSRg#o_OEG#(g+&ptY{Evf4ZbW;|24%i#6c`slr3yOR=GwL%cz=N{_x{YLVwBQ`x zU4B~JB7SN(IATgZ2^8`w)xtzX-}vISdzvVg%ul`Oeei2Scg_;*$%7hZS6Ww+Tdt!o$zuqbdH3?hR zb{q^7si|agb_YVuKe2ohd`6B~&3Kww8G>Ddq&)OFIv!w(ZOs)@j3HYIY%G%{SDH>Z zf*ufvXTHnE&GDz!a;PA!;U_7ng);8X5k>o}f1_4ArwrQ&9$||srE)h>J?IcZQ-5iI zxS5*4EhFAmA7WST;9&MIuANzpzwArUpUBS3$Ei45QZ+q@x1;?{Pg~p8yEVg52@Q`Hi4a~?7AB3na0=-ol*#JGupf6Vn?m1-t4$HdhLx?qa464>>B%ZBk4QxS z3mkYEAlfuRi*u)R&uh>bGi2ktX++p@BQLp$x4X*x>@<4HqQlz#VisLp7Z$2kAuc8c zY2AIj*ztQ<;RrOqr08$~$_raw%ffR$b1uKpm0puH7^r@nbdnb;hg_VuRb`Ra2WzAA z=K7xSDo$FL9M8ej1$2+xklFGJ& zc=j=GSGWLQw{j($Ts0c67cR!4G@t%qN&6lP@^3Q6yy<2a3jl)IhiBK+axya9T&J6m z1~a%1MQr};SDrLu2TT2idG;9-x$bdogC0K<(EDuw@>-a!5}7*1%iiA@r#WvBeesvP zngszrs*@^l0IsD=#*NW)A`pnHPDrmr&((wrPc{BYMr8BnORm|fnkvr-5Z9gaTOw_^ z#p!?@VkjH{0_CU*E_svBi+st+dZ?B3(#VVxeQOcgkbnztUP+K@<|bi9VEE=+c8UHK z_Ba>+gD)h^I9FN~UZa#A`&a*IDBj}#|+^3Bgpq;k#X@X;JH1dm2Cgp%kpaeKT;cei0=J57=0t-u3 zmG6xkTpM%YeEx=y^LpeA{8k-xy{yAL2Bwh);Y&NZ7;@`RN2I@!V)<27>RsyUc?fpt zE(>?sny_d*o5Fm%0$&;h=B~_HZ;DRz&O1u61N8O$8qeZW#oKy#mGcbC zHtZMw2K7~+xkWG+rY19T*G7Vqk8b2yygZYH#r>)#YVsvK%ekt{Q_~0yT3(+S(+peF z`Foum-sN%)y3~A+^i@Z841%KJnA*B7>3zP1U2@eL{uJbmgf7WGoA%OUH8ee_A+(uh z=qhtze&iDVa2Nvh6-PYtKj6!d^Pg+D^=>e}_3WjvizAVK7niUJTGHlaBbyWZbm)!;svMLXuOu^1u%q`&g0 z=s2B`;c{xmo?&G+{RXYB+qws#)nnWbSP#qxJFCyXejbqNKde?3(~CeNKxhS6MTJud zxDP1#f9x({W5?&oCSPq=qs;@i=&j@mGiGKZ*KRl0xOK06o;MAaH|QXHJ)7)goVTTW zo0Xl8pAD3R5BtvMu6@bv*UozWk(STE<00S>30P15-pO*?jyVhED}9P%*QoJG@)QM6 zZf*)Q!&OH+7#MZtrd+A6M#S%qRXmPeSWe!mjjbc?R*q{P`(7b6V|G1n zuV=p~)z+bVilw9zI()zq2XZxZ>0Nm%7Ohe;zs!H(yMJK3lx`BD`8_ zaWGNPjaC?AnkXiXj7>Xje-NS=%SgRfO027nrY zu$vX*<4n>-#B32Rhy%y>I5vyCR67UzHj?pMD_cq@U3gY%-spH|)V*1mu<)Nr4OIgq zetr&CF$rTFo!BVG!^_*JG18m`4ANAgJPMc3PCX%igI+oOJee(#+Op+CL*8-9boy%e zx5Eni>bb^%K-rJ-lPX#ow^1DKUQeBc?o2)JSp=@8i8hm2)1?{b6k7OdE91W$K$;5T zk>UfegFoF{Ypj&lYeBKlrn>(8jwJun6XAlx?LWS-swPtetsg`xBOze%Yw=k&5M-cd z9bziJQK60ji`BNZ_Z=Y^Ks8hqt;LPao$b9pn>+jD{kHtDXN4*ch7(-hyKHo8CDAzw z&ser0w!3argn2?B_q>U}yMbb1BB=1`)Ek}K zEg${mM=exKZ-x3q`pr(aB3aWjkqaaX<543Lu1p`GHdh^?3d9Ck41-xY=*{jhE?)euM%UKi-@UsVqf}JbYw7;>7w=JHo<}5w z?Tg*fw}dAn4!Xkr@a2O}1h`k4ycWUQm@QIWa{kt`V&AvF& z0Eetq4_nJWy55yPmDKmoEP?RCcZ_o0fQOL!w7hJ=C ze*pj*l-CNE2Mz*|1G8l1qDZo`IsCMq@A8opNQGpoUPyoI`WZq!Bw(Sh57$VTJg;y% z0{V(o>)R-vyuw&8urd^RubvoQg5-)nIxDh?&{-8XY^wlsSYhPj_m{1~6L6Pm-pk3x z%{lY%%Z^jnuHP@ByayDEh-3Op%GPYh|3AT_$lJe|a)fQG1poa6k(E@0R*D-1{U56< B%a8y7 literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_152.png b/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_152.png new file mode 100644 index 0000000000000000000000000000000000000000..bdd5de3c3c0aa6d459e2b852838d0b41c22188b6 GIT binary patch literal 11459 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 S{?aiD07`P|vbEA?q5lhYSEw)m literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_167.png b/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_167.png new file mode 100644 index 0000000000000000000000000000000000000000..1bb56407dd68e33fe359e267080dcb1242d5db1d GIT binary patch literal 12235 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 literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_76.png b/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_76.png new file mode 100644 index 0000000000000000000000000000000000000000..dc824d5e7710955b47346c35e32f8d35adc89fd6 GIT binary patch literal 5671 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 AHvj+t literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json index eeea76c2d..0b6ca5edb 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Examples/Example-iOS_ObjC-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,5 +1,15 @@ { "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, { "idiom" : "iphone", "size" : "29x29", @@ -21,15 +31,27 @@ "scale" : "3x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppAuth_Icon_120.png", "scale" : "2x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppAuth_Icon_180.png", "scale" : "3x" }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, { "idiom" : "ipad", "size" : "29x29", @@ -51,19 +73,28 @@ "scale" : "2x" }, { - "idiom" : "ipad", "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppAuth_Icon_76.png", "scale" : "1x" }, { - "idiom" : "ipad", "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppAuth_Icon_152.png", "scale" : "2x" }, { - "idiom" : "ipad", "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "AppAuth_Icon_167.png", "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "AppAuth_Icon_1024.png", + "scale" : "1x" } ], "info" : { diff --git a/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_1024.png b/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..9d74d6575edc240dfc83467c6793b4bdc57e582b GIT binary patch literal 82647 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¨%Ba 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%0Dr{Hl+BxqnJ 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 literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_120.png b/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_120.png new file mode 100644 index 0000000000000000000000000000000000000000..f73edc78afe0b7b73758d4f0611d5813038e0ee7 GIT binary patch literal 8785 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 B%a8y7 literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_152.png b/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_152.png new file mode 100644 index 0000000000000000000000000000000000000000..bdd5de3c3c0aa6d459e2b852838d0b41c22188b6 GIT binary patch literal 11459 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 S{?aiD07`P|vbEA?q5lhYSEw)m literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_167.png b/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_167.png new file mode 100644 index 0000000000000000000000000000000000000000..1bb56407dd68e33fe359e267080dcb1242d5db1d GIT binary patch literal 12235 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 literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_76.png b/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_76.png new file mode 100644 index 0000000000000000000000000000000000000000..dc824d5e7710955b47346c35e32f8d35adc89fd6 GIT binary patch literal 5671 zcmX|F1ymG3^FKm3l{k?G;V40rqvNQfyBm%ajueiLI~pXUK|oqkkd|%)X`~yZL%KoI zKYsu3ef!?V?9Thmn|V7sI}@&^B8&fo@(BO{z?YYk(s-Da{&gVc!zg#&?F|56DB5W0 zx#}q^37RAAA!ZiHw+M)*z2n0<03ZzWbTl)!L%4$9BCKp2MCcEj+UUVH79#YzyviKP zj*LN&_^|%B%}x*g z4~wgv2)+0}fnYslHLxVo83E>n@UfY5@^FCpp%6|!4jx|4H()LfPJVU{UUm*XHV#ff z4go>eH-5D#t$(%Fig6AFd0b8xY9 zaj`vcu(^0SxSDygIk+(VgZO_8DTIr;vyG#x4blPp57X=|(#=(bo}T@m5cdB{c~Fo2 zfBHQT|FaOn;lV7<5Bjr7O4R`X#L4nf;#!{R`*1JskCP3)Lzh0MKU94J5upG&#(+JGG@q+l zmjhbv1dEpm@4FUmPA~2l+WOWrPBW}BPg}QqvRov{&HofoUABn?tW>PvP>jvE$mW~* z{&oyn@Rlh0Gjpk(ESOIC!XY*yMlN=C^{W|^i=Ozwg>};o!eUoc;3e<-?FtbOJ#kT2 z4m^AgN8o5EflSDLLnp3to9w(6!}tCmu9-iRlW|Ea^e%tIASEBf%s>EuU?AA17WIk# zbobHr1#T{TYUdf!jjKdBCm$E<&27YFoImAj>E?i=Y5u74M;7qp!a-dOpo}gs0-hRA|fEW48 zE>&xF_9Recxyd5wo}V8m4|oigh?0Rg)&w#9K6y0$vDhNx4$3`_$nbM`e zvWwlI`gbJW*>-d%%2w^GJHr-u{*4h5N9u_Zhu_zEH zHb@idj`~?{*aGxdH5;m-`HG3c1mJzf6UG2w1{Y2aHf+s#N$^CG8eYoT!IK9zfHD>) zl2nnas^v|4(`~nzv*64jJXQXex;{tJ+xRhMSG%Neca)BLS@af9w_fsL0K6BJO!R0otUV*aG{!Hu(g02;r-<!aCa`RySBX2uNs=$>lLf^|ip)zQVS0;S|asA$x z;WF=SBeau)j%8|rE#C5@)&a7Ki)mr5!+DfC?lLS!VbPuBB>@1qoIAbsdvhZ~7qPuO z{njG2!Cd)d9-j*kI(gP!TD5MZy(kt&=GiT#%_cOA=%cDU(nT()9j_MsywLh|_MV>G z!Oya%R$;o{n;@98+bKQACWBWQCwVOZZUs9Zw}VCl97`@eeRyy*5F>h>hO(LA^ZQdn zMu!0@xMYvYNvw9GhpI7RNzRXx7mbcDUgi%RvJP!PC0?xw&=*%1fAyV8e?@!e@V!sv zPhQFdOUrF^zEJB-mbMPLbnjE|Wd0HTaV=4Ff2MXs=KZXjdT{aX1~%oLfmf^O@DB`u zp1v$@4Join!TWL>9Z?%yj4(m7SrPty&$YhS5TWvx#_zj?$C#?G#*Y`m#sXvby?r-C z+Mu)0ES?1J1C+x+_kIfEy;3d!J7Z+Q5tGvB>ZsY|M}qQ%jQts}Yhi_>sXtEec0j`g z7y2E;>|B}gZ2*}L4K~Z4@Uj zh@L^Ss1u$Vds+>Iuyb{j_-g=~+xe&Ype387Na48Pqw*+c$tp~4T+&=8oywUgP`fn5 ztV6DnnXB}+6?GLc6l{t5ev9-w+hMS5DZA2B3nB(lf-Uf zPs^^D!kG2m{CosdYzg%@TUlAZ zx10L-t^B@v>Fh>sJTBrS-})mGt_~Znu#cR^%Wz5+ zy%$sEI&7?iDyq)+c}7L=)J#{JY|?oY(o-Rf4Mth7N!NE!^yB@=vcwB?4P)I{M|!yq z`&4*iQW8Pt)+>a2+GER34_fpi$%vZlz~)_kyT<4mOG||HM`!Be`n%YVY~{j+v5AqO zb{|}N5pzv|9;awZccF-;r9Tm~CV*IrT4^*oa$tDwPm@Up?Zu5GHC?Is%MtU(#pd8LABVHy%;JLSFd5bURe|Ur^;32 zQaDG#OZwwHr~Ni{!!0cNr;?!^m9{tlEUk+({UV#lIn6b^upjE%K#A%cT7<>-(yEsx z&MLA-+{34loT0x;+&yV%F;jw&bV7tLg#Ki(s4wjiJi!5q)4xpiG`4 zhsMof)m2eW!90%D3E^%nJuzQ|ARu^60-u?tdB6W75aUfr{AUi}v9n>0>REQBr2MVH zrD)WS?6oJ^M5h2tg{>B_K(dg33XmKE^S=?sxlT=xg@5C(V4%E0V!jvv4vsi*Uq-ox*w#?OznGPy_S6+RhV9#Ph&; zpkB@Q$B6e^8QQU9S1GoSG4S6CQwE(aj-nXH{z#POHE0XGkLKf@gS(|kGQ!?Wb<#4A z_ewWTknk4ur9Zf*j#(K$2t$k_XtH|k=vWh5e|*AsPbztq+NzT*T@b=6QQnaWqYh6L z05Gx&UiA>|3M77G3Z>_82K$SVYQ02WD-PgQ#L{r#FXRM0<4wGOH?j*!RV!({8#Q@U zRaslqNw%juK@D6V?dF@Mop^fe9BH=mXZ2&|KrutAY($X&HgKe=oBWLgW=vaof{#6` z5;eK$3T0ZN8=C?$R0+$&JWf<+1B1h<)%`r`dcwwiF8D1=Wehy(db75ye{NF~L;u%I zNW~8qZ2uD5A7&IL9}_xa%r?|t@u2}vf>O#^Mwz|XN~7CBmvjKt%)JBy(kMIBC0=}AT{cw3~v0>HMZB^>f*crHT#pzbm&ZH8&evWj5cK>lO76*l^Wv=n$YSu=B zY5me#Zh)AKuJ<50cIMf+*SaY-eYkoa27OZEd3GfiwI(eSPFo1ady}9a?O0Em{)##4 z7;-KGiFQYGo;M@?xKgNBurqhHJrjaw30~y2_taqnf?0gVu1qL5LM$6(*N$weKAa(H zv1?)Wx|h@mfc9bpg?tyZ;F7)W-aBx#i22BRxuQX*?1U*6DkAwwz!rp%8t2hhf4qIa zm{K;(N<}0WA;0ts^*rDbr}JC{|W(03h(4bL~ORc2eW({b3c3 zB&%Ix;U3pLg&QP0kLut=wr0y!@w%e{j}BqfISYfB2J1yAlb_yLmnM#uEg#d?DP4do zv_#Fk5HDwB^Ko_8r3v7#wJrf4tH?&Zi4ea@dVHbNJA2)sy5mEW-5JqwcX;LEh}~9o z`1(O*NQ=umzm};uI!Cm~W0R#C=v(!&892oiy0m&}T=Gx2ib*K{=jkrAQ@7#81j#uV z{P3pIwbDeQGMdKGdG}J6Cgx6K@U>5-N|f0X-N@z%gUmDo(y%%FtJO_KHm~mWuB6YA zNB$k8X9``~=cVyNpUEBpB00l4dIx=n+i)=Txty_;aFlZaXM!&TTe1$IM0ev3b&cif z2XS994dkN_aw08?SdCy>_^*tz(kC4hbY%X-L@WO4?`3X^0fRntXFoWMEh|Nemjd`y zhTML$A_o)L_oSp?`}teMFG*a65tJRZXXEgq=k*1xe81c$ogc3kBZg@3b47D(0GMVr zT9zmqad*EpJZvtQP%dK+$&(Ohv?tYkSt_^N=0`5TjqqH-oUxI1Ea}F!!ywfy@c6TA zlb`PI1^h+k%lCHPePiY(O~rbLk@4Z=DaFP4zcb>fAh5q3+s}U~?^DZGi+Q@14b1$d z*c1al{(e>^+qs{>5+p;mz{P#-+aW>#Ao6j3*OtO&yGyxoqI!# zkDxoJ+6$7_jYRtn*f|+l7_u{k7zh^xumUPD1I4Tr^jE~ha7e%L*_Wrg-EfrrWv+bdg{;2NyjXe!Pj~xbfJ}qsS>=sTQfF~-=E3HWDp?%uH?`m4 zn{C$Ce7*FhVewvqS2AmD%Nuu+2HZNQV|+L64}+rx7gL4#%F|!ir#m0aV1Nh+;z3+R z0?EW1gY~A={*aWGN%$I2Q?t?0L%qX5n+0qrvGiC4hsZf^J3fJS-9?87sBcskiYl4 zgBSbD80w;?dcFz+WQevkvdY5&;NV-|S)*w~VH;B*vy%M+azTx;gRJ}{ z$3tdElV|*ueW8*@_t+G=MVxXsh6Lxsl-*uR_>=bDsMc;35*z|?eBv(1O0DTa5-wNUdjdEnm#Mt+)PFn8}+yzueQ2sYV){g^0z0)LQLN|4{qJ9GPnyTt9JB7Mvb zV%B;1>$?`VYzy|9(~Yhc3kbJIv%E@Czn0ax{x}|%RDi3aHb65{exyu_o zs(O2uSG_Li9#-38bY~QGwt3Hy+#Ooxb^-CdMAwAdp_eGK-|I{4mwfM|CQ2yYLHGq~ zNtd&N60C*yrVn5PYUqU1ji-ZO9zN2<$w%E?Gqx$Gh?LXykKV zMO4;d)7}tb4e5PW1S=6)To|1;>(fW|;?dxI+Z&qV0Hs5iPz$4yTmeXItC9POCL_JP54J7vwDV{BCghNy#?=8}n?8hM@R znOelwN*$lW(n*&n2Tl-;LzW2g2OBr@aGUM*`{zW_%!_t9#-c>Co)Xfr9b#gYhpac) z=(ZJZpxli?%;BeWBbG(u&wth9J|X4F3yXe=+>o`EEu68hTYRb-E0MuMd}J_`QWyT2 zVfscy=q}LdQG()A=A?C!je4$ed%mi;ErCRha@fS&cB#GX68zT@0FkH_2-b_6aA; zWh1$3cuQ=cD4#W~SPQpbQ@<4ND-PPDB_-TtLdN@fD2s=I*^!SVG-h^X^+Jr94U|>V zX5URbrT$`V0DjZpBR)wRSkhs^0exc`)EhVB4WtWZDBY*2MnfKKw+2fQvU4s1=Pma$ zRBF48o$`*QX$~&!R2?&r&NzMIu3#o4&7WVbDI`Ez7U${b{}oct~pJC zwf?=n$qI!x#ab#W*6n)U=ix9@#}QAC9|Z9KrcU(gsYw>*XKfpsA>88Z@q=57hqru8ds#sS_($E7?MS^KZMTP zYWX17_AjO;WW5e9Xsk(md)_E}wv0s;6An;3vGVgri?$PAX4x7bJ-?fuF)?tLCRvzW zKIea&4PIc{?&KorB$TLBuoSU54&r` AHvj+t literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/Contents.json index 1d060ed28..0b6ca5edb 100644 --- a/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Examples/Example-iOS_ObjC/Source/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -31,13 +31,15 @@ "scale" : "3x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppAuth_Icon_120.png", "scale" : "2x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppAuth_Icon_180.png", "scale" : "3x" }, { @@ -71,19 +73,28 @@ "scale" : "2x" }, { - "idiom" : "ipad", "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppAuth_Icon_76.png", "scale" : "1x" }, { - "idiom" : "ipad", "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppAuth_Icon_152.png", "scale" : "2x" }, { - "idiom" : "ipad", "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "AppAuth_Icon_167.png", "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "AppAuth_Icon_1024.png", + "scale" : "1x" } ], "info" : { diff --git a/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_1024.png b/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..9d74d6575edc240dfc83467c6793b4bdc57e582b GIT binary patch literal 82647 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¨%Ba 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%0Dr{Hl+BxqnJ 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 literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_120.png b/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_120.png new file mode 100644 index 0000000000000000000000000000000000000000..f73edc78afe0b7b73758d4f0611d5813038e0ee7 GIT binary patch literal 8785 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 B%a8y7 literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_152.png b/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_152.png new file mode 100644 index 0000000000000000000000000000000000000000..bdd5de3c3c0aa6d459e2b852838d0b41c22188b6 GIT binary patch literal 11459 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 S{?aiD07`P|vbEA?q5lhYSEw)m literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_167.png b/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_167.png new file mode 100644 index 0000000000000000000000000000000000000000..1bb56407dd68e33fe359e267080dcb1242d5db1d GIT binary patch literal 12235 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 literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_76.png b/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_Icon_76.png new file mode 100644 index 0000000000000000000000000000000000000000..dc824d5e7710955b47346c35e32f8d35adc89fd6 GIT binary patch literal 5671 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 AHvj+t literal 0 HcmV?d00001 diff --git a/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json index d8db8d65f..0b6ca5edb 100644 --- a/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Examples/Example-iOS_Swift-Carthage/Source/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -31,13 +31,15 @@ "scale" : "3x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppAuth_Icon_120.png", "scale" : "2x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppAuth_Icon_180.png", "scale" : "3x" }, { @@ -71,23 +73,27 @@ "scale" : "2x" }, { - "idiom" : "ipad", "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppAuth_Icon_76.png", "scale" : "1x" }, { - "idiom" : "ipad", "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppAuth_Icon_152.png", "scale" : "2x" }, { - "idiom" : "ipad", "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "AppAuth_Icon_167.png", "scale" : "2x" }, { - "idiom" : "ios-marketing", "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "AppAuth_Icon_1024.png", "scale" : "1x" } ], From 971d2dce04e4caf1c98557973459c522acf26f3b Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 12 Jul 2018 18:31:43 -0700 Subject: [PATCH 033/204] AppAuth macOS app icon for the macOS sample --- .../AppAuth_macOS_Icon_1024.png | Bin 0 -> 137215 bytes .../AppAuth_macOS_Icon_512.png | Bin 0 -> 60805 bytes .../AppIcon.appiconset/Contents.json | 6 ++++-- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Examples/Example-macOS/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_macOS_Icon_1024.png create mode 100644 Examples/Example-macOS/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_macOS_Icon_512.png diff --git a/Examples/Example-macOS/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_macOS_Icon_1024.png b/Examples/Example-macOS/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_macOS_Icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..b7883fbcc5d499a6811686d76b655e590809e734 GIT binary patch literal 137215 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

EBt) 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

75iYMh4xl& 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 |By?!Ua`bvV zF7;maG*dpD;!o9$twb!2ZGP|W&F$0k9QeuJAt}YBWGhX*?oy4#_ZbpZ7=*2{U`*{rQT@S=^zu`P z3R;OjU?(s;miu+V`Fa)Neub&lwsDQ>1iP-g?`2Uwtazl^Wd zY;Z(Wl6wFuzrC#Cd2oJi-URhq*3ktVoZ)D`{MzO+WWEb7f4MxXTDUw2hsR}};!i<4_6kPmQP zaj{-(c(@%D{u0tK08|WC@yw812qxv%LAa}vBU!0C{CX2F60B!iMULoNmx*X3e?iLs ze=KXN@q+qW@+P*Ii}7qdFv#u$3&qFqMnDhtX0~8<;EMy>apoEoRZbLgSL%ijefw z{#o!9hgx1W_p_Z+y*rn5lKTUMyG`8;wO>fV(C6uz348Pe$MK;!khAV#GzdZH%0Qv5ZOasZI9kj{;tc)Z8Nl9x}CD1hz`?c_<4rs|tdcrjlRwz!-$;YvcFlw5Sv!5iX=Ue@&)qa$yxS5pgSZP0Ge= z(`mXc56l@9veNsR8X*GzW*wzV*E$4A*i(tQOv z_9h_!O=3oYr_vmWCRdvAe46thM=8zD9tOuR9&puS9q%>IeFjcSr9c^W^+|2wfVYwM zO?J^pYpP?8YJ;;N|GoRWZ5XD73t0ymy=2$h(*v(5hfWTRI0}_%J17UG+Q{f~BoqP! z7u#!rs|2+#nmuqKkcP6#_`tqQA~8Yi;pI0WYW6?nBSN?Y#CdU86OOqLn{Mer<+wCA z4=3C|04#%Uee~Dcwepjs>uHl~KTMj~z6qqyZN%!95;7R27y(o%@FYHne z@oiJ&K6Yj#0Ml`=v@0~n_wDZ0;0TKRRsH%*j+PfK^WtZYH=MW4cK_O_nV6XTzi-G$ zx>nRyL~4dLzn4q-eQ@qS=Ld-Mk@>a@XEbtdPsZk^dY}8-b8f+clGBx#jmbd-P|@o~ ziCfimj@#5-ERCsorFVB%No05`%?7IGGC7`Demz5(CQy{G|K)K|hXQhqj;v^N7fs72 zR_OXuANCqKYuD3wXLNPUTx4jzAypP`XCu=7rDpX|r#kEw>15)FwiPkoO1k#08-&;- z+@t9Fu;dgbA<>Qj9Br<}v9z5$qAMTa2p%fDW47$>k(HRrGUl27AeA;UBIj&CQmyBR zd#MLY1{b_=KI=7Mi~zMVI%GC?wDxlALH46xP#+!B0?U=;!yRL3C%s+Xv33tV_{RN3^zt&)E1qCBauUy~!rs?_3KoOu^o0_w*C-Z8(Oxt4@&=x0O z?TtXcZL{s?f7OqQ1f)IyZ$BwCkPW*-|a2!5*AyF0H!(HSqKkll9sq+9B5!hKuL z-Dxl)oFg-g`~D#mJezq%8Sbi&$IRr65A+5(1v5)rb}8kT*~NCBZ4S3Ei6sqk)Vkbs z_T^R=ier&(;skvCv<(0sdIhnZ4tSU@It?Ulj!x6Rob?W*yU9a?@;iEtp%2&Qjt4)_AA+`gtn9x^(imp^=^YVv5H47$NYY$9Km)u29yLM2*2Sk(bN*mwA;@C)G)O7levap<@p zn);fTm#q@iojsnG;EQRhvD6ybq;=#*MMpDdA;hySNIT}LqLSi!6HghqcEVdt{RuA= zu3Uxnimbm8%7de-;=`OofYDWQ7T{K%QCbj;m}*IE=6Qp6eOEIx^Kf|@I^yZ|;oHo} zcVW~T^Ukq``wbP&i|yb%M)BR(^q=x~f*$sD_wKb$cHkMt(i72Og2JGQyZepo#KZ)P zQxT<)85_jb3-V_4VN35S=3_nRGQ;&{!zDS|$pqo>Zog=%=ji}1k}Ga?=x8inVa%uD zdY=GCOB^uGs3{@zDNbpWh(u6bWqsQ0bCwTh?9mcbK;6S0BBv@xz=bLlasJA%`5?tM zSDWq_h{8w{eUJChXDo4T)2E?1dzT@6&J=1FYdEBUmtPQ!8a?;Zc18fzJ1x!k0n_(h zn@O$;ZPK`JyS|W#Bzx3{T`~IvP(qGq!&@P+b-I0Tm*uY*?>5;V6DoS$v84PLI2`P^ z9a@Rxz%4a@&vA@pe|OYLj=pIcPoW}N@@QKRbfiKuHsdW5$~FvUSIh9bjZ z(1-EQSbL^O=|c-Z1TDp52H_FucF_|kW*F*FhnnW#pgf33+dU&5PSnuV?oWgvdXP^( z-&(kYLXNL?|ImE&XWV8txc|lQhP>KE*>p?Yb-gPUNg2gvRs$6-bBe5Miosjf>p7b)+c=mgy zsu(pSPJ&oiSV$`*3-N;yUDMF{BR9Fkso6zor>A1B_t~c!PoBt-$kxMbKzDextHB-3 z8@2IqF8AL1RL0Cqx)p~WDZ61tZ2H6?zJnUy>+}6D=KdLFq}Wu%(gp?{MH%BF#gVM_wZ@vgdW&o>Nr$P$e4 zsbsWSi7d96Y7(^g3{LLHd1TImraewKhnjsu3E*NUybVH!Jav85znk@}QbD#M9slgr zM|O5vTB8rTx%wQfl=A zWd#^QY5A@jSJH_LsrYHSKNB4F-^dU&VJpx_>Tp1(BiXM$4MkA?;CpN~Qe|o$3DS}T z?aZ@b;uyMeM3TK-&|CHF#!7545%q&~!aw%MYuTn?>G)iAT}UqnnYBj66MvyB#}`!%-TVzb^&X2p{Ws_D3HX(hLUj|;jwbk zz*y!!o2HNlLBb7I$CD#;|CtEMOdu%mlRj*Y`4Nc?640zCRG8YVzWvGD1-H(^Q5((1 zARMCf$8(u6d!KLtJDnDo_w)@FNKofw;P9U#4Q zJGy5s8Q0g_D`4=SX27ks3Mfu(bkOkam(2f*g9q1+uq-+o%FJBV99@Eh4E%+Tq0iVg zw`tWf#KUB2%HchT#_G=lu#_~fs}*IYVXSC>9_|tCf8VDA1Vi?~5(<~?$sG`wxX>9` zTZJaglY{0*X7yIhf5!jZb5Ss(bBU6=?@xjRHGeU%=YO)`S>Gxj^c z4ScVCSyW?)lB8WHzkIDfH$_{7ohs;^h!%N@B#ukxxvt{9Ed`idCP8 zG|@b0gz$7av3KB8`9KL!DOuv4QGLhFcEzN|f8#@!DfR}FA`%}3i%!fuZML(qS1qP+ zv3k%GR$vTd3q@A4TrZO@zwnGciz?4xaXb{>)Rw(J#B!BC6Z_~6buxg6M5h$SVEHUM zt@3vruhz4s+mE@B(h=x~ZP$q_xV(_e`2FQ}Ox>S$R2$egw8rHix^5{V7141p@2{i! z_wc|#9O=)opm(F;9eHEzdf^a-6f#*NPc}}ebKgU!vxb1Yv-s1{oORkSfCnveP zjOm)h$X$3GGmCIbaOOnkuT@AuoCjRfYU41u$UYMj5{bSE!W4~u<)iC7R@-IY_07>@ zz9C7BDj=`-p}nGHhBCJFv+^z9;_C@L(J}$@2&8fhj{E}R+_0s{bBKAdo+3_WN7OrK zlM1o{#JG#-oL+^Cs32nT-{}1OoksDLpp{~BEOCvx%`3B=?ds*8qsaK}=m~`Inw|MC zmZcWB|G|LS(EMbv8Bd`)B?sf32Sw{M3L%Hg`adBj;}PDhWK7!#;q~=0cC!X8ys{)i z4HkhSxsdFrG82YDU6U>*d9Wql+De}+X5st;j_L!U-ecC&_!gU zd=-nI+7*5rkf43W*Swp=`nEnLiLK%`a3qAcDJdTvePu?=RUN+S9P>fX^SHy;_=xS| z?|dRmwt`+py($xa$mB1PVRh=liN1NVBcXxQjaS)pwo%0{o_in0W*)A(9Nr4UyVWfv zc&+7|>Z$JXPYr8A^6D})U?Sqo;}Ws_f%Ur8;seW)*f_hDmxHbA4}MF48MB5eH@iCq+RX-=v;_tx6qE*nnw-LFacovuCOWde?yaW)YcjsZ&0-v2g$4=p}?5;_bP-9Eq~Av&{Ws&<{og^P@K#Vz)ao;)Hg&^Xv+i z2#S+W2@e z)@9MfY>AW7PVNJrn}@&hVXc_{+wB+psUXz2#qG8`Kr1`_LptYq9JN z%4nI@3Dm)Hl#LbqpBA8{_*lv!Trm%@AbMZ^b;WWx?3njX z*-Ayp`Yb#Pc}pT>$eu3>kCE2%90J>&(A(1!Z@=Vq8DfIhV#o?W2#?b`W3s!EzJ4b7PwPWmTfV=pDQ9QS%%iqUnj#EsXf_79BbK`+8w1wKiX< zCTTSW9R)45?kQJIN zmGvTZ6vX2VuU!avo6!&=Dn@^L$6PW2$y1~4UAKrTGl?}{Z^+)3M-wI@Mz%QuEhSfC zQ1*a%Hwkc#=(-#o!@r-$dk!Z^OK^9%6VVh}kk89|g83s?tMn_S&b9Ef-}e+?!`an& z-(EWzVU~T+ygz(}%}00;zg#L13H?`ul{}402nd>tNnMDZ6?SQZ_>1`4-r+kd5isK@ zwWUga&Ymypfw?R34{=@ZXVF zFo@!uOHm6bCtNi`lT>oH5nuS(0a3%rNplYc$M-))S`-SOzv^}a+Pk|1 zF64IC>KnYbYN4-0P*fJdVXog2N%+fdrieO?|9Ha6Fi5kA#E2459@AcUKlL&_9O>}9 znAcg`FjgsTBvp%6?N~67bar@)x9&Ow_ni{aHh1Qud6b*C;|>h#@mh^vO%1$nj(MiY zu3TQ>!o}V*dOJWWVZQI3plkX0bIQQr;9@;PO6(S*N_||#Vg5DWf&%)wS>J<}0~Ixp z3jAFsO6iZ9O0jn4M(veWdyI?lpfI*XHS6V5Q>H3V6=3j>5aH+Z%6ehUzExbgg+CXm zvS$bh5ztHe%@U>Jwb$P9na}WG;7S^Yx_$HQ6#e_GdQc?p3jIWgR)sj_I4R>Sz?IF6 z2dbB7?`8xdz)OOmZfoDWv!3_`(X2PAY~7bRep#x*p^qwZ%>lfLV}%vrf(JE56*%yY zgPN_h&D7F(aWsC;t0j3{|IScnEt2Y?k6(sW}w!WT?+y$>A|c5)x{{!?z?s=S=W-9J!Rl= zX_B-mQMH2PxFKaB>wNdRi|%X8`Ryd(@KP2W5|!H}KNLwE1r~=-6g@sp$96=L>ryj* zP`9Vv^|~)01$Kv042~OLx|A!OgzF5t=~`?8-e#Dz+|JVC zmZ$vJ#xUc{{J-n=$A?RcZ8Zjd>i*ozLNzaBoTVNJosb@+Yv$+&L^=NGdEoPOkn+Ir zj6L=YC7>&RRO#uzWVJF{pQpMtn@<$@?lGAstvkeYlk3}8oqUGcUyH(ec9bMZnORvy z;Pp%QHmn;YkR+J@cxRZ-3-r;~4^x{{Cg`=U6`fL07@P0b+1Qi_r3Q~AtA*~`nx~r# zQPHHXRWNGEfPB8L4EK$Ji01|m?%d~}6V#9R#3^j9tLS;IVq@(<%u7E#TGZvtLSNu) zPB;jT>GQr$s%=o3G9EdnPMV{#Q%1K7lqYp*8f;oRab5)VcIJ1#wyGm(AKeW3bz4 z<-h_1%0ksBcq2-tlGjSpFK985>7C2E{}wV z{aYg$iX`0T;l9Ge7*+6`oAouZPu?`=`lroRqD`cmuIs=okMHdmw}m)!-FlF;9^D7c z_ds;sM)68g13T56xw7va|K~-k4GRADop5xz1s@Cz^cIGEgib{fe2Kru$e9ks&902M zN-C>CL68%Y(kAf|b(Fc4CSI2p)^MrvvsVD5)g~S-HlvSx@9wNVRjD0kbY&8XIfdor zHO1<>Zwoq69MaE`mdH2Et-R~v^nu@4{3`IKjZVNnUj{u^#dt=~?pp*?*f!6n78%UW zl+u;AZUhqYFV0lq+W2_?g_kSohxI!>}gpuDU=nK5#@=E`o zWq$i(3sCmF6b+*jY^1Nj-!yBEI52ZHMfEJdoh@SUq6P+-T(>5srV2az3{*E^vp-BX zSlphlFc1`v894JCcsPMb0Q zIrv9G8$NqB)GD#@@%->s3`%mvo#2-6SvSso2K3Pb!fdi_gtchB>R9Qi!>~98#kj9Q zFm$<&K`+C=h~Llei=;C@!9K3Ms*!V~E-&KxJmgAPMaw!mr3lM#{H@XJzv&sKNz_uf zgG>=kB`yw5Uws4VSPj)fN$_g z?#IUXMA-QcBwXe4xY)aw^{Rwb@7|9U;!y3s0Cj@*Loy~lDcq>~Tf=D>mkxL7l$y1r zzn7Y?ST&7)JPCO>w|<~RXl{e`#M*qLI83kaPr4_Fz{B3nOb=|p!oB?CxX+h+U!B1f zoK#x)a&(8V01vct{p%BtCXS}M~?KyitBJaHVtxDptPkaw0SM}89L zmyx7FvgfLVxn=8S zMBTEDs@jga)v`YbWUhF>4M8BAjl5yhdP$264}?d5et)3LN4Pj0uoD@Z<44BlvvP7j zv&AYeQFxJ4c(FFHDdCLz=+S`rC?UHE`ts0Oy2ixu-(AX&;1Q-3NwFO_3KQn>1IToa zF5$rzRZqt<);?l{}D*PTlIL&aWHa8Q-@P$eh&BARe0%~meiuKX4 zx5?ifbM%J34e~)eHsR>mN-cI{6e^P+(D--}b1eJq_90|Ur3L1$7y~Jy%>mceFm$D6Q>Q%x@aZ?__L_#@i&7-@0Cw? zx=cwWvmt$`6r1f*KT)`MehW~yaaeV+^x)V z%^V(W?|rOB8=#I_$_)ZxHH>wm{IY4S60fNgB)@Nw{g$d*U+oRc5GhVfs9TC=T&Csh z3pecwaqR!jL;}?_h1=O{{*}fKqpwpX!m|{VsmMnxboDwjT>H%sfwTb4wP<}LT8Cwc zgA>ny!bkW#`PH0CwlBtuFRcuXkUDlqb7yRpcvD_mlT!Mw4v9pK@ zrDgRRPXI0HAEpLR#vYjjYZZh@K-l@Fh8*$kd}O2^v80upp~^(q5EBxvm)mPH@rs<+ zA#dS7CxO#Nx785My_sGZv(a!xIBP4jy*F?EzksP_@z0GFh{pXD^ZLfBstdfCnHi7E zi>Sxjho>f4bI#yO=)=8_6UYDwP+7M1l!Sq#tV4>_v#4=7VhNXncfN z*@EsjHlPM9v4%ft4)yXe`CNYWJ7Y7Bcn`6nuszy z-j**K6T-8%auE$xiaxvN*pZYmZGOC}`wA)Y<-@R2;w`y;?Fk9cwCjTlFJ`0OW5h0 z>b}|L?WzgZ-+D&Tn~^YugT@jmn9A62KiQ#IpzXANI+&IF4oHr9aY)r|S1!l~O(!et z!B6(-4R1yCwv`TUb4E-=;DVfonC%hs*vO1TQYuqvbH;QBg!2QuUightXoz(K{0Of# zx75hvQNmt8)DxYl<6nd)eHw6y_w2?XlaNPFsK`heOT5{vEnCDl*ZT;Bo-Y9Vxkp$Cdu9O`O0@L4@!cM>HQi`rY9uH5-lvS z^jG^0w1E3Sre!-%k@wxxe2K>vNY{efC{O35J)T(|#qNW8XlDBZu|!E!xAM7xn+Hxc zza4s+zJMdBdb42>;B>Jhm47`}j-YZ~!*|M^cpsRZXSJl#T9cb`-KSVoB0xTpCtz$h zRLw%AHRo&sQTFZ1dAEshx=7qEgo$4NIY5SYQQgruS$i@1WI02#v5CcZ&ntKPl#Dfe zei17t?L|nq-MHGGT%V{Q@kc6{BX?9jDDWA7ftaDQHhj%xZWk|wy~ort5>5>~oh}YX zMMM3zOr}JRnT_4-1Wi_(KfFehz{fcrMFjvq$b9o(rTCxVZ-OjM`8Nu?JFtKH3`=Cp z@}r|O|E_T_NH@Nr_n>UG6^eQN@}HD>=97d(99Pn~2GX@wkK(XVk7{+6m1++HH3~t( z8i@2Om#h;V?plLa7+)&J8e>cqz|*y20|^+CM}0Yhy>|{%SpV(fJ&V*+31F>6FpZFt zo`2?Xys?mG_qt;TKdA6cUF(nr29;_Q;2FOOTq>M=r10B2_5N*LLFn5vAAX77KcimxTnS;f?qIqW zM^Zrd)-ok7H}di~m7AO7@-$S|Mn4?Y)ozr=8kZ^3t)IL5Bi0y1<7F<<;kiFW$Fgy# zbY4LE_XhXm{rj%LM04FGf0oe!FW42vOGNdrZ!6+kWrunydFe`8I$hBs3TT6eF9peZo79YMXv|A4~vo#J6H=fA)>m>jgY;KQp$nVKdX3|h70O5 zr0i&QHcB5=xX)-;RU)nJ-6D7m{?lJj^j$|0b458$Ow7y<&xnX9 z$BZ5ad+*l5ym5UUloZ=?etb2CG5`uL$-)x$I{+taMhr3RK8^`&#M=1CH zP{Z_*L&=g%^&TdEnAX=iM%UtBx)7Js6V%v$14HR~>Ce=niEKE748+vYVSt2C_oo>Z zQ&t|?14epvGxx3DpJJilTT^Mda$QppKpvw4TLAVy~ zB`S5sCxba1H~RU#$jfCUc!iFc8L@3-+uy%aCbZ1kwKp|8YFi)z$YX1*@Pxh960o{2 z^}{0$m(w3}GR#vBusWr$ndATZ`hmzMT3`OVtE*%|sHBs_U?~Rsx)0L_>**I+5GFf8 zZl$L-h=HX7T&c{jBwVKb22;}|^{YJc{?gq( z?K4=}cky=Y5fgL~CLLu=tHnWIjg+3$-*VtuB?&d~JWw!xTET^89(n?62SkNlEn$tt zZ;XU~u7*?Ec#BYB|1TruKXLP|*cj{d06#_%72 z9PUGB!*&Pbtic{dRag68n@X_9z}W<_NtUM!EmqQ(>WsdTNmrpmz5jSgkM1B?>v+ll2=#jyT~Kc0;E%4EO(3Rwa~+RKTf#pi9q#V z%;<|QdRgA~9sO$$+iG`{B6Z(6$D1y7p!LOe%shoX0jfleDNI%dSj;#~tIk=9JtS(v zJpzSWGG1_Y0AI1rn?C4++iSmk2KQ9i1gOi1dyG>bwgtMmbnI_W;&KJPxZZOU^8~q- z7wNm`XcFk9_9LXpUv>$=Li1>VSrvW?Mv`?J=n#vm5W}wWq{VlLrE5Nr<(n8A|J?ri zQA+gU=oBIOvRgc&6s96D++gz(Z@~?B^@r>fto}OvMQ@ut_0jv&W>y*>>2;_DoR?~z zdyv9|5@-@aaaxK?jU59N^RtM2D1;q%ua{j^bacY9XOGVoL_VO7-@Q8UxL^Tsi)Zul zw(GH3LEPG#oUSLE7lT%>W%V}|!;=*dOZ@q#cc(*?d5l4FY1pb^4M(h68dg-zRMG+n zhBOP(r&%WCf|oKjLhoHBDl&qJUi%tM-x4fgV=Gav_Xj@jH0y08!9=emw3+RDC=ZCu z;K}Nij^Ir2K_R4^0h`a8BK5s!zsij7*jWv*uj)i zshm>7Zx3Hg2`<^}?)f+gtTuNRp<14+T?UC%!a=7 z|5XcjP;5o%-w#0W$LK$4^a!~o zuR!6k8Az<=hak+TC36qN%zAKLs>cwN?ptZ?^!IvnCu?A^3>tR_&!MW;D0lS!x|(q) z^F|v19>^sgcbQE*j+xk^dR@1GQfu9Fw~^u`+CTT_--g9zqjpGZY;N> zV1lsY6SLpWuU_Z#r1P&i@jNabgAPESyB$~~k4!dWko%O5D@AK^G!%c+We%5aPXMa# z(xMU{A{z%6HR{Bffh)${SZ%w*;e%$zv4p)lFcj?JCpZ!kT(BB15s|RH6dUQVqKD!i zs}C6()sTq*2knB&t*F>F1$KF`4iK06sh^GC!N53v>*TeNOy_&yBs(s>E~|R94ar0(91j+ z+zrjd>n0+me?pD_2sJW`smX{4?J;&8u2!bN@f&mfHd5ur=_3Exi@y)Ze-4}3QTiUT zzP7d}XuH_p>E4Xf@@$VA8oniV0O}@luW*qTT*|`FE4O$>bQ^vsihPtgIHE~c=BMQb zBYxDPU$6aSTrvAE`Tia#sQzGhH+}hC@L_sW24JvhgOd75Xb8MeXTE+R9s5zADh>*~ z|5U)zS-%8#N{52e8D|r|R61VxQerHVutknLFG|>uozkl!pjh3eLV(=BXbPliBZbW` zUP5ImaUfB*ugr45vz%n6JBvu(Ye{?re=3$F^yS39k*G?>6;Bw!#*Q=>>?0gRERw6o;mbq#6X+NQmivWmg9(#Oy{*@-ADmpg0RX8@~t^Q}U z?gu`(e{7a1MdCD;;6I;|RfFfEBp1vAurNz2`d= zMoVF9CR_^Ua9X_NqaT11oB4oYlo<1y>gu&1TZI6jK{}23(QxFm?hhvC&0MIMyF#xQ zK0DL3y-iL$K$VADxG#Yf$`h)i6Z;~jLbtagokJ5rU$je3uC`Ltd3eDTg*06r1oS~TUweZqW}+nWVQ;5GbBzD41~r$N zCCVB~(0x{ib@*pPFNLiftg$Ky35iizdHL(QSa^y{7R2T1myAnzotFQ|I;Yv-Zw_I~ zsx3D7-9aoPzqgZwf{k1*wtUuH&3)Mzv6@i#j`{aM=}Gi7)+hN3Pbn(52=SclFWBR^ z2y_NW*YtPTHC}nVby`zT73n?Xl?9oMu=9fE^02v;Rm2aU7a!1$YQCI=;2|Tzue`hi zGTR1w91r#bt9!*Pfu_puAQcsOZ({1lcNwn>#$j@l#~MAe*jYz-b`Vi|O2C%^0a?l- zuQtHr^YLfVzE1W{key z*thEaV|dUAw<6`S?h429slh=#@n1XLzFj)HRBS@7tZGh*_s#JhTuf9syUu#r0;OfI zCF>)UB&cO8-8P4>j7kf9JG5{VwrYQJ;Vn%&MkHh`>6yBwKBkc{9 zdW0#w+Dcf@yXx0`8X7=^SU`ijpfqTaL(%$G^)0flwEV*aN;b$b=L0|&zwOV&Ruxj} zCw94Z=X`kFyC)($a!~FX`**Eb9Y@?jd=Ox#b2(4xgBEA4p! z6C(i+6hs}+(TskmXe!grp(Gp$aASUVyQ{oJo|$oroFo_G+$=<5h(_RPoN?Gbm%1XP zfQWjSl67tT$X;h8^R*{51h101eS4xxdR5guaiAUJXsM>P|nI z>zZdx*>Xt(q2V;gog|4GLz-@LY`G{GAPN>}y%JRZ->4r8fI5+GTcfIJMY2_B+-bY$ z@$`Oo*8^didg`FG(DnmK>Y?a#MKdpu`-9{+Tjlw%+34|4#u@RY>zZJ;rkd`8nzIFzIz$#V^D`wpdkjWdD}aqFSM6l|uc>qPAuKw{I~>p3awEdA|6z_DFrsYm|& zUhc@SAPpg_wx7}L;;hOX1{F1w>OveJRoU|5WRX(Ou zsGK%-m{iP=^njSx=C>RqfK}K1@cqvFzV74O0^E)mWS>m-^QmQ_gO~}J)~%w{_w~fg z3(=yuq3Oq@) z7(lr)ejg`4%FsI58AIr>YmzfkV}cQ6IHl^nON?$zxPE%u?EQB#h!mMyWiSTq0(*6l zZ*;<&GD5m&!5Fv4AI~kq&3WiFEhXQXKvc8k_%qGkJiYJXLR|e4(?s*OfD>u_Wlv3u z_lI2x1B@$sm(ET6-Z9p(4$#z0y;~>@62|9C<6Y5@li?*1)$}2AxuUrjWG$>_ z(x&B1J*H}M45-)-AL8w-$4QaitO}-N;P$qL7uweWuc}^awSGP)$_?@=V1XU*z_J}( zu?WX!h?Vjmk0Io-ggPRs1}2Z6jxMUpfLbGYkg^rnm~EU^4+_tp?_8XHc1k=`X5**! zSN_CDVa2GWTAU<}#Ha24xtM zr_&{PsiN@t_@Y7%S8-Q5;eDGPygtBrsfeCUREslvRr%?kyv>GH+zNpw5r32NQIv{( zwPNT6m3-bNr5h5R{tze(?W}706L_L*f%!w=x`O*}&P3#@%|^H<2F6eO_dpEDinfz6 zPc4Wtz4+8I43gr%A=bU4WYq<6ICcD3>GlY5;~9^nSnT=8`89N$C(MyAfjr7|%me}C zm@a`|HW%6sYLeWIc&kq>R{jw!p~a&5JGE`UQ6|iL`I)3_ikMYMInQj*O0|4&^*w5M z*1LU}nLsL;5U+&GmCRbY)Byf#!#U|4bRxD$mXase3r>{gAbTFfp?bgCV*-7bNH5hk zh&yLSzL~_4Sodg-?jaR!PPR|p$?7Hoq!DdDE*4yulna})xpC#~60JWTGwG|-M(j~# z31NjNCTh(`L4(gO_h(r=Z>~AZf=@=WuJts6-qbWCplwnp{y38^sRWQKjf0e|Ay5aI z1Z}0UniBC9fK^+}4=6EKB`&L&{w^ESc_L2@uhqH0=H==rb{IDgGlu?M{gBE*+V}2B zmCMi_fwIH>IB(BV9v3n*;taSH)oJSuSx2Ufrr^D}9d2xenf3Id^uu+EOC3l{9EBw} zmsI)x*drjowj&ZsCE#{JnIf)3pc{*WBcKXSUGJx$?4KJaW2VR$oGM^^he>WyDaf=xKGd?^)A{)R>aLCbgo+R^+hRW$XrQd#+9 zscTQfFHLT1=Ga$0e4aKK<2)z+)u6~iXuu`$14niov!rpM2U24EX{IOz!u05DbpdFu z?r2hmc&ilC2`yULmq9g5oa}cn>j0vTozTzx%hPHv!~^mKQ{;07RuJS#K*OR5Sm_ab zh8&{z`_1uemg%0AOyl+_xs~yAZ36}(MUf+g@$t2!w_y$vNX^a2?8D{3r)6bUuCAVl z2fyZDk&lrfH_gQ$Ix)}Rdm#1!L~ zf~Y(jQg-mz>q)K*S+mC}Ee{JDy(n)VIKRO1khzz6`nJSEo8wXj`OH~aj9hnMg9_Yx z3_`yp`O8lffrUC%H6%1|3z8d!A7PTrmw!UH3lu7iB_fn^d9c+F;p9&KM48FYOsU|) z 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
T7!f28 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 pPUa^&k-+dN@ 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 Xv~O9;M==jNbVS|LM%u4WUW)oZ6yS{V literal 0 HcmV?d00001 diff --git a/Examples/Example-macOS/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_macOS_Icon_512.png b/Examples/Example-macOS/Source/Assets.xcassets/AppIcon.appiconset/AppAuth_macOS_Icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..a38111faa489fe1f732fbe0e00bdeb7040819f9d GIT binary patch literal 60805 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

f)zd3H3XaY$5Cx~v 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 literal 0 HcmV?d00001 diff --git a/Examples/Example-macOS/Source/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/Example-macOS/Source/Assets.xcassets/AppIcon.appiconset/Contents.json index 2db2b1c7c..be30d8e10 100644 --- a/Examples/Example-macOS/Source/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Examples/Example-macOS/Source/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -41,13 +41,15 @@ "scale" : "2x" }, { - "idiom" : "mac", "size" : "512x512", + "idiom" : "mac", + "filename" : "AppAuth_macOS_Icon_512.png", "scale" : "1x" }, { - "idiom" : "mac", "size" : "512x512", + "idiom" : "mac", + "filename" : "AppAuth_macOS_Icon_1024.png", "scale" : "2x" } ], From 619bb7c7d5f83cc2ed19380d425ca8afa279644c Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 13 Jul 2018 11:39:58 -0700 Subject: [PATCH 034/204] Update samples and docs to not use or reference deprecated APIs --- Examples/Example-iOS_ObjC-Carthage/Source/AppDelegate.h | 4 ++-- Examples/Example-iOS_ObjC-Carthage/Source/AppDelegate.m | 2 +- Examples/Example-iOS_ObjC/Source/AppDelegate.h | 4 ++-- Examples/Example-iOS_ObjC/Source/AppDelegate.m | 2 +- .../Example-iOS_Swift-Carthage/Source/AppDelegate.swift | 4 ++-- Examples/Example-macOS/Source/AppDelegate.h | 6 +++--- Examples/Example-macOS/Source/AppDelegate.m | 2 +- README.md | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/AppDelegate.h b/Examples/Example-iOS_ObjC-Carthage/Source/AppDelegate.h index 4fb93255b..ece00038d 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Source/AppDelegate.h +++ b/Examples/Example-iOS_ObjC-Carthage/Source/AppDelegate.h @@ -17,7 +17,7 @@ */ #import -@protocol OIDAuthorizationFlowSession; +@protocol OIDExternalUserAgentSession; /*! @brief The example application's delegate. */ @@ -32,7 +32,7 @@ incoming URL on UIApplicationDelegate.application:openURL:options:. This property will be nil, except when an authorization flow is in progress. */ -@property(nonatomic, strong, nullable) id currentAuthorizationFlow; +@property(nonatomic, strong, nullable) id currentAuthorizationFlow; @end diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/AppDelegate.m b/Examples/Example-iOS_ObjC-Carthage/Source/AppDelegate.m index e9dfd54b1..716270381 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Source/AppDelegate.m +++ b/Examples/Example-iOS_ObjC-Carthage/Source/AppDelegate.m @@ -49,7 +49,7 @@ - (BOOL)application:(UIApplication *)app options:(NSDictionary *)options { // Sends the URL to the current authorization flow (if any) which will process it if it relates to // an authorization response. - if ([_currentAuthorizationFlow resumeAuthorizationFlowWithURL:url]) { + if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) { _currentAuthorizationFlow = nil; return YES; } diff --git a/Examples/Example-iOS_ObjC/Source/AppDelegate.h b/Examples/Example-iOS_ObjC/Source/AppDelegate.h index 4fb93255b..ece00038d 100644 --- a/Examples/Example-iOS_ObjC/Source/AppDelegate.h +++ b/Examples/Example-iOS_ObjC/Source/AppDelegate.h @@ -17,7 +17,7 @@ */ #import -@protocol OIDAuthorizationFlowSession; +@protocol OIDExternalUserAgentSession; /*! @brief The example application's delegate. */ @@ -32,7 +32,7 @@ incoming URL on UIApplicationDelegate.application:openURL:options:. This property will be nil, except when an authorization flow is in progress. */ -@property(nonatomic, strong, nullable) id currentAuthorizationFlow; +@property(nonatomic, strong, nullable) id currentAuthorizationFlow; @end diff --git a/Examples/Example-iOS_ObjC/Source/AppDelegate.m b/Examples/Example-iOS_ObjC/Source/AppDelegate.m index 3422c91fe..cbf861d4a 100644 --- a/Examples/Example-iOS_ObjC/Source/AppDelegate.m +++ b/Examples/Example-iOS_ObjC/Source/AppDelegate.m @@ -49,7 +49,7 @@ - (BOOL)application:(UIApplication *)app options:(NSDictionary *)options { // Sends the URL to the current authorization flow (if any) which will process it if it relates to // an authorization response. - if ([_currentAuthorizationFlow resumeAuthorizationFlowWithURL:url]) { + if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) { _currentAuthorizationFlow = nil; return YES; } diff --git a/Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift b/Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift index 8cfcf9bff..070c3d71a 100644 --- a/Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift +++ b/Examples/Example-iOS_Swift-Carthage/Source/AppDelegate.swift @@ -23,7 +23,7 @@ import UIKit class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - var currentAuthorizationFlow: OIDAuthorizationFlowSession? + var currentAuthorizationFlow: OIDExternalUserAgentSession? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. @@ -32,7 +32,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { - if let authorizationFlow = self.currentAuthorizationFlow, authorizationFlow.resumeAuthorizationFlow(with: url) { + if let authorizationFlow = self.currentAuthorizationFlow, authorizationFlow.resumeExternalUserAgentFlow(with: url) { self.currentAuthorizationFlow = nil return true } diff --git a/Examples/Example-macOS/Source/AppDelegate.h b/Examples/Example-macOS/Source/AppDelegate.h index 3a5883c05..e7347493c 100644 --- a/Examples/Example-macOS/Source/AppDelegate.h +++ b/Examples/Example-macOS/Source/AppDelegate.h @@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN -@protocol OIDAuthorizationFlowSession; +@protocol OIDExternalUserAgentSession; /*! @class AppDelegate @brief The example application's delegate. @@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN @interface AppDelegate : NSObject { // property variables NSWindow *_window; - id _currentAuthorizationFlow; + id _currentAuthorizationFlow; } /*! @property currentAuthorizationFlow @@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN @discussion We need to store this in the app delegate as it's that delegate which receives the incoming URL. This property will be nil, except when an authorization flow is in progress. */ -@property(nonatomic, strong, nullable) id currentAuthorizationFlow; +@property(nonatomic, strong, nullable) id currentAuthorizationFlow; @property(nullable) IBOutlet NSWindow *window; diff --git a/Examples/Example-macOS/Source/AppDelegate.m b/Examples/Example-macOS/Source/AppDelegate.m index 367b02ca5..7a8874b1b 100644 --- a/Examples/Example-macOS/Source/AppDelegate.m +++ b/Examples/Example-macOS/Source/AppDelegate.m @@ -48,7 +48,7 @@ - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { NSString *URLString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; NSURL *URL = [NSURL URLWithString:URLString]; - [_currentAuthorizationFlow resumeAuthorizationFlowWithURL:URL]; + [_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:URL]; } @end diff --git a/README.md b/README.md index aac96ca2d..aaa7c9754 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ order to continue the authorization flow from the redirect. ```objc // property of the app's AppDelegate @property(nonatomic, strong, nullable) - id currentAuthorizationFlow; + id currentAuthorizationFlow; ``` And your main class, a property to store the auth state: @@ -224,7 +224,7 @@ authorization session (created in the previous session). options:(NSDictionary *)options { // Sends the URL to the current authorization flow (if any) which will // process it if it relates to an authorization response. - if ([_currentAuthorizationFlow resumeAuthorizationFlowWithURL:url]) { + if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) { _currentAuthorizationFlow = nil; return YES; } From fadb76d2da6958bd3c75703fc38bb91d40fa790f Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 13 Jul 2018 15:08:47 -0700 Subject: [PATCH 035/204] Version bump -> 0.94.0 --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 43441e937..07c4b83a4 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "0.93.0" + s.version = "0.94.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC From b8c893f0999b53da53bf2e204ab2e61a836ef97c Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 13 Jul 2018 10:56:01 -0700 Subject: [PATCH 036/204] Remove all deprecated APIs AppAuth 1.0 gets a clean start. --- AppAuth.xcodeproj/project.pbxproj | 10 ----- Source/AppAuth.h | 1 - Source/Framework/AppAuth.h | 1 - Source/OIDAuthState.h | 13 +------ Source/OIDAuthState.m | 10 +---- Source/OIDAuthorizationFlowSession.h | 44 ---------------------- Source/OIDAuthorizationRequest.h | 35 ++--------------- Source/OIDAuthorizationRequest.m | 26 ------------- Source/OIDAuthorizationService.h | 13 ++----- Source/OIDAuthorizationService.m | 33 +++------------- Source/OIDError.h | 2 +- Source/OIDError.m | 2 +- Source/iOS/OIDAuthState+IOS.h | 7 +--- Source/iOS/OIDAuthState+IOS.m | 2 +- Source/iOS/OIDAuthorizationService+IOS.h | 12 ++---- Source/iOS/OIDAuthorizationService+IOS.m | 7 ++-- Source/macOS/OIDAuthState+Mac.h | 7 +--- Source/macOS/OIDAuthState+Mac.m | 2 +- Source/macOS/OIDAuthorizationService+Mac.h | 10 +---- Source/macOS/OIDAuthorizationService+Mac.m | 9 +++-- Source/macOS/OIDRedirectHTTPHandler.h | 11 +----- Source/macOS/OIDRedirectHTTPHandler.m | 3 +- UnitTests/OIDRPProfileCode.m | 2 +- 23 files changed, 38 insertions(+), 224 deletions(-) delete mode 100644 Source/OIDAuthorizationFlowSession.h diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index baf27bf61..542cc7bfa 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -401,10 +401,6 @@ A5EEF29A20D821960044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; A5EEF29B20D821970044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; A5EEF29C20D821970044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; - A6339DAA20321ADD0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6339DAB20321AE50043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6339DAC20321AE70043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6339DAD20321AEB0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -607,7 +603,6 @@ 60140F841DE43C8C00DA0DC3 /* OIDRegistrationResponseTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRegistrationResponseTests.h; sourceTree = ""; }; 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRegistrationResponseTests.m; sourceTree = ""; }; A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDTokenUtilitiesTests.m; sourceTree = ""; }; - A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDAuthorizationFlowSession.h; sourceTree = ""; }; A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgent.h; sourceTree = ""; }; A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentSession.h; sourceTree = ""; }; A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentRequest.h; sourceTree = ""; }; @@ -783,7 +778,6 @@ 340DAE241D581FE700EC285B /* macOS */, F6F60FAF1D2BFEF000325CB3 /* iOS */, 341741AF1C5D8243000EF209 /* AppAuth.h */, - A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */, 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */, 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */, 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */, @@ -963,7 +957,6 @@ 343AAAF91E83499000F9D36E /* OIDTokenUtilities.h in Headers */, 343AAAEC1E83499000F9D36E /* OIDError.h in Headers */, A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentIOS.h in Headers */, - A6339DAA20321ADD0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -993,7 +986,6 @@ 343AAAFC1E83499100F9D36E /* OIDAuthorizationResponse.h in Headers */, 343AAB0C1E83499100F9D36E /* OIDScopeUtilities.h in Headers */, 55A094D020DFBB11000045D1 /* OIDURLSessionProvider.h in Headers */, - A6339DAB20321AE50043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, 343AAB011E83499100F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAAFB1E83499100F9D36E /* OIDAuthorizationRequest.h in Headers */, 343AAB051E83499100F9D36E /* OIDErrorUtilities.h in Headers */, @@ -1026,7 +1018,6 @@ 343AAB141E83499200F9D36E /* OIDAuthorizationResponse.h in Headers */, 343AAB241E83499200F9D36E /* OIDScopeUtilities.h in Headers */, 55A094D120DFBB12000045D1 /* OIDURLSessionProvider.h in Headers */, - A6339DAC20321AE70043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, 343AAB191E83499200F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAB131E83499200F9D36E /* OIDAuthorizationRequest.h in Headers */, 343AAB1D1E83499200F9D36E /* OIDErrorUtilities.h in Headers */, @@ -1060,7 +1051,6 @@ 343AAAE11E83494A00F9D36E /* OIDLoopbackHTTPServer.h in Headers */, 343AAB311E83499200F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAB2F1E83499200F9D36E /* OIDAuthState.h in Headers */, - A6339DAD20321AEB0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */, 343AAB3E1E83499200F9D36E /* OIDServiceDiscovery.h in Headers */, A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgent.h in Headers */, 34A6632C1E871DD40060B664 /* OIDIDToken.h in Headers */, diff --git a/Source/AppAuth.h b/Source/AppAuth.h index dd30d9935..e7f7faa61 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -19,7 +19,6 @@ #import "OIDAuthState.h" #import "OIDAuthStateChangeDelegate.h" #import "OIDAuthStateErrorDelegate.h" -#import "OIDAuthorizationFlowSession.h" #import "OIDAuthorizationRequest.h" #import "OIDAuthorizationResponse.h" #import "OIDAuthorizationService.h" diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index 7cba488ca..4541a1ced 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -27,7 +27,6 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import -#import #import #import #import diff --git a/Source/OIDAuthState.h b/Source/OIDAuthState.h index 3a98d378a..6af06b285 100644 --- a/Source/OIDAuthState.h +++ b/Source/OIDAuthState.h @@ -23,7 +23,6 @@ @class OIDRegistrationResponse; @class OIDTokenResponse; @class OIDTokenRequest; -@protocol OIDAuthorizationFlowSession; @protocol OIDAuthStateChangeDelegate; @protocol OIDAuthStateErrorDelegate; @protocol OIDExternalUserAgent; @@ -149,7 +148,7 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt receives a @c OIDExternalUserAgentSession.cancel message, or after processing a @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest externalUserAgent:(id)externalUserAgent callback:(OIDAuthStateAuthorizationCallback)callback; @@ -274,16 +273,6 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt - (nullable OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: (nullable NSDictionary *)additionalParameters; -/*! @brief Deprecated, use @c OIDAuthState.performActionWithFreshTokens:. - @discussion 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. - @deprecated Use @c OIDAuthState.performActionWithFreshTokens: which is equivalent. - */ -- (void)withFreshTokensPerformAction:(OIDAuthStateAction)action - __deprecated_msg("Use OIDAuthState.performActionWithFreshTokens:"); - @end NS_ASSUME_NONNULL_END diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index 8b141a667..d8c4f7566 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -105,12 +105,12 @@ @implementation OIDAuthState #pragma mark - Convenience initializers -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest externalUserAgent:(id)externalUserAgent callback:(OIDAuthStateAuthorizationCallback)callback { // presents the authorization request - id authFlowSession = [OIDAuthorizationService + id authFlowSession = [OIDAuthorizationService presentAuthorizationRequest:authorizationRequest externalUserAgent:externalUserAgent callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, @@ -507,12 +507,6 @@ - (void)performActionWithFreshTokens:(OIDAuthStateAction)action }]; } -#pragma mark - Deprecated - -- (void)withFreshTokensPerformAction:(OIDAuthStateAction)action { - [self performActionWithFreshTokens:action additionalRefreshParameters:nil]; -} - #pragma mark - /*! @fn isTokenFresh diff --git a/Source/OIDAuthorizationFlowSession.h b/Source/OIDAuthorizationFlowSession.h deleted file mode 100644 index 78903f6c7..000000000 --- a/Source/OIDAuthorizationFlowSession.h +++ /dev/null @@ -1,44 +0,0 @@ -/*! @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. - */ - -/*! @brief Represents an in-flight authorization flow session. - @deprecated Use the more generic OIDExternalUserAgentSession instead. - */ -__attribute__((deprecated("Use the more generic OIDExternalUserAgentSession instead."))) -@protocol OIDAuthorizationFlowSession - -/*! @brief Clients should call this method with the result of the authorization code flow if it - becomes available. - @param URL The redirect URL invoked by the authorization server. - @discussion When the URL represented a valid authorization response, implementations - should clean up any left-over UI state from the authorization, for example by - closing the \SFSafariViewController or looback HTTP listener if those were used. - The completion block of the pending authorization 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)resumeAuthorizationFlowWithURL:(NSURL *)URL; - -/*! @brief @c OIDExternalUserAgent implementations should call this method when the - authorization flow failed with a non-OAuth error. - @param error The error that is the reason for the failure of this authorization flow. - @remarks Has no effect if called more than once, or after a @c cancel message was received. - */ -- (void)failAuthorizationFlowWithError:(NSError *)error; - -@end diff --git a/Source/OIDAuthorizationRequest.h b/Source/OIDAuthorizationRequest.h index 5c30f2bf6..121f09bba 100644 --- a/Source/OIDAuthorizationRequest.h +++ b/Source/OIDAuthorizationRequest.h @@ -193,37 +193,6 @@ extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256; responseType:(NSString *)responseType additionalParameters:(nullable NSDictionary *)additionalParameters; -/*! @brief Deprecated, replaced with @c OIDAuthState.initWithConfiguration:clientId:clientSecret:scope:redirectURL:responseType:state:nonce:codeVerifier:codeChallenge:codeChallengeMethod:additionalParameters:. - @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 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 - codeVerifier:(nullable NSString *)codeVerifier - codeChallenge:(nullable NSString *)codeChallenge - codeChallengeMethod:(nullable NSString *)codeChallengeMethod - additionalParameters:(nullable NSDictionary *)additionalParameters -__deprecated_msg("Replaced with OIDAuthState.initWithConfiguration:clientId:clientSecret:scope:redirectURL:responseType:state:nonce:codeVerifier:codeChallenge:codeChallengeMethod:additionalParameters:"); - /*! @brief Designated initializer. @param configuration The service's configuration. @param clientID The client identifier. @@ -232,7 +201,9 @@ __deprecated_msg("Replaced with OIDAuthState.initWithConfiguration:clientId:clie @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. + @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:. diff --git a/Source/OIDAuthorizationRequest.m b/Source/OIDAuthorizationRequest.m index 7f6c8caf6..0ffc7b586 100644 --- a/Source/OIDAuthorizationRequest.m +++ b/Source/OIDAuthorizationRequest.m @@ -159,32 +159,6 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration return self; } -// Deprecated -- (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 - codeVerifier:(nullable NSString *)codeVerifier - codeChallenge:(nullable NSString *)codeChallenge - codeChallengeMethod:(nullable NSString *)codeChallengeMethod - additionalParameters:(nullable NSDictionary *)additionalParameters { - return [self initWithConfiguration:configuration - clientId:clientID - clientSecret:clientSecret - scope:scope - redirectURL:redirectURL - responseType:responseType - state:state - nonce:nil - codeVerifier:codeVerifier - codeChallenge:codeChallenge - codeChallengeMethod:OIDOAuthorizationRequestCodeChallengeMethodS256 - additionalParameters:additionalParameters]; -} - - (instancetype) initWithConfiguration:(OIDServiceConfiguration *)configuration clientId:(NSString *)clientID diff --git a/Source/OIDAuthorizationService.h b/Source/OIDAuthorizationService.h index c61bbc880..64243a4a3 100644 --- a/Source/OIDAuthorizationService.h +++ b/Source/OIDAuthorizationService.h @@ -26,7 +26,6 @@ @class OIDServiceConfiguration; @class OIDTokenRequest; @class OIDTokenResponse; -@protocol OIDAuthorizationFlowSession; @protocol OIDExternalUserAgent; @protocol OIDExternalUserAgentSession; @@ -111,9 +110,6 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL completion:(OIDDiscoveryCallback)completion; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - /*! @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 @@ -123,12 +119,9 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg receives a @c OIDExternalUserAgentSession.cancel message, or after processing a @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id) - presentAuthorizationRequest:(OIDAuthorizationRequest *)request - externalUserAgent:(id)externalUserAgent - callback:(OIDAuthorizationCallback)callback; - -#pragma GCC diagnostic pop ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request + externalUserAgent:(id)externalUserAgent + callback:(OIDAuthorizationCallback)callback; /*! @brief Performs a token request. @param request The token request. diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 7381a90bc..0aa2f4fe2 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -22,7 +22,6 @@ #import "OIDAuthorizationResponse.h" #import "OIDDefines.h" #import "OIDErrorUtilities.h" -#import "OIDAuthorizationFlowSession.h" #import "OIDExternalUserAgent.h" #import "OIDExternalUserAgentSession.h" #import "OIDIDToken.h" @@ -43,18 +42,13 @@ NS_ASSUME_NONNULL_BEGIN -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -@interface OIDAuthorizationFlowSessionImplementation : NSObject { +@interface OIDAuthorizationSession : NSObject { // private variables OIDAuthorizationRequest *_request; id _externalUserAgent; OIDAuthorizationCallback _pendingauthorizationFlowCallback; } -#pragma GCC diagnostic pop - - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithRequest:(OIDAuthorizationRequest *)request @@ -62,7 +56,7 @@ - (instancetype)initWithRequest:(OIDAuthorizationRequest *)request @end -@implementation OIDAuthorizationFlowSessionImplementation +@implementation OIDAuthorizationSession - (instancetype)initWithRequest:(OIDAuthorizationRequest *)request { self = [super init]; @@ -179,14 +173,6 @@ - (void)didFinishWithResponse:(nullable OIDAuthorizationResponse *)response } } -- (void)failAuthorizationFlowWithError:(NSError *)error { - [self failExternalUserAgentFlowWithError:error]; -} - -- (BOOL)resumeAuthorizationFlowWithURL:(NSURL *)URL { - return [self resumeExternalUserAgentFlowWithURL:URL]; -} - @end @implementation OIDAuthorizationService @@ -274,24 +260,17 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL #pragma mark - Authorization Endpoint -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -+ (id) - presentAuthorizationRequest:(OIDAuthorizationRequest *)request - externalUserAgent:(id)externalUserAgent - callback:(OIDAuthorizationCallback)callback { ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request + externalUserAgent:(id)externalUserAgent + callback:(OIDAuthorizationCallback)callback { AppAuthRequestTrace(@"Authorization Request: %@", request); - OIDAuthorizationFlowSessionImplementation *flowSession = - [[OIDAuthorizationFlowSessionImplementation alloc] initWithRequest:request]; + OIDAuthorizationSession *flowSession = [[OIDAuthorizationSession alloc] initWithRequest:request]; [flowSession presentAuthorizationWithExternalUserAgent:externalUserAgent callback:callback]; return flowSession; } -#pragma GCC diagnostic pop - #pragma mark - Token Endpoint + (void)performTokenRequest:(OIDTokenRequest *)request callback:(OIDTokenCallback)callback { diff --git a/Source/OIDError.h b/Source/OIDError.h index e48813936..5131f0ad4 100644 --- a/Source/OIDError.h +++ b/Source/OIDError.h @@ -381,7 +381,7 @@ typedef NS_ENUM(NSInteger, OIDErrorCodeOAuthRegistration) { /*! @brief The exception text for the exception which occurs when a - @c OIDAuthorizationFlowSession receives a message after it has already completed. + @c OIDExternalUserAgentSession receives a message after it has already completed. */ extern NSString *const OIDOAuthExceptionInvalidAuthorizationFlow; diff --git a/Source/OIDError.m b/Source/OIDError.m index 2d23d84bf..87c8623ee 100644 --- a/Source/OIDError.m +++ b/Source/OIDError.m @@ -31,7 +31,7 @@ NSString *const OIDHTTPErrorDomain = @"org.openid.appauth.remote-http"; NSString *const OIDOAuthExceptionInvalidAuthorizationFlow = @"An OAuth redirect was sent to a " - "OIDAuthorizationFlowSession after it already completed."; + "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"; diff --git a/Source/iOS/OIDAuthState+IOS.h b/Source/iOS/OIDAuthState+IOS.h index 5b8891c7f..1a3750c5e 100644 --- a/Source/iOS/OIDAuthState+IOS.h +++ b/Source/iOS/OIDAuthState+IOS.h @@ -26,9 +26,6 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDAuthState (IOS) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - /*! @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. @param authorizationRequest The authorization request to present. @@ -39,13 +36,11 @@ NS_ASSUME_NONNULL_BEGIN receives a @c OIDExternalUserAgentSession.cancel message, or after processing a @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthStateAuthorizationCallback)callback; -#pragma GCC diagnostic pop - @end NS_ASSUME_NONNULL_END diff --git a/Source/iOS/OIDAuthState+IOS.m b/Source/iOS/OIDAuthState+IOS.m index 37f614021..5e7b1fee8 100644 --- a/Source/iOS/OIDAuthState+IOS.m +++ b/Source/iOS/OIDAuthState+IOS.m @@ -22,7 +22,7 @@ @implementation OIDAuthState (IOS) -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthStateAuthorizationCallback)callback { diff --git a/Source/iOS/OIDAuthorizationService+IOS.h b/Source/iOS/OIDAuthorizationService+IOS.h index b41729054..5cdc6cfe4 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.h +++ b/Source/iOS/OIDAuthorizationService+IOS.h @@ -27,9 +27,6 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDAuthorizationService (IOS) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - /*! @brief Perform an authorization flow using \SFSafariViewController. @param request The authorization request. @param presentingViewController The view controller from which to present the @@ -39,12 +36,9 @@ NS_ASSUME_NONNULL_BEGIN receives a @c OIDExternalUserAgentSession.cancel message, or after processing a @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id) - presentAuthorizationRequest:(OIDAuthorizationRequest *)request - presentingViewController:(UIViewController *)presentingViewController - callback:(OIDAuthorizationCallback)callback; ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request + presentingViewController:(UIViewController *)presentingViewController + callback:(OIDAuthorizationCallback)callback; @end -#pragma GCC diagnostic pop - NS_ASSUME_NONNULL_END diff --git a/Source/iOS/OIDAuthorizationService+IOS.m b/Source/iOS/OIDAuthorizationService+IOS.m index 131f60f92..7364f7f13 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.m +++ b/Source/iOS/OIDAuthorizationService+IOS.m @@ -24,10 +24,9 @@ @implementation OIDAuthorizationService (IOS) -+ (id) - presentAuthorizationRequest:(OIDAuthorizationRequest *)request - presentingViewController:(UIViewController *)presentingViewController - callback:(OIDAuthorizationCallback)callback { ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request + presentingViewController:(UIViewController *)presentingViewController + callback:(OIDAuthorizationCallback)callback { OIDExternalUserAgentIOS *externalUserAgent = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController:presentingViewController]; return [self presentAuthorizationRequest:request externalUserAgent:externalUserAgent callback:callback]; diff --git a/Source/macOS/OIDAuthState+Mac.h b/Source/macOS/OIDAuthState+Mac.h index 4e94f1f20..6f1824f33 100644 --- a/Source/macOS/OIDAuthState+Mac.h +++ b/Source/macOS/OIDAuthState+Mac.h @@ -24,9 +24,6 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDAuthState (Mac) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - /*! @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. @param authorizationRequest The authorization request to present. @@ -35,12 +32,10 @@ NS_ASSUME_NONNULL_BEGIN receives a @c OIDExternalUserAgentSession.cancel message, or after processing a @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest callback:(OIDAuthStateAuthorizationCallback)callback; -#pragma GCC diagnostic pop - @end NS_ASSUME_NONNULL_END diff --git a/Source/macOS/OIDAuthState+Mac.m b/Source/macOS/OIDAuthState+Mac.m index 7b99e734f..ce99a7370 100644 --- a/Source/macOS/OIDAuthState+Mac.m +++ b/Source/macOS/OIDAuthState+Mac.m @@ -22,7 +22,7 @@ @implementation OIDAuthState (Mac) -+ (id) ++ (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest callback:(OIDAuthStateAuthorizationCallback)callback { OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] init]; diff --git a/Source/macOS/OIDAuthorizationService+Mac.h b/Source/macOS/OIDAuthorizationService+Mac.h index 95231e0e9..405d25ce7 100644 --- a/Source/macOS/OIDAuthorizationService+Mac.h +++ b/Source/macOS/OIDAuthorizationService+Mac.h @@ -24,9 +24,6 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDAuthorizationService (Mac) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - /*! @brief Perform an authorization flow using the default browser. @param request The authorization request. @param callback The method called when the request has completed or failed. @@ -34,11 +31,8 @@ NS_ASSUME_NONNULL_BEGIN receives a @c OIDExternalUserAgentSession.cancel message, or after processing a @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message. */ -+ (id) - presentAuthorizationRequest:(OIDAuthorizationRequest *)request - callback:(OIDAuthorizationCallback)callback; - -#pragma GCC diagnostic pop ++ (id)presentAuthorizationRequest:(OIDAuthorizationRequest *)request + callback:(OIDAuthorizationCallback)callback; @end diff --git a/Source/macOS/OIDAuthorizationService+Mac.m b/Source/macOS/OIDAuthorizationService+Mac.m index 4c7892083..781394253 100644 --- a/Source/macOS/OIDAuthorizationService+Mac.m +++ b/Source/macOS/OIDAuthorizationService+Mac.m @@ -24,11 +24,12 @@ @implementation OIDAuthorizationService (Mac) -+ (id) - presentAuthorizationRequest:(OIDAuthorizationRequest *)request - callback:(OIDAuthorizationCallback)callback { ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request + callback:(OIDAuthorizationCallback)callback { OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] init]; - return [self presentAuthorizationRequest:request externalUserAgent:externalUserAgent callback:callback]; + return [self presentAuthorizationRequest:request + externalUserAgent:externalUserAgent + callback:callback]; } @end diff --git a/Source/macOS/OIDRedirectHTTPHandler.h b/Source/macOS/OIDRedirectHTTPHandler.h index bd1e977c5..a925dee89 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.h +++ b/Source/macOS/OIDRedirectHTTPHandler.h @@ -21,12 +21,8 @@ NS_ASSUME_NONNULL_BEGIN @class HTTPServer; -@protocol OIDAuthorizationFlowSession; @protocol OIDExternalUserAgentSession; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - /*! @brief Start a HTTP server on the loopback interface (i.e. @c 127.0.0.1) to receive the OAuth response redirects on macOS. */ @@ -35,7 +31,7 @@ NS_ASSUME_NONNULL_BEGIN HTTPServer *_httpServ; NSURL *_successURL; // property variables - NSObject *_currentAuthorizationFlow; + NSObject *_currentAuthorizationFlow; } /*! @brief The external user-agent request flow session which receives the return URL from the @@ -44,10 +40,7 @@ NS_ASSUME_NONNULL_BEGIN redirect handler to continue the flow. This should be set while an external user-agent request flow is in progress. */ -@property(nonatomic, strong, nullable) id currentAuthorizationFlow; - -#pragma GCC diagnostic pop +@property(nonatomic, strong, nullable) id currentAuthorizationFlow; /*! @brief Creates an a loopback HTTP redirect URI handler with the given success URL. @param successURL The URL that the user is redirected to after the external user-agent request flow completes diff --git a/Source/macOS/OIDRedirectHTTPHandler.m b/Source/macOS/OIDRedirectHTTPHandler.m index fa19d416a..8c5b21872 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.m +++ b/Source/macOS/OIDRedirectHTTPHandler.m @@ -18,7 +18,6 @@ #import "OIDRedirectHTTPHandler.h" -#import "OIDAuthorizationFlowSession.h" #import "OIDAuthorizationService.h" #import "OIDErrorUtilities.h" #import "OIDExternalUserAgentSession.h" @@ -33,7 +32,7 @@ /*! @brief Error warning that the @c currentAuthorizationFlow is not set on this object (likely a developer error, unless the user stumbled upon the loopback server before the authorization had started completely). - @description An object conforming to @c OIDAuthorizationFlowSession is returned when the + @description An object conforming to @c OIDExternalUserAgentSession is returned when the authorization is presented with @c OIDAuthorizationService::presentAuthorizationRequest:callback:. It should be set to @c currentAuthorization when using a loopback redirect. diff --git a/UnitTests/OIDRPProfileCode.m b/UnitTests/OIDRPProfileCode.m index 1cf4a127d..7011bc563 100644 --- a/UnitTests/OIDRPProfileCode.m +++ b/UnitTests/OIDRPProfileCode.m @@ -81,7 +81,7 @@ - (void)URLSession:(NSURLSession *)session } @end -@interface OIDAuthorizationFlowSessionImplementation : NSObject +@interface OIDAuthorizationSession : NSObject - (instancetype)init NS_UNAVAILABLE; From eb59c89a5ce2224f6c882eba82e394cfd591c7bf Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 13 Jul 2018 15:34:34 -0700 Subject: [PATCH 037/204] Remove support for macOS 32-bit Applications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit – Revert 8e4af3f78b70682dbff55c5801b127d86114b4a5. – Remove code to support 32-bit macOS from newer classes. 32-bit support is being discontinued by Apple ("At our Worldwide Developers Conference in 2017, Apple informed developers that macOS High Sierra would be the last version of macOS to run 32-bit apps without compromise.") https://support.apple.com/en-us/HT208436. 32-bit support places a burden on AppAuth maintainers to continue supporting it, and given it has no future there's little reason to continue maintaining that code. Developers who need 32-bit support may continue to use the last version of AppAuth which included 32-bit support. --- AppAuth.xcodeproj/project.pbxproj | 4 --- .../Example-macOS.xcodeproj/project.pbxproj | 3 --- .../Source/AppAuthExampleViewController.h | 16 +----------- .../Source/AppAuthExampleViewController.m | 14 +++-------- Examples/Example-macOS/Source/AppDelegate.h | 8 +----- Examples/Example-macOS/Source/AppDelegate.m | 7 +++--- Source/OIDAuthState.h | 25 +------------------ Source/OIDAuthState.m | 23 +++++++++-------- Source/OIDAuthorizationRequest.h | 17 ++----------- Source/OIDAuthorizationRequest.m | 13 ---------- Source/OIDAuthorizationResponse.h | 13 +--------- Source/OIDAuthorizationResponse.m | 10 -------- Source/OIDAuthorizationService.h | 5 +--- Source/OIDAuthorizationService.m | 15 +++++------ Source/OIDFieldMapping.h | 7 +----- Source/OIDFieldMapping.m | 4 --- Source/OIDIDToken.h | 12 +-------- Source/OIDIDToken.m | 9 ------- Source/OIDRegistrationRequest.h | 12 +-------- Source/OIDRegistrationRequest.m | 9 ------- Source/OIDRegistrationResponse.h | 13 +--------- Source/OIDRegistrationResponse.m | 10 -------- Source/OIDServiceConfiguration.h | 9 +------ Source/OIDServiceConfiguration.m | 6 ----- Source/OIDServiceDiscovery.h | 5 +--- Source/OIDServiceDiscovery.m | 4 ++- Source/OIDTokenRequest.h | 14 +---------- Source/OIDTokenRequest.m | 11 -------- Source/OIDTokenResponse.h | 12 +-------- Source/OIDTokenResponse.m | 9 ------- Source/OIDURLQueryComponent.h | 7 +----- Source/OIDURLQueryComponent.m | 6 ++++- .../OIDExternalUserAgentIOSCustomBrowser.m | 10 +------- Source/macOS/OIDExternalUserAgentMac.h | 6 +---- Source/macOS/OIDExternalUserAgentMac.m | 5 +++- Source/macOS/OIDRedirectHTTPHandler.h | 9 +------ Source/macOS/OIDRedirectHTTPHandler.m | 7 +++--- UnitTests/OIDAuthStateTests.h | 17 +------------ UnitTests/OIDAuthStateTests.m | 16 +++++++++++- UnitTests/OIDRPProfileCode.h | 16 ++---------- UnitTests/OIDRPProfileCode.m | 12 ++++++--- UnitTests/OIDServiceConfigurationTests.h | 7 +----- UnitTests/OIDServiceConfigurationTests.m | 6 ++++- 43 files changed, 94 insertions(+), 349 deletions(-) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 542cc7bfa..c1d1be14f 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -1908,7 +1908,6 @@ 340DAE551D58216A00EC285B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; CLANG_ANALYZER_NONNULL = YES; CODE_SIGN_IDENTITY = "-"; EXECUTABLE_PREFIX = lib; @@ -1921,7 +1920,6 @@ 340DAE561D58216A00EC285B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; CLANG_ANALYZER_NONNULL = YES; CODE_SIGN_IDENTITY = "-"; EXECUTABLE_PREFIX = lib; @@ -2398,7 +2396,6 @@ 343AAAD41E8348AA00F9D36E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CODE_SIGN_IDENTITY = "-"; @@ -2424,7 +2421,6 @@ 343AAAD51E8348AA00F9D36E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CODE_SIGN_IDENTITY = "-"; diff --git a/Examples/Example-macOS/Example-macOS.xcodeproj/project.pbxproj b/Examples/Example-macOS/Example-macOS.xcodeproj/project.pbxproj index 429bbca91..8990ba02e 100644 --- a/Examples/Example-macOS/Example-macOS.xcodeproj/project.pbxproj +++ b/Examples/Example-macOS/Example-macOS.xcodeproj/project.pbxproj @@ -442,7 +442,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7FF583C8F7036437BE8875B3 /* Pods-Example-macOS.debug.xcconfig */; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; @@ -456,7 +455,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 60553B3AB5BA3E1BB259E487 /* Pods-Example-macOS.release.xcconfig */; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; @@ -476,7 +474,6 @@ 341AA4C81E7F2E5000FCA5C6 /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; }; F6016E6B1D2AC11E003497D7 /* Build configuration list for PBXProject "Example-macOS" */ = { isa = XCConfigurationList; diff --git a/Examples/Example-macOS/Source/AppAuthExampleViewController.h b/Examples/Example-macOS/Source/AppAuthExampleViewController.h index 93c05029d..99a460a7f 100644 --- a/Examples/Example-macOS/Source/AppAuthExampleViewController.h +++ b/Examples/Example-macOS/Source/AppAuthExampleViewController.h @@ -20,26 +20,12 @@ @class AppDelegate; @class OIDAuthState; @class OIDServiceConfiguration; -@class OIDRedirectHTTPHandler; NS_ASSUME_NONNULL_BEGIN /*! @brief The example application's view controller. */ -@interface AppAuthExampleViewController : NSViewController { - // private variables - OIDRedirectHTTPHandler *_redirectHTTPHandler; - // property variables - NSButton *_authAutoButton; - NSButton *_authManual; - NSButton *_authAutoHTTPButton; - NSButton *_codeExchangeButton; - NSButton *_userinfoButton; - NSButton *_clearAuthStateButton; - NSTextView *_logTextView; - __weak AppDelegate *_appDelegate; - OIDAuthState *_authState; -} +@interface AppAuthExampleViewController : NSViewController @property(nullable) IBOutlet NSButton *authAutoButton; @property(nullable) IBOutlet NSButton *authManual; diff --git a/Examples/Example-macOS/Source/AppAuthExampleViewController.m b/Examples/Example-macOS/Source/AppAuthExampleViewController.m index 9a282caba..0cc8064b5 100644 --- a/Examples/Example-macOS/Source/AppAuthExampleViewController.m +++ b/Examples/Example-macOS/Source/AppAuthExampleViewController.m @@ -60,17 +60,9 @@ @interface AppAuthExampleViewController () @end -@implementation AppAuthExampleViewController - -@synthesize authAutoButton = _authAutoButton; -@synthesize authManual = _authManual; -@synthesize authAutoHTTPButton = _authAutoHTTPButton; -@synthesize codeExchangeButton = _codeExchangeButton; -@synthesize userinfoButton = _userinfoButton; -@synthesize clearAuthStateButton = _clearAuthStateButton; -@synthesize logTextView = _logTextView; -@synthesize appDelegate = _appDelegate; -@synthesize authState = _authState; +@implementation AppAuthExampleViewController { + OIDRedirectHTTPHandler *_redirectHTTPHandler; +} - (void)viewDidLoad { [super viewDidLoad]; diff --git a/Examples/Example-macOS/Source/AppDelegate.h b/Examples/Example-macOS/Source/AppDelegate.h index e7347493c..8d7b65f04 100644 --- a/Examples/Example-macOS/Source/AppDelegate.h +++ b/Examples/Example-macOS/Source/AppDelegate.h @@ -25,11 +25,7 @@ NS_ASSUME_NONNULL_BEGIN /*! @class AppDelegate @brief The example application's delegate. */ -@interface AppDelegate : NSObject { - // property variables - NSWindow *_window; - id _currentAuthorizationFlow; -} +@interface AppDelegate : NSObject /*! @property currentAuthorizationFlow @brief The authorization flow session which receives the return URL from the browser. @@ -38,8 +34,6 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, strong, nullable) id currentAuthorizationFlow; -@property(nullable) IBOutlet NSWindow *window; - @end NS_ASSUME_NONNULL_END diff --git a/Examples/Example-macOS/Source/AppDelegate.m b/Examples/Example-macOS/Source/AppDelegate.m index 7a8874b1b..7e5a8068b 100644 --- a/Examples/Example-macOS/Source/AppDelegate.m +++ b/Examples/Example-macOS/Source/AppDelegate.m @@ -24,10 +24,11 @@ NS_ASSUME_NONNULL_BEGIN -@implementation AppDelegate +@interface AppDelegate () +@property(nullable) IBOutlet NSWindow *window; +@end -@synthesize window = _window; -@synthesize currentAuthorizationFlow = _currentAuthorizationFlow; +@implementation AppDelegate - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { _window.title = @"AppAuth Example for macOS"; diff --git a/Source/OIDAuthState.h b/Source/OIDAuthState.h index 6af06b285..9a81b0029 100644 --- a/Source/OIDAuthState.h +++ b/Source/OIDAuthState.h @@ -51,30 +51,7 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt /*! @brief A convenience class that retains the auth state between @c OIDAuthorizationResponse%s and @c OIDTokenResponse%s. */ -@interface OIDAuthState : NSObject { - // private variables - /*! @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; - - // property variables - NSString *_refreshToken; - NSString *_scope; - OIDAuthorizationResponse *_lastAuthorizationResponse; - OIDTokenResponse *_lastTokenResponse; - OIDRegistrationResponse *_lastRegistrationResponse; - NSError *_authorizationError; - __weak id _stateChangeDelegate; - __weak id _errorDelegate; -} +@interface OIDAuthState : NSObject /*! @brief The most recent refresh token received from the server. @discussion Rather than using this property directly, you should call diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index d8c4f7566..b86206ef6 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -92,16 +92,19 @@ - (void)didChangeState; @end -@implementation OIDAuthState - -@synthesize refreshToken = _refreshToken; -@synthesize scope = _scope; -@synthesize lastAuthorizationResponse = _lastAuthorizationResponse; -@synthesize lastTokenResponse = _lastTokenResponse; -@synthesize lastRegistrationResponse = _lastRegistrationResponse; -@synthesize authorizationError = _authorizationError; -@synthesize stateChangeDelegate = _stateChangeDelegate; -@synthesize errorDelegate = _errorDelegate; +@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 diff --git a/Source/OIDAuthorizationRequest.h b/Source/OIDAuthorizationRequest.h index 121f09bba..eff3c6482 100644 --- a/Source/OIDAuthorizationRequest.h +++ b/Source/OIDAuthorizationRequest.h @@ -38,21 +38,8 @@ extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256; @see https://tools.ietf.org/html/rfc6749#section-4 @see https://tools.ietf.org/html/rfc6749#section-4.1.1 */ -@interface OIDAuthorizationRequest : NSObject { - // property variables - OIDServiceConfiguration *_configuration; - NSString *_responseType; - NSString *_clientID; - NSString *_clientSecret; - NSString *_scope; - NSURL *_redirectURL; - NSString *_state; - NSString *_nonce; - NSString *_codeVerifier; - NSString *_codeChallenge; - NSString *_codeChallengeMethod; - NSDictionary *_additionalParameters; -} +@interface OIDAuthorizationRequest : + NSObject /*! @brief The service's configuration. @remarks This configuration specifies how to connect to a particular OAuth provider. diff --git a/Source/OIDAuthorizationRequest.m b/Source/OIDAuthorizationRequest.m index 0ffc7b586..72873025c 100644 --- a/Source/OIDAuthorizationRequest.m +++ b/Source/OIDAuthorizationRequest.m @@ -95,19 +95,6 @@ @implementation OIDAuthorizationRequest -@synthesize configuration = _configuration; -@synthesize responseType = _responseType; -@synthesize clientID = _clientID; -@synthesize clientSecret = _clientSecret; -@synthesize scope = _scope; -@synthesize redirectURL = _redirectURL; -@synthesize state = _state; -@synthesize nonce = _nonce; -@synthesize codeVerifier = _codeVerifier; -@synthesize codeChallenge = _codeChallenge; -@synthesize codeChallengeMethod = _codeChallengeMethod; -@synthesize additionalParameters = _additionalParameters; - - (instancetype)init OID_UNAVAILABLE_USE_INITIALIZER( @selector(initWithConfiguration: diff --git a/Source/OIDAuthorizationResponse.h b/Source/OIDAuthorizationResponse.h index 57b3af7b2..8aa7d06f5 100644 --- a/Source/OIDAuthorizationResponse.h +++ b/Source/OIDAuthorizationResponse.h @@ -28,18 +28,7 @@ NS_ASSUME_NONNULL_BEGIN @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 { - // property variables - OIDAuthorizationRequest *_request; - NSString *_authorizationCode; - NSString *_state; - NSString *_accessToken; - NSDate *_accessTokenExpirationDate; - NSString *_tokenType; - NSString *_idToken; - NSString *_scope; - NSDictionary *> *_additionalParameters; -} +@interface OIDAuthorizationResponse : NSObject /*! @brief The request which was serviced. */ diff --git a/Source/OIDAuthorizationResponse.m b/Source/OIDAuthorizationResponse.m index 3ba07502e..a8f92c75e 100644 --- a/Source/OIDAuthorizationResponse.m +++ b/Source/OIDAuthorizationResponse.m @@ -74,16 +74,6 @@ @implementation OIDAuthorizationResponse -@synthesize request = _request; -@synthesize authorizationCode = _authorizationCode; -@synthesize state = _state; -@synthesize accessToken = _accessToken; -@synthesize accessTokenExpirationDate = _accessTokenExpirationDate; -@synthesize tokenType = _tokenType; -@synthesize idToken = _idToken; -@synthesize scope = _scope; -@synthesize additionalParameters = _additionalParameters; - /*! @brief Returns a mapping of incoming parameters to instance variables. @return A mapping of incoming parameters to instance variables. */ diff --git a/Source/OIDAuthorizationService.h b/Source/OIDAuthorizationService.h index 64243a4a3..31c790311 100644 --- a/Source/OIDAuthorizationService.h +++ b/Source/OIDAuthorizationService.h @@ -71,10 +71,7 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg /*! @brief Performs various OAuth and OpenID Connect related calls via the user agent or \NSURLSession. */ -@interface OIDAuthorizationService : NSObject { - // property variables - OIDServiceConfiguration *_configuration; -} +@interface OIDAuthorizationService : NSObject /*! @brief The service's configuration. @remarks Each authorization service is initialized with a configuration. This configuration diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 0aa2f4fe2..2ce1c07a2 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -42,12 +42,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface OIDAuthorizationSession : NSObject { - // private variables - OIDAuthorizationRequest *_request; - id _externalUserAgent; - OIDAuthorizationCallback _pendingauthorizationFlowCallback; -} +@interface OIDAuthorizationSession : NSObject - (instancetype)init NS_UNAVAILABLE; @@ -56,7 +51,11 @@ - (instancetype)initWithRequest:(OIDAuthorizationRequest *)request @end -@implementation OIDAuthorizationSession +@implementation OIDAuthorizationSession { + OIDAuthorizationRequest *_request; + id _externalUserAgent; + OIDAuthorizationCallback _pendingauthorizationFlowCallback; +} - (instancetype)initWithRequest:(OIDAuthorizationRequest *)request { self = [super init]; @@ -177,8 +176,6 @@ - (void)didFinishWithResponse:(nullable OIDAuthorizationResponse *)response @implementation OIDAuthorizationService -@synthesize configuration = _configuration; - + (void)discoverServiceConfigurationForIssuer:(NSURL *)issuerURL completion:(OIDDiscoveryCallback)completion { NSURL *fullDiscoveryURL = diff --git a/Source/OIDFieldMapping.h b/Source/OIDFieldMapping.h index 22d915924..f0a56fef3 100644 --- a/Source/OIDFieldMapping.h +++ b/Source/OIDFieldMapping.h @@ -28,12 +28,7 @@ typedef _Nullable id(^OIDFieldMappingConversionFunction)(NSObject *_Nullable val /*! @brief Describes the mapping of a key/value pair to an iVar with an optional conversion function. */ -@interface OIDFieldMapping : NSObject { - // property variables - NSString *_name; - Class _expectedType; - OIDFieldMappingConversionFunction _conversion; -} +@interface OIDFieldMapping : NSObject /*! @brief The name of the instance variable the field should be mapped to. */ diff --git a/Source/OIDFieldMapping.m b/Source/OIDFieldMapping.m index eaad82c78..f84365602 100644 --- a/Source/OIDFieldMapping.m +++ b/Source/OIDFieldMapping.m @@ -22,10 +22,6 @@ @implementation OIDFieldMapping -@synthesize name = _name; -@synthesize expectedType = _expectedType; -@synthesize conversion = _conversion; - - (nonnull instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithName:type:conversion:)) diff --git a/Source/OIDIDToken.h b/Source/OIDIDToken.h index d39f3802e..6fe84d7fe 100644 --- a/Source/OIDIDToken.h +++ b/Source/OIDIDToken.h @@ -29,17 +29,7 @@ NS_ASSUME_NONNULL_BEGIN @see https://tools.ietf.org/html/rfc7519 @see https://jwt.io/ */ -@interface OIDIDToken : NSObject { - // property variables - NSDictionary *_header; - NSDictionary *_claims; - NSURL *_issuer; - NSString *_subject; - NSArray *_audience; - NSDate *_expiresAt; - NSDate *_issuedAt; - NSString *_nonce; -} +@interface OIDIDToken : NSObject /*! @internal @brief Unavailable. Please use @c initWithAuthorizationResponse:. diff --git a/Source/OIDIDToken.m b/Source/OIDIDToken.m index 94989d323..57a7324e8 100644 --- a/Source/OIDIDToken.m +++ b/Source/OIDIDToken.m @@ -30,15 +30,6 @@ @implementation OIDIDToken -@synthesize header = _header; -@synthesize claims = _claims; -@synthesize issuer = _issuer; -@synthesize subject = _subject; -@synthesize audience = _audience; -@synthesize expiresAt = _expiresAt; -@synthesize issuedAt = _issuedAt; -@synthesize nonce = _nonce; - - (instancetype)initWithIDTokenString:(NSString *)idToken { self = [super init]; NSArray *sections = [idToken componentsSeparatedByString:@"."]; diff --git a/Source/OIDRegistrationRequest.h b/Source/OIDRegistrationRequest.h index 47cf06357..dac8d287c 100644 --- a/Source/OIDRegistrationRequest.h +++ b/Source/OIDRegistrationRequest.h @@ -26,17 +26,7 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief Represents a registration request. @see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest */ -@interface OIDRegistrationRequest : NSObject { - // property variables - OIDServiceConfiguration *_configuration; - NSString *_applicationType; - NSArray *_redirectURIs; - NSArray *_responseTypes; - NSArray *_grantTypes; - NSString *_subjectType; - NSString *_tokenEndpointAuthenticationMethod; - NSDictionary *_additionalParameters; -} +@interface OIDRegistrationRequest : NSObject /*! @brief The service's configuration. @remarks This configuration specifies how to connect to a particular OAuth provider. diff --git a/Source/OIDRegistrationRequest.m b/Source/OIDRegistrationRequest.m index bf3899bca..cd6ba245d 100644 --- a/Source/OIDRegistrationRequest.m +++ b/Source/OIDRegistrationRequest.m @@ -49,15 +49,6 @@ @implementation OIDRegistrationRequest -@synthesize configuration = _configuration; -@synthesize applicationType = _applicationType; -@synthesize redirectURIs = _redirectURIs; -@synthesize responseTypes = _responseTypes; -@synthesize grantTypes = _grantTypes; -@synthesize subjectType = _subjectType; -@synthesize tokenEndpointAuthenticationMethod = _tokenEndpointAuthenticationMethod; -@synthesize additionalParameters = _additionalParameters; - #pragma mark - Initializers - (instancetype)init diff --git a/Source/OIDRegistrationResponse.h b/Source/OIDRegistrationResponse.h index f7fec7716..df6239064 100644 --- a/Source/OIDRegistrationResponse.h +++ b/Source/OIDRegistrationResponse.h @@ -50,18 +50,7 @@ extern NSString *const OIDRegistrationClientURIParam; /*! @brief Represents a registration response. @see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse */ -@interface OIDRegistrationResponse : NSObject { - // property variables - OIDRegistrationRequest *_request; - NSString *_clientID; - NSDate *_clientIDIssuedAt; - NSString *_clientSecret; - NSDate *_clientSecretExpiresAt; - NSString *_registrationAccessToken; - NSURL *_registrationClientURI; - NSString *_tokenEndpointAuthenticationMethod; - NSDictionary *> *_additionalParameters; -} +@interface OIDRegistrationResponse : NSObject /*! @brief The request which was serviced. */ diff --git a/Source/OIDRegistrationResponse.m b/Source/OIDRegistrationResponse.m index c9aca6971..ec0411b79 100644 --- a/Source/OIDRegistrationResponse.m +++ b/Source/OIDRegistrationResponse.m @@ -41,16 +41,6 @@ @implementation OIDRegistrationResponse -@synthesize request = _request; -@synthesize clientID = _clientID; -@synthesize clientIDIssuedAt = _clientIDIssuedAt; -@synthesize clientSecret = _clientSecret; -@synthesize clientSecretExpiresAt = _clientSecretExpiresAt; -@synthesize registrationAccessToken = _registrationAccessToken; -@synthesize registrationClientURI = _registrationClientURI; -@synthesize tokenEndpointAuthenticationMethod = _tokenEndpointAuthenticationMethod; -@synthesize additionalParameters = _additionalParameters; - /*! @brief Returns a mapping of incoming parameters to instance variables. @return A mapping of incoming parameters to instance variables. */ diff --git a/Source/OIDServiceConfiguration.h b/Source/OIDServiceConfiguration.h index 8c33698e4..2221a1da7 100644 --- a/Source/OIDServiceConfiguration.h +++ b/Source/OIDServiceConfiguration.h @@ -32,14 +32,7 @@ typedef void (^OIDServiceConfigurationCreated) /*! @brief Represents the information needed to construct a @c OIDAuthorizationService. */ -@interface OIDServiceConfiguration : NSObject { - // property variables - NSURL *_authorizationEndpoint; - NSURL *_tokenEndpoint; - NSURL *_issuer; - NSURL *_registrationEndpoint; - OIDServiceDiscovery *_discoveryDocument; -} +@interface OIDServiceConfiguration : NSObject /*! @brief The authorization endpoint URI. */ diff --git a/Source/OIDServiceConfiguration.m b/Source/OIDServiceConfiguration.m index d858dc9f8..517c955bd 100644 --- a/Source/OIDServiceConfiguration.m +++ b/Source/OIDServiceConfiguration.m @@ -57,12 +57,6 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint @implementation OIDServiceConfiguration -@synthesize authorizationEndpoint = _authorizationEndpoint; -@synthesize tokenEndpoint = _tokenEndpoint; -@synthesize issuer = _issuer; -@synthesize registrationEndpoint = _registrationEndpoint; -@synthesize discoveryDocument = _discoveryDocument; - - (instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector( initWithAuthorizationEndpoint: diff --git a/Source/OIDServiceDiscovery.h b/Source/OIDServiceDiscovery.h index e3c8eda90..982d71005 100644 --- a/Source/OIDServiceDiscovery.h +++ b/Source/OIDServiceDiscovery.h @@ -23,10 +23,7 @@ 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 { - // private variables - NSDictionary *_discoveryDictionary; -} +@interface OIDServiceDiscovery : NSObject /*! @brief The decoded OpenID Connect 1.0 Discovery Document as a dictionary. */ diff --git a/Source/OIDServiceDiscovery.m b/Source/OIDServiceDiscovery.m index 195c072e1..ca9628ef1 100644 --- a/Source/OIDServiceDiscovery.m +++ b/Source/OIDServiceDiscovery.m @@ -71,7 +71,9 @@ static NSString *const kOPPolicyURIKey = @"op_policy_uri"; static NSString *const kOPTosURIKey = @"op_tos_uri"; -@implementation OIDServiceDiscovery +@implementation OIDServiceDiscovery { + NSDictionary *_discoveryDictionary; +} - (nonnull instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithDictionary:error:)) diff --git a/Source/OIDTokenRequest.h b/Source/OIDTokenRequest.h index 85958c6f1..5cf9835c2 100644 --- a/Source/OIDTokenRequest.h +++ b/Source/OIDTokenRequest.h @@ -31,19 +31,7 @@ NS_ASSUME_NONNULL_BEGIN @see https://tools.ietf.org/html/rfc6749#section-3.2 @see https://tools.ietf.org/html/rfc6749#section-4.1.3 */ -@interface OIDTokenRequest : NSObject { - // property variables - OIDServiceConfiguration *_configuration; - NSString *_grantType; - NSString *_authorizationCode; - NSURL *_redirectURL; - NSString *_clientID; - NSString *_clientSecret; - NSString *_scope; - NSString *_refreshToken; - NSString *_codeVerifier; - NSDictionary *_additionalParameters; -} +@interface OIDTokenRequest : NSObject /*! @brief The service's configuration. @remarks This configuration specifies how to connect to a particular OAuth provider. diff --git a/Source/OIDTokenRequest.m b/Source/OIDTokenRequest.m index e188555c3..bd27dd480 100644 --- a/Source/OIDTokenRequest.m +++ b/Source/OIDTokenRequest.m @@ -69,17 +69,6 @@ @implementation OIDTokenRequest -@synthesize configuration = _configuration; -@synthesize grantType = _grantType; -@synthesize authorizationCode = _authorizationCode; -@synthesize redirectURL = _redirectURL; -@synthesize clientID = _clientID; -@synthesize clientSecret = _clientSecret; -@synthesize scope = _scope; -@synthesize refreshToken = _refreshToken; -@synthesize codeVerifier = _codeVerifier; -@synthesize additionalParameters = _additionalParameters; - - (instancetype)init OID_UNAVAILABLE_USE_INITIALIZER( @selector(initWithConfiguration: diff --git a/Source/OIDTokenResponse.h b/Source/OIDTokenResponse.h index fcad9dde1..b446e944a 100644 --- a/Source/OIDTokenResponse.h +++ b/Source/OIDTokenResponse.h @@ -26,17 +26,7 @@ NS_ASSUME_NONNULL_BEGIN @see https://tools.ietf.org/html/rfc6749#section-3.2 @see https://tools.ietf.org/html/rfc6749#section-4.1.3 */ -@interface OIDTokenResponse : NSObject { - // property variables - OIDTokenRequest *_request; - NSString *_accessToken; - NSDate *_accessTokenExpirationDate; - NSString *_tokenType; - NSString *_idToken; - NSString *_refreshToken; - NSString *_scope; - NSDictionary *> *_additionalParameters; -} +@interface OIDTokenResponse : NSObject /*! @brief The request which was serviced. */ diff --git a/Source/OIDTokenResponse.m b/Source/OIDTokenResponse.m index 1e4eeef91..6995fb914 100644 --- a/Source/OIDTokenResponse.m +++ b/Source/OIDTokenResponse.m @@ -61,15 +61,6 @@ @implementation OIDTokenResponse -@synthesize request = _request; -@synthesize accessToken = _accessToken; -@synthesize accessTokenExpirationDate = _accessTokenExpirationDate; -@synthesize tokenType = _tokenType; -@synthesize idToken = _idToken; -@synthesize refreshToken = _refreshToken; -@synthesize scope = _scope; -@synthesize additionalParameters = _additionalParameters; - /*! @brief Returns a mapping of incoming parameters to instance variables. @return A mapping of incoming parameters to instance variables. */ diff --git a/Source/OIDURLQueryComponent.h b/Source/OIDURLQueryComponent.h index 2a87d4f81..673d2db12 100644 --- a/Source/OIDURLQueryComponent.h +++ b/Source/OIDURLQueryComponent.h @@ -30,12 +30,7 @@ extern BOOL gOIDURLQueryComponentForceIOS7Handling; /*! @brief A utility class for creating and parsing URL query components. */ -@interface OIDURLQueryComponent : NSObject { - // private variables - /*! @brief A dictionary of parameter names and values representing the contents of the query. - */ - NSMutableDictionary *> *_parameters; -} +@interface OIDURLQueryComponent : NSObject /*! @brief The parameter names in the query. */ diff --git a/Source/OIDURLQueryComponent.m b/Source/OIDURLQueryComponent.m index 97fb1e295..f5b07b16c 100644 --- a/Source/OIDURLQueryComponent.m +++ b/Source/OIDURLQueryComponent.m @@ -26,7 +26,11 @@ */ static NSString *const kQueryStringParamAdditionalDisallowedCharacters = @"=&+"; -@implementation OIDURLQueryComponent +@implementation OIDURLQueryComponent { + /*! @brief A dictionary of parameter names and values representing the contents of the query. + */ + NSMutableDictionary *> *_parameters; +} - (nullable instancetype)init { self = [super init]; diff --git a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m index 30c4aa7bd..7bca4c7e0 100644 --- a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m +++ b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m @@ -27,15 +27,7 @@ NS_ASSUME_NONNULL_BEGIN -@implementation OIDExternalUserAgentIOSCustomBrowser { - OIDCustomBrowserURLTransformation _URLTransformation; - NSString *_canOpenURLScheme; - NSURL *_appStoreURL; -} - -@synthesize URLTransformation = _URLTransformation; -@synthesize canOpenURLScheme = _canOpenURLScheme; -@synthesize appStoreURL = _appStoreURL; +@implementation OIDExternalUserAgentIOSCustomBrowser + (instancetype)CustomBrowserChrome { // Chrome iOS documentation: https://developer.chrome.com/multidevice/ios/links diff --git a/Source/macOS/OIDExternalUserAgentMac.h b/Source/macOS/OIDExternalUserAgentMac.h index eea1bf55e..ed2d2c276 100644 --- a/Source/macOS/OIDExternalUserAgentMac.h +++ b/Source/macOS/OIDExternalUserAgentMac.h @@ -23,11 +23,7 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief A Mac-specific external user-agent UI Coordinator that uses the default browser to present an external user-agent request. */ -@interface OIDExternalUserAgentMac : NSObject { - // private variables - BOOL _externalUserAgentFlowInProgress; - __weak id _session; -} +@interface OIDExternalUserAgentMac : NSObject @end diff --git a/Source/macOS/OIDExternalUserAgentMac.m b/Source/macOS/OIDExternalUserAgentMac.m index 37e67c166..d064f406a 100644 --- a/Source/macOS/OIDExternalUserAgentMac.m +++ b/Source/macOS/OIDExternalUserAgentMac.m @@ -26,7 +26,10 @@ NS_ASSUME_NONNULL_BEGIN -@implementation OIDExternalUserAgentMac +@implementation OIDExternalUserAgentMac { + BOOL _externalUserAgentFlowInProgress; + __weak id _session; +} - (BOOL)presentExternalUserAgentRequest:(id)request session:(id)session { diff --git a/Source/macOS/OIDRedirectHTTPHandler.h b/Source/macOS/OIDRedirectHTTPHandler.h index a925dee89..50b6584f6 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.h +++ b/Source/macOS/OIDRedirectHTTPHandler.h @@ -20,19 +20,12 @@ NS_ASSUME_NONNULL_BEGIN -@class HTTPServer; @protocol OIDExternalUserAgentSession; /*! @brief Start a HTTP server on the loopback interface (i.e. @c 127.0.0.1) to receive the OAuth response redirects on macOS. */ -@interface OIDRedirectHTTPHandler : NSObject { - // private variables - HTTPServer *_httpServ; - NSURL *_successURL; - // property variables - NSObject *_currentAuthorizationFlow; -} +@interface OIDRedirectHTTPHandler : NSObject /*! @brief The external user-agent request flow session which receives the return URL from the browser. diff --git a/Source/macOS/OIDRedirectHTTPHandler.m b/Source/macOS/OIDRedirectHTTPHandler.m index 8c5b21872..28953ad07 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.m +++ b/Source/macOS/OIDRedirectHTTPHandler.m @@ -47,9 +47,10 @@ static NSString *const kHTMLErrorRedirectNotValid = @"AppAuth Error: Not a valid redirect."; -@implementation OIDRedirectHTTPHandler - -@synthesize currentAuthorizationFlow = _currentAuthorizationFlow; +@implementation OIDRedirectHTTPHandler { + HTTPServer *_httpServ; + NSURL *_successURL; +} - (instancetype)init { return [self initWithSuccessURL:nil]; diff --git a/UnitTests/OIDAuthStateTests.h b/UnitTests/OIDAuthStateTests.h index 66acd82da..5be7d6059 100644 --- a/UnitTests/OIDAuthStateTests.h +++ b/UnitTests/OIDAuthStateTests.h @@ -27,22 +27,7 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief Unit tests for @c OIDAuthState. */ -@interface OIDAuthStateTests : XCTestCase { - // private variables - /*! @brief An expectation for tests waiting on OIDAuthStateChangeDelegate.didChangeState:. - */ - XCTestExpectation *_didChangeStateExpectation; - - /*! @brief An expectation for tests waiting on - OIDAuthStateErrorDelegate.didEncounterAuthorizationError:. - */ - XCTestExpectation *_didEncounterAuthorizationErrorExpectation; - - /*! @brief An expectation for tests waiting on - OIDAuthStateErrorDelegate.didEncounterTransientError:. - */ - XCTestExpectation *_didEncounterTransientErrorExpectation; -} +@interface OIDAuthStateTests : XCTestCase /*! @brief Creates a new @c OIDAuthState for testing. */ diff --git a/UnitTests/OIDAuthStateTests.m b/UnitTests/OIDAuthStateTests.m index e159cf0b3..97d3caa01 100644 --- a/UnitTests/OIDAuthStateTests.m +++ b/UnitTests/OIDAuthStateTests.m @@ -41,7 +41,21 @@ - (BOOL)isTokenFresh; @interface OIDAuthStateTests () @end -@implementation OIDAuthStateTests +@implementation OIDAuthStateTests { + /*! @brief An expectation for tests waiting on OIDAuthStateChangeDelegate.didChangeState:. + */ + XCTestExpectation *_didChangeStateExpectation; + + /*! @brief An expectation for tests waiting on + OIDAuthStateErrorDelegate.didEncounterAuthorizationError:. + */ + XCTestExpectation *_didEncounterAuthorizationErrorExpectation; + + /*! @brief An expectation for tests waiting on + OIDAuthStateErrorDelegate.didEncounterTransientError:. + */ + XCTestExpectation *_didEncounterTransientErrorExpectation; +} + (OIDAuthState *)testInstance { OIDAuthorizationResponse *authorizationResponse = diff --git a/UnitTests/OIDRPProfileCode.h b/UnitTests/OIDRPProfileCode.h index 07ef3e8c6..c613b39df 100644 --- a/UnitTests/OIDRPProfileCode.h +++ b/UnitTests/OIDRPProfileCode.h @@ -22,19 +22,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface OIDAuthorizationUICoordinatorNonInteractive : NSObject { - // private variables - NSURLSession *_urlSession; - __weak id _session; -} -@end - -@interface OIDRPProfileCode : XCTestCase { - // private variables - OIDAuthorizationUICoordinatorNonInteractive *_coordinator; - FILE * _logFile; -} - -@end + + NS_ASSUME_NONNULL_END diff --git a/UnitTests/OIDRPProfileCode.m b/UnitTests/OIDRPProfileCode.m index 7011bc563..0bb61fe95 100644 --- a/UnitTests/OIDRPProfileCode.m +++ b/UnitTests/OIDRPProfileCode.m @@ -42,7 +42,10 @@ Simply performs the authorization request as a GET request, and looks for a redirect in the response. */ -@interface OIDAuthorizationUICoordinatorNonInteractive () +@interface OIDAuthorizationUICoordinatorNonInteractive : NSObject { + NSURLSession *_urlSession; + __weak id _session; +} @end @implementation OIDAuthorizationUICoordinatorNonInteractive @@ -90,8 +93,11 @@ - (instancetype)initWithRequest:(OIDAuthorizationRequest *)request @end -@interface OIDRPProfileCode () - +@interface OIDRPProfileCode : XCTestCase { + // private variables + OIDAuthorizationUICoordinatorNonInteractive *_coordinator; + FILE * _logFile; +} typedef void (^PostRegistrationCallback)(OIDServiceConfiguration *configuration, OIDRegistrationResponse *registrationResponse, NSError *error diff --git a/UnitTests/OIDServiceConfigurationTests.h b/UnitTests/OIDServiceConfigurationTests.h index e45184db7..982db2b5b 100644 --- a/UnitTests/OIDServiceConfigurationTests.h +++ b/UnitTests/OIDServiceConfigurationTests.h @@ -24,12 +24,7 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief Unit tests for @c OIDServiceConfiguration. */ -@interface OIDServiceConfigurationTests : XCTestCase { - // private variables - /*! @brief A list of tasks to perform during tearDown. - */ - NSMutableArray *_teardownTasks; -} +@interface OIDServiceConfigurationTests : XCTestCase /*! @brief Creates a new @c OIDServiceConfiguration for testing. */ diff --git a/UnitTests/OIDServiceConfigurationTests.m b/UnitTests/OIDServiceConfigurationTests.m index a15cbd6b8..ff7e30932 100644 --- a/UnitTests/OIDServiceConfigurationTests.m +++ b/UnitTests/OIDServiceConfigurationTests.m @@ -81,7 +81,11 @@ typedef void(^DataTaskWithURLCompletionHandler)(NSData *_Nullable data, @"https://accounts.google.com/.well-known/openid-configuration"; -@implementation OIDServiceConfigurationTests +@implementation OIDServiceConfigurationTests { + /*! @brief A list of tasks to perform during tearDown. + */ + NSMutableArray *_teardownTasks; +} + (OIDServiceConfiguration *)testInstance { NSURL *authEndpoint = [NSURL URLWithString:kInitializerTestAuthEndpoint]; From b4ca39a38f0d991bd17c56ebd01756a41da54061 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 18 Jul 2018 17:06:34 -0700 Subject: [PATCH 038/204] Allow user to specify dispatch queue for the `performActionWithFreshTokens` method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To date, all AppAuth blocks execute on the main queue, including the user's own actions. This change allows the user to specify the queue that the action will be scheduled on in `performActionWithFreshTokens`. Unlike the rest of AppAuth's callbacks, this one is called frequently – for every API call – thus it is important for the user to determine on which dispatch queue it executes. OIDAuthState itself is not yet thread-safe, so all calls, including `performActionWithFreshTokens` should be performed on the main thread. This change also removes an unneeded duplicate dispatch on the main thread after token refresh (the callback is already dispatched on the main thread). --- Source/OIDAuthState.h | 13 +++++++ Source/OIDAuthState.m | 81 +++++++++++++++++++++++++++++-------------- 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/Source/OIDAuthState.h b/Source/OIDAuthState.h index 9a81b0029..27e55f14c 100644 --- a/Source/OIDAuthState.h +++ b/Source/OIDAuthState.h @@ -225,6 +225,19 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt additionalRefreshParameters: (nullable NSDictionary *)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 *)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. */ diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index b86206ef6..a1d6f668b 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -65,6 +65,23 @@ */ 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. @@ -440,9 +457,19 @@ - (void)performActionWithFreshTokens:(OIDAuthStateAction)action { - (void)performActionWithFreshTokens:(OIDAuthStateAction)action additionalRefreshParameters: (nullable NSDictionary *)additionalParameters { + [self performActionWithFreshTokens:action + additionalRefreshParameters:additionalParameters + dispatchQueue:dispatch_get_main_queue()]; +} + +- (void)performActionWithFreshTokens:(OIDAuthStateAction)action + additionalRefreshParameters: + (nullable NSDictionary *)additionalParameters + dispatchQueue:(dispatch_queue_t)dispatchQueue { + if ([self isTokenFresh]) { // access token is valid within tolerance levels, perform action - dispatch_async(dispatch_get_main_queue(), ^() { + dispatch_async(dispatchQueue, ^{ action(self.accessToken, self.idToken, nil); }); return; @@ -454,7 +481,7 @@ - (void)performActionWithFreshTokens:(OIDAuthStateAction)action OIDErrorUtilities errorWithCode:OIDErrorCodeTokenRefreshError underlyingError:nil description:@"Unable to refresh expired token without a refresh token."]; - dispatch_async(dispatch_get_main_queue(), ^() { + dispatch_async(dispatchQueue, ^{ action(nil, nil, tokenRefreshError); }); return; @@ -462,15 +489,17 @@ - (void)performActionWithFreshTokens:(OIDAuthStateAction)action // 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:action]; + [_pendingActions addObject:pendingAction]; return; } // creates a list of pending actions, starting with this one - _pendingActions = [NSMutableArray arrayWithObject:action]; + _pendingActions = [NSMutableArray arrayWithObject:pendingAction]; } // refresh the tokens @@ -480,33 +509,33 @@ - (void)performActionWithFreshTokens:(OIDAuthStateAction)action originalAuthorizationResponse:_lastAuthorizationResponse callback:^(OIDTokenResponse *_Nullable response, NSError *_Nullable error) { - dispatch_async(dispatch_get_main_queue(), ^() { - // update OIDAuthState based on response - if (response) { + // update OIDAuthState based on response + if (response) { + self->_needsTokenRefresh = NO; + [self updateWithTokenResponse:response error:nil]; + } else { + if (error.domain == OIDOAuthTokenErrorDomain) { self->_needsTokenRefresh = NO; - [self updateWithTokenResponse:response error:nil]; + [self updateWithAuthorizationError:error]; } 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]; - } + 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 (OIDAuthStateAction actionToProcess in actionsToProcess) { - actionToProcess(self.accessToken, self.idToken, 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); + }); + } }]; } From 87b0823ced25ecfbed07a36adddd508281bfe546 Mon Sep 17 00:00:00 2001 From: Joseph Heenan Date: Thu, 13 Sep 2018 17:02:50 +0200 Subject: [PATCH 039/204] Fix typo (sha265 -> sha256) --- Source/OIDAuthorizationRequest.m | 2 +- Source/OIDTokenUtilities.h | 2 +- Source/OIDTokenUtilities.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/OIDAuthorizationRequest.m b/Source/OIDAuthorizationRequest.m index 72873025c..05442da80 100644 --- a/Source/OIDAuthorizationRequest.m +++ b/Source/OIDAuthorizationRequest.m @@ -290,7 +290,7 @@ + (nullable NSString *)codeChallengeS256ForVerifier:(NSString *)codeVerifier { // 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 sha265:codeVerifier]; + NSData *sha256Verifier = [OIDTokenUtilities sha256:codeVerifier]; return [OIDTokenUtilities encodeBase64urlNoPadding:sha256Verifier]; } diff --git a/Source/OIDTokenUtilities.h b/Source/OIDTokenUtilities.h index cc54f6888..fda898546 100644 --- a/Source/OIDTokenUtilities.h +++ b/Source/OIDTokenUtilities.h @@ -48,7 +48,7 @@ NS_ASSUME_NONNULL_BEGIN @param inputString The input string. @return The SHA256 data. */ -+ (NSData *)sha265:(NSString *)inputString; ++ (NSData *)sha256:(NSString *)inputString; /*! @brief Truncated intput string after first 6 characters followed by ellipses @param inputString The input string. diff --git a/Source/OIDTokenUtilities.m b/Source/OIDTokenUtilities.m index 3ca46a5ee..3280c856c 100644 --- a/Source/OIDTokenUtilities.m +++ b/Source/OIDTokenUtilities.m @@ -47,7 +47,7 @@ + (nullable NSString *)randomURLSafeStringWithSize:(NSUInteger)size { return [[self class] encodeBase64urlNoPadding:randomData]; } -+ (NSData *)sha265:(NSString *)inputString { ++ (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); From 086e4b0033564ea4356dff0270ce3f99cacca544 Mon Sep 17 00:00:00 2001 From: Eric DeLabar Date: Wed, 19 Sep 2018 18:00:07 -0400 Subject: [PATCH 040/204] Run xcode 10 migration wizard --- AppAuth.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++++++++ .../xcshareddata/xcschemes/AppAuth-iOS.xcscheme | 4 +--- .../xcshareddata/xcschemes/AppAuth-macOS.xcscheme | 4 +--- .../xcshareddata/xcschemes/AppAuth-tvOS.xcscheme | 4 +--- .../xcshareddata/xcschemes/AppAuth-watchOS.xcscheme | 4 +--- .../xcshareddata/xcschemes/AppAuth_iOS.xcscheme | 4 +--- .../xcshareddata/xcschemes/AppAuth_macOS.xcscheme | 4 +--- .../xcshareddata/xcschemes/AppAuth_tvOS.xcscheme | 4 +--- .../xcshareddata/xcschemes/AppAuth_watchOS.xcscheme | 4 +--- 10 files changed, 17 insertions(+), 25 deletions(-) create mode 100644 AppAuth.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index c1d1be14f..580eb5428 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -1318,7 +1318,7 @@ 340E73741C5D819B0076B1F6 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0900; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "OpenID Foundation"; TargetAttributes = { 340DAE4D1D58216A00EC285B = { diff --git a/AppAuth.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/AppAuth.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/AppAuth.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-iOS.xcscheme b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-iOS.xcscheme index 3685f59f8..5c03fe8e1 100644 --- a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-iOS.xcscheme +++ b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-iOS.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_iOS.xcscheme b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_iOS.xcscheme index 717f71b1b..dbbb00b1d 100644 --- a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_iOS.xcscheme +++ b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_iOS.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" From 960c2ab0b0c9109c39372b6ef801018cc97a1e0e Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 19 Sep 2018 15:26:45 -0700 Subject: [PATCH 041/204] Update Travis config for Xcode 10 / iOS 12 --- .travis.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index f0b0f9039..a1ceff4d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,17 @@ # Travis CI config for AppAuth # Use the `wwtd` gem to test locally. language: objective-c -osx_image: xcode9 +osx_image: xcode10 # Tests iOS, macOS and tvOS: both static library and framework schemes. env: # To generate this list, use `xcodebuild -showsdks` to get possible SDK values, and platforms. # Use `instruments -s devices` to get the device name. - - SCHEME=AppAuth-iOS DESTINATION="'platform=iOS Simulator,name=iPhone 6,OS=11.0'" SDK=iphonesimulator11.0 - - SCHEME=AppAuth_iOS DESTINATION="'platform=iOS Simulator,name=iPhone 6,OS=11.0'" SDK=iphonesimulator11.0 - - SCHEME=AppAuth-macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.13 - - SCHEME=AppAuth_macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.13 - - SCHEME=AppAuth-tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=11.0'" SDK=appletvsimulator11.0 - - SCHEME=AppAuth_tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=11.0'" SDK=appletvsimulator11.0 + - SCHEME=AppAuth-iOS DESTINATION="'platform=iOS Simulator,name=iPhone 6,OS=12.0'" SDK=iphonesimulator12.0 + - SCHEME=AppAuth_iOS DESTINATION="'platform=iOS Simulator,name=iPhone 6,OS=12.0'" SDK=iphonesimulator12.0 + - SCHEME=AppAuth-macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.14 + - SCHEME=AppAuth_macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.14 + - SCHEME=AppAuth-tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=12.0'" SDK=appletvsimulator12.0 + - SCHEME=AppAuth_tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=12.0'" SDK=appletvsimulator12.0 before_script: - sudo gem install xcpretty script: From f7449a5d4b539c54dcca3cafa2d1d46f6c548df0 Mon Sep 17 00:00:00 2001 From: Joseph Heenan Date: Fri, 14 Sep 2018 11:29:07 +0200 Subject: [PATCH 042/204] Specify indent width in .xcodeproj This project seems to use an indent width of 2; it makes life easier for people that have to work on other projects with other indent widths if that is specified in the xcodeproj. --- AppAuth.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 580eb5428..97ae77e02 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -748,7 +748,9 @@ 341741AE1C5D8243000EF209 /* Source */, 340E737D1C5D819B0076B1F6 /* Products */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; 340E737D1C5D819B0076B1F6 /* Products */ = { isa = PBXGroup; From 59f770babe8c8104f1716b24b0d1d111a13bca73 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Mon, 17 Sep 2018 13:07:11 -0700 Subject: [PATCH 043/204] Add unit tests for x-www-form-urlencoded encoding and decoding --- UnitTests/OIDURLQueryComponentTests.m | 82 +++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/UnitTests/OIDURLQueryComponentTests.m b/UnitTests/OIDURLQueryComponentTests.m index e496897a8..e8f08c042 100644 --- a/UnitTests/OIDURLQueryComponentTests.m +++ b/UnitTests/OIDURLQueryComponentTests.m @@ -80,6 +80,88 @@ - (void)testAddingParameter { kTestParameterValue, @""); } +/*! @brief Test that URI query items are decoded correctly, using application/x-www-form-urlencoded + encoding. + @see https://tools.ietf.org/html/rfc6749#section-4.1.2 + @see https://tools.ietf.org/html/rfc6749#appendix-B + */ +- (void)test_formurlencoded_decoding { + // Authorization response URL template + NSString *responseURLtemplate = @"com.example.apps.1234-tepulg5joaks7:/?state=z634l182&code=4/WQA" + "stm4iiN_0Qi-n4mEo-jL-85CvQ&scope=%@&authuser=0&session_state=ab78c20&prompt=consent#"; + + NSString *expectedDecodedScope = + @"https://www.example.com/auth/plus.me https://www.example.com/auth/userinfo.profile"; + + // Tests an encoded scope with a '+'-encoded space + { + NSString* encodedScope = + @"https://www.example.com/auth/plus.me+https://www.example.com/auth/userinfo.profile"; + NSString *authorizationResponse = [NSString stringWithFormat:responseURLtemplate,encodedScope]; + OIDURLQueryComponent *query = + [[OIDURLQueryComponent alloc] initWithURL:[NSURL URLWithString:authorizationResponse]]; + NSString* value = [query valuesForParameter:@"scope"][0]; + XCTAssertEqualObjects(value, + expectedDecodedScope, + @"Failed to decode scope with '+' delimiter"); + } + // Tests an encoded scope with a '%20'-encoded space + { + NSString* encodedScope = + @"https://www.example.com/auth/plus.me%20https://www.example.com/auth/userinfo.profile"; + NSString *authorizationResponse = [NSString stringWithFormat:responseURLtemplate,encodedScope]; + OIDURLQueryComponent *query = + [[OIDURLQueryComponent alloc] initWithURL:[NSURL URLWithString:authorizationResponse]]; + NSString* value = [query valuesForParameter:@"scope"][0]; + XCTAssertEqualObjects(value, + expectedDecodedScope, + @"Failed to decode scope with '%%20' delimiter"); + } + // Tests that the example string from RFC6749 Appendix B is decoded correctly + { + NSString* encodedScope = @"+%25%26%2B%C2%A3%E2%82%AC"; + NSString *authorizationResponse = [NSString stringWithFormat:responseURLtemplate,encodedScope]; + OIDURLQueryComponent *query = + [[OIDURLQueryComponent alloc] initWithURL:[NSURL URLWithString:authorizationResponse]]; + NSString* value = [query valuesForParameter:@"scope"][0]; + XCTAssertEqualObjects(value, + @" %&+£€", + @"Failed to decode RFC6749 Appendix B sample string correctly."); + } +} + +/*! @brief Test that URI query items are encoded correctly, using application/x-www-form-urlencoded + encoding. Note that AppAuth always encodes "+" as "%20" (as permitted) to reduce + ambiguity. + @see https://tools.ietf.org/html/rfc6749#section-4.1.3 + @see https://tools.ietf.org/html/rfc6749#appendix-B + */ +- (void)test_formurlencoded_encoding { + NSURL *baseURL = [NSURL URLWithString:kTestURLRoot]; + // Tests that space is encoded as %20 + { + OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] initWithURL:baseURL]; + [query addParameter:@"scope" value:@"openid profile"]; + NSString *encodedParams = [query URLEncodedParameters]; + NSString *expected = @"scope=openid%20profile"; + XCTAssertEqualObjects(encodedParams, + expected, + @"Failed to encode space as %%20."); + } + // Tests that the example string from RFC6749 Appendix B is encoded correctly (but with space + // encoded as %20, not +, as allowed by application/x-www-form-urlencoded. + { + OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] initWithURL:baseURL]; + [query addParameter:@"scope" value:@" %&+£€"]; + // Tests the URLEncodedParameters method + NSString *encodedParams = [query URLEncodedParameters]; + NSString *expected = @"scope=%20%25%26%2B%C2%A3%E2%82%AC"; + XCTAssertEqualObjects(encodedParams, + expected, + @"Failed to encode RFC6749 Appendix B sample string correctly."); + } +} + - (void)testAddingTwoParameters { OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init]; [query addParameter:kTestParameterName value:kTestParameterValue]; From 488bfe9d2bac590477795838727df610ea45faf8 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Mon, 17 Sep 2018 13:37:57 -0700 Subject: [PATCH 044/204] Implement the application/x-www-form-urlencoded format for URL query components Previously only percent encoding was used, which misses space being encoded as '+'. We're still percent encoding space during encoding (as this appears to still be valid application/x-www-form-urlencoded encoded data, since percent encoding is permitted), but it will now correctly decode '+' as space. --- Source/OIDURLQueryComponent.h | 10 +++++++++- Source/OIDURLQueryComponent.m | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Source/OIDURLQueryComponent.h b/Source/OIDURLQueryComponent.h index 673d2db12..054b11ea1 100644 --- a/Source/OIDURLQueryComponent.h +++ b/Source/OIDURLQueryComponent.h @@ -28,7 +28,15 @@ NS_ASSUME_NONNULL_BEGIN */ extern BOOL gOIDURLQueryComponentForceIOS7Handling; -/*! @brief A utility class for creating and parsing URL query components. +/*! @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 diff --git a/Source/OIDURLQueryComponent.m b/Source/OIDURLQueryComponent.m index f5b07b16c..07050c905 100644 --- a/Source/OIDURLQueryComponent.m +++ b/Source/OIDURLQueryComponent.m @@ -48,6 +48,12 @@ - (nullable instancetype)initWithURL:(NSURL *)URL { 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 *queryItems = components.queryItems; for (NSURLQueryItem *queryItem in queryItems) { [self addParameter:queryItem.name value:queryItem.value]; @@ -58,6 +64,10 @@ - (nullable instancetype)initWithURL:(NSURL *)URL { // 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 *queryParts = [query componentsSeparatedByString:@"&"]; for (NSString *queryPart in queryParts) { NSRange equalsRange = [queryPart rangeOfString:@"="]; From 2c934c924de5a294fb64b7460e31cba2fb005aac Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 20 Sep 2018 17:02:29 -0700 Subject: [PATCH 045/204] Support iOS 12 SFAuthenticationSession is deprecated in iOS 12. Uses the replacement (and functionally equivalent) ASWebAuthenticationSession on iOS 12+. The AuthenticationServices framework is now required. AppAuth now requires Xcode 10 to build (but still supports a deployment target of iOS 7), per our policy to only support the current development tools. --- AppAuth.podspec | 2 +- AppAuth.xcodeproj/project.pbxproj | 4 ++++ Source/iOS/OIDExternalUserAgentIOS.m | 36 ++++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 07c4b83a4..518770748 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -40,7 +40,7 @@ It follows the OAuth 2.0 for Native Apps best current practice # iOS s.ios.source_files = "Source/iOS/**/*.{h,m}" s.ios.deployment_target = "7.0" - s.ios.framework = "SafariServices" + s.ios.frameworks = "SafariServices", "AuthenticationServices" # macOS s.osx.source_files = "Source/macOS/**/*.{h,m}" diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 97ae77e02..913481f7d 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -383,6 +383,7 @@ 34AF736B1FB4E4B30022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; 34AF736C1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; 34AF736D1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; + 34B822932153602C00D96702 /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34B822922153602C00D96702 /* AuthenticationServices.framework */; }; 34D5EC451E6D1AD900814354 /* OIDSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */; }; 34FEA6AE1DB6E083005C9212 /* OIDLoopbackHTTPServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */; }; 34FEA6AF1DB6E083005C9212 /* OIDLoopbackHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FEA6AD1DB6E083005C9212 /* OIDLoopbackHTTPServer.m */; }; @@ -588,6 +589,7 @@ 34A663271E871DD40060B664 /* OIDIDToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDIDToken.m; sourceTree = ""; }; 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRPProfileCode.m; sourceTree = ""; }; 34A663911E886AED0060B664 /* OIDRPProfileCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRPProfileCode.h; sourceTree = ""; }; + 34B822922153602C00D96702 /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = System/Library/Frameworks/AuthenticationServices.framework; sourceTree = SDKROOT; }; 34D5EC431E6D1AD900814354 /* OIDAppAuthTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OIDAppAuthTests-Bridging-Header.h"; sourceTree = ""; }; 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDSwiftTests.swift; sourceTree = ""; }; 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDLoopbackHTTPServer.h; sourceTree = ""; }; @@ -633,6 +635,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 34B822932153602C00D96702 /* AuthenticationServices.framework in Frameworks */, 3417422D1C5D850C000EF209 /* Security.framework in Frameworks */, 3417422B1C5D8502000EF209 /* SafariServices.framework in Frameworks */, 341741F51C5D8283000EF209 /* libAppAuth-iOS.a in Frameworks */, @@ -875,6 +878,7 @@ 341742291C5D84D0000EF209 /* Frameworks */ = { isa = PBXGroup; children = ( + 34B822922153602C00D96702 /* AuthenticationServices.framework */, 3417422C1C5D850C000EF209 /* Security.framework */, 3417422A1C5D8502000EF209 /* SafariServices.framework */, ); diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index 0a88cb64a..2c834bbc4 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -19,6 +19,7 @@ #import "OIDExternalUserAgentIOS.h" #import +#import #import "OIDErrorUtilities.h" #import "OIDExternalUserAgentSession.h" @@ -49,6 +50,7 @@ @implementation OIDExternalUserAgentIOS { #pragma clang diagnostic ignored "-Wpartial-availability" __weak SFSafariViewController *_safariVC; SFAuthenticationSession *_authenticationVC; + ASWebAuthenticationSession *_webAuthenticationVC; #pragma clang diagnostic pop } @@ -88,7 +90,34 @@ - (BOOL)presentExternalUserAgentRequest:(id)request BOOL openedSafari = NO; NSURL *requestURL = [request externalUserAgentRequestURL]; - if (@available(iOS 11.0, *)) { + // iOS 12 and later, use ASWebAuthenticationSession + if (@available(iOS 12.0, *)) { + __weak OIDExternalUserAgentIOS *weakSelf = self; + NSString *redirectScheme = request.redirectScheme; + ASWebAuthenticationSession *authenticationVC = + [[ASWebAuthenticationSession alloc] initWithURL:requestURL + callbackURLScheme:redirectScheme + completionHandler:^(NSURL * _Nullable callbackURL, + NSError * _Nullable error) { + __strong OIDExternalUserAgentIOS *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + strongSelf->_webAuthenticationVC = nil; + if (callbackURL) { + [strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL]; + } else { + NSError *safariError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow + underlyingError:error + description:nil]; + [strongSelf->_session failExternalUserAgentFlowWithError:safariError]; + } + }]; + _webAuthenticationVC = authenticationVC; + openedSafari = [authenticationVC start]; + // iOS 11, use SFAuthenticationSession + } else if (@available(iOS 11.0, *)) { __weak OIDExternalUserAgentIOS *weakSelf = self; NSString *redirectScheme = request.redirectScheme; SFAuthenticationSession *authenticationVC = @@ -97,8 +126,9 @@ - (BOOL)presentExternalUserAgentRequest:(id)request completionHandler:^(NSURL * _Nullable callbackURL, NSError * _Nullable error) { __strong OIDExternalUserAgentIOS *strongSelf = weakSelf; - if (!strongSelf) + if (!strongSelf) { return; + } strongSelf->_authenticationVC = nil; if (callbackURL) { [strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL]; @@ -112,6 +142,7 @@ - (BOOL)presentExternalUserAgentRequest:(id)request }]; _authenticationVC = authenticationVC; openedSafari = [authenticationVC start]; + // iOS 9 and 10, use SFSafariViewController } else if (@available(iOS 9.0, *)) { SFSafariViewController *safariVC = [[[self class] safariViewControllerFactory] safariViewControllerWithURL:requestURL]; @@ -119,6 +150,7 @@ - (BOOL)presentExternalUserAgentRequest:(id)request _safariVC = safariVC; [_presentingViewController presentViewController:safariVC animated:YES completion:nil]; openedSafari = YES; + // iOS 8 and earlier, use mobile Safari } else { openedSafari = [[UIApplication sharedApplication] openURL:requestURL]; } From dd945e14c4b5b889eeb712361cf4476868b87217 Mon Sep 17 00:00:00 2001 From: Joseph Heenan Date: Thu, 20 Sep 2018 14:29:56 +0100 Subject: [PATCH 046/204] Allow applications to use the "code id_token" response_type This response type is required to build a FAPI compliant client, and as the application can verify the id_token signature and c_hash before calling the token endpoint there is no reason to block applications from using code id_token. Other response types are still blocked as they don't appear to make sense in the context of a native app. The 'id_token code' response_type is also permitted as https://tools.ietf.org/html/rfc6749#section-3.1.1 explicitly allows this. closes #292 --- Source/OIDAuthState.h | 6 ++++- Source/OIDAuthState.m | 9 +++++-- Source/OIDAuthorizationRequest.m | 32 ++++++++++++++--------- Source/iOS/OIDAuthState+IOS.h | 6 ++++- Source/macOS/OIDAuthState+Mac.h | 6 ++++- UnitTests/OIDAuthorizationRequestTests.m | 33 +++++++++++++++++++++++- 6 files changed, 74 insertions(+), 18 deletions(-) diff --git a/Source/OIDAuthState.h b/Source/OIDAuthState.h index 27e55f14c..68697d2ca 100644 --- a/Source/OIDAuthState.h +++ b/Source/OIDAuthState.h @@ -117,7 +117,11 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt @property(nonatomic, weak, nullable) id 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. + 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. diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index a1d6f668b..9b4ed64cf 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -159,8 +159,13 @@ @implementation OIDAuthState { callback(authState, tokenError); }]; } else { - // implicit or hybrid flow (hybrid flow assumes code is not for this - // client) + // 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); diff --git a/Source/OIDAuthorizationRequest.m b/Source/OIDAuthorizationRequest.m index 05442da80..ccfacda0f 100644 --- a/Source/OIDAuthorizationRequest.m +++ b/Source/OIDAuthorizationRequest.m @@ -87,7 +87,7 @@ /*! @brief Assertion text for unsupported response types. */ static NSString *const OIDOAuthUnsupportedResponseTypeMessage = - @"The response_type \"%@\" isn't supported. AppAuth only supports the \"code\" response_type."; + @"The response_type \"%@\" isn't supported. AppAuth only supports the \"code\" or \"code id_token\" response_type."; /*! @brief Code challenge request method. */ @@ -105,6 +105,23 @@ - (instancetype)init 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 @@ -126,11 +143,7 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration _scope = [scope copy]; _redirectURL = [redirectURL copy]; _responseType = [responseType copy]; - // Attention: Please refer to https://github.com/openid/AppAuth-iOS/issues/105 - // If you change the restriction on response type here, you must also update initWithCoder: - if (![_responseType isEqualToString:OIDResponseTypeCode]) { - // AppAuth only supports the `code` response type. - // Discussion: https://github.com/openid/AppAuth-iOS/issues/98 + if (![[self class] isSupportedResponseType:_responseType]) { NSAssert(NO, OIDOAuthUnsupportedResponseTypeMessage, _responseType); return nil; } @@ -209,12 +222,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { OIDServiceConfiguration *configuration = [aDecoder decodeObjectOfClass:[OIDServiceConfiguration class] forKey:kConfigurationKey]; - // Attention: Please refer to https://github.com/openid/AppAuth-iOS/issues/105 - // If the initializer relaxes it's restriction on the response type field, this code must also - // be updated to re-enable use of the serialized responseType value. The value of 'code' here - // is only a valid assumption for that reason. - // [aDecoder decodeObjectOfClass:[NSString class] forKey:kResponseTypeKey]; - NSString *responseType = OIDResponseTypeCode; + 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]; diff --git a/Source/iOS/OIDAuthState+IOS.h b/Source/iOS/OIDAuthState+IOS.h index 1a3750c5e..8afc5dcc4 100644 --- a/Source/iOS/OIDAuthState+IOS.h +++ b/Source/iOS/OIDAuthState+IOS.h @@ -27,7 +27,11 @@ NS_ASSUME_NONNULL_BEGIN @interface OIDAuthState (IOS) /*! @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. + 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 presentingViewController The view controller from which to present the @c SFSafariViewController. diff --git a/Source/macOS/OIDAuthState+Mac.h b/Source/macOS/OIDAuthState+Mac.h index 6f1824f33..e412a7886 100644 --- a/Source/macOS/OIDAuthState+Mac.h +++ b/Source/macOS/OIDAuthState+Mac.h @@ -25,7 +25,11 @@ NS_ASSUME_NONNULL_BEGIN @interface OIDAuthState (Mac) /*! @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. + 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 callback The method called when the request has completed or failed. @return A @c OIDExternalUserAgentSession instance which will terminate when it diff --git a/UnitTests/OIDAuthorizationRequestTests.m b/UnitTests/OIDAuthorizationRequestTests.m index 1d4af083a..93b4f5982 100644 --- a/UnitTests/OIDAuthorizationRequestTests.m +++ b/UnitTests/OIDAuthorizationRequestTests.m @@ -440,7 +440,7 @@ - (void)testSupportedResponseTypes { NSString *scope = [OIDScopeUtilities scopesWithArray:@[ kTestScope, kTestScopeA ]]; - XCTAssertThrows( + XCTAssertNoThrow( [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID clientSecret:kTestClientSecret @@ -455,6 +455,22 @@ - (void)testSupportedResponseTypes { additionalParameters:additionalParameters] ); + // https://tools.ietf.org/html/rfc6749#section-3.1.1 says the order of values does not matter + XCTAssertNoThrow( + [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:kTestClientID + clientSecret:kTestClientSecret + scope:scope + redirectURL:[NSURL URLWithString:kTestRedirectURL] + responseType:@"id_token code" + state:kTestState + nonce:kTestNonce + codeVerifier:kTestCodeVerifier + codeChallenge:[[self class] codeChallenge] + codeChallengeMethod:[[self class] codeChallengeMethod] + additionalParameters:additionalParameters] + ); + XCTAssertThrows( [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID @@ -470,6 +486,21 @@ - (void)testSupportedResponseTypes { additionalParameters:additionalParameters] ); + XCTAssertThrows( + [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:kTestClientID + clientSecret:kTestClientSecret + scope:scope + redirectURL:[NSURL URLWithString:kTestRedirectURL] + responseType:@"token" + state:kTestState + nonce:kTestNonce + codeVerifier:kTestCodeVerifier + codeChallenge:[[self class] codeChallenge] + codeChallengeMethod:[[self class] codeChallengeMethod] + additionalParameters:additionalParameters] + ); + XCTAssertNoThrow( [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID From 0deb4e2ba0de68aecfb64edc2e57b0583c3b2bd9 Mon Sep 17 00:00:00 2001 From: Joseph Heenan Date: Thu, 20 Sep 2018 20:17:42 +0100 Subject: [PATCH 047/204] Add support for an initial access token to registration endpoint As per the specification, the AS may require an access token for access to the registration endpoint - allow the app to specify this. Specification: https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration which says: "The OpenID Provider MAY require an Initial Access Token that is provisioned out-of-band (in a manner that is out of scope for this specification) to restrict registration requests to only authorized Clients or developers." --- Source/OIDRegistrationRequest.h | 33 +++++++++++++++++++++++++ Source/OIDRegistrationRequest.m | 33 +++++++++++++++++++++++++ UnitTests/OIDRegistrationRequestTests.m | 11 +++++++++ 3 files changed, 77 insertions(+) diff --git a/Source/OIDRegistrationRequest.h b/Source/OIDRegistrationRequest.h index dac8d287c..e509c60a2 100644 --- a/Source/OIDRegistrationRequest.h +++ b/Source/OIDRegistrationRequest.h @@ -34,6 +34,16 @@ NS_ASSUME_NONNULL_BEGIN */ @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 @@ -79,6 +89,25 @@ NS_ASSUME_NONNULL_BEGIN */ - (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 *)redirectURIs + responseTypes:(nullable NSArray *)responseTypes + grantTypes:(nullable NSArray *)grantTypes + subjectType:(nullable NSString *)subjectType + tokenEndpointAuthMethod:(nullable NSString *)tokenEndpointAuthMethod + additionalParameters:(nullable NSDictionary *)additionalParameters; + /*! @brief Designated initializer. @param configuration The service's configuration. @param redirectURIs The redirect URIs to register for the client. @@ -87,7 +116,10 @@ NS_ASSUME_NONNULL_BEGIN @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 *)redirectURIs @@ -95,6 +127,7 @@ NS_ASSUME_NONNULL_BEGIN grantTypes:(nullable NSArray *)grantTypes subjectType:(nullable NSString *)subjectType tokenEndpointAuthMethod:(nullable NSString *)tokenEndpointAuthMethod + initialAccessToken:(nullable NSString *)initialAccessToken additionalParameters:(nullable NSDictionary *)additionalParameters NS_DESIGNATED_INITIALIZER; diff --git a/Source/OIDRegistrationRequest.m b/Source/OIDRegistrationRequest.m index cd6ba245d..9efd18fdd 100644 --- a/Source/OIDRegistrationRequest.m +++ b/Source/OIDRegistrationRequest.m @@ -26,6 +26,10 @@ */ 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"; @@ -69,9 +73,28 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration subjectType:(nullable NSString *)subjectType tokenEndpointAuthMethod:(nullable NSString *)tokenEndpointAuthenticationMethod additionalParameters:(nullable NSDictionary *)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 *)redirectURIs + responseTypes:(nullable NSArray *)responseTypes + grantTypes:(nullable NSArray *)grantTypes + subjectType:(nullable NSString *)subjectType + tokenEndpointAuthMethod:(nullable NSString *)tokenEndpointAuthenticationMethod + initialAccessToken:(nullable NSString *)initialAccessToken + additionalParameters:(nullable NSDictionary *)additionalParameters { self = [super init]; if (self) { _configuration = [configuration copy]; + _initialAccessToken = [initialAccessToken copy]; _redirectURIs = [redirectURIs copy]; _responseTypes = [responseTypes copy]; _grantTypes = [grantTypes copy]; @@ -105,6 +128,8 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { OIDServiceConfiguration *configuration = [aDecoder decodeObjectOfClass:[OIDServiceConfiguration class] forKey:kConfigurationKey]; + NSString *initialAccessToken = [aDecoder decodeObjectOfClass:[NSString class] + forKey:kInitialAccessToken]; NSArray *redirectURIs = [aDecoder decodeObjectOfClass:[NSArray class] forKey:kRedirectURIsKey]; NSArray *responseTypes = [aDecoder decodeObjectOfClass:[NSArray class] @@ -127,12 +152,14 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { 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]; @@ -157,8 +184,10 @@ - (NSString *)description { - (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) { @@ -170,6 +199,10 @@ - (NSURLRequest *)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; } diff --git a/UnitTests/OIDRegistrationRequestTests.m b/UnitTests/OIDRegistrationRequestTests.m index 3248dbabd..424f3cc31 100644 --- a/UnitTests/OIDRegistrationRequestTests.m +++ b/UnitTests/OIDRegistrationRequestTests.m @@ -31,6 +31,10 @@ */ static NSString *const kTestAdditionalParameterValue = @"1"; +/*! @brief Test value for the @c initialAccessToken property. + */ +static NSString *const kInitialAccessTokenTestValue = @"test"; + /*! @brief Test value for the @c redirectURL property. */ static NSString *kRedirectURLTestValue = @"https://client.example.com/redirect"; @@ -66,6 +70,7 @@ + (OIDRegistrationRequest *)testInstance { grantTypes:@[ kGrantTypeTestValue ] subjectType:kSubjectTypeTestValue tokenEndpointAuthMethod:kTokenEndpointAuthMethodTestValue + initialAccessToken:kInitialAccessTokenTestValue additionalParameters:additionalParameters]; return request; @@ -84,6 +89,7 @@ - (void)testCopying { XCTAssertNotNil(request.configuration); XCTAssertEqualObjects(request.applicationType, OIDApplicationTypeNative); + XCTAssertEqualObjects(request.initialAccessToken, kInitialAccessTokenTestValue); XCTAssertEqualObjects(request.redirectURIs, @[ [NSURL URLWithString:kRedirectURLTestValue] ]); XCTAssertEqualObjects(request.responseTypes, @[ kResponseTypeTestValue ]); XCTAssertEqualObjects(request.grantTypes, @[ kGrantTypeTestValue ]); @@ -101,6 +107,7 @@ - (void)testCopying { XCTAssertEqualObjects(requestCopy.configuration, request.configuration); XCTAssertEqualObjects(requestCopy.applicationType, request.applicationType); + XCTAssertEqualObjects(requestCopy.initialAccessToken, kInitialAccessTokenTestValue); XCTAssertEqualObjects(requestCopy.redirectURIs, request.redirectURIs); XCTAssertEqualObjects(requestCopy.responseTypes, request.responseTypes); XCTAssertEqualObjects(requestCopy.grantTypes, request.grantTypes); @@ -120,6 +127,7 @@ - (void)testSecureCoding { XCTAssertNotNil(request.configuration); XCTAssertEqualObjects(request.applicationType, OIDApplicationTypeNative); + XCTAssertEqualObjects(request.initialAccessToken, kInitialAccessTokenTestValue); XCTAssertEqualObjects(request.redirectURIs, @[ [NSURL URLWithString:kRedirectURLTestValue] ]); XCTAssertEqualObjects(request.responseTypes, @[ kResponseTypeTestValue ]); XCTAssertEqualObjects(request.grantTypes, @[ kGrantTypeTestValue ]); @@ -139,6 +147,7 @@ - (void)testSecureCoding { XCTAssertNotNil(requestCopy.configuration); XCTAssertEqualObjects(requestCopy.applicationType, request.applicationType); + XCTAssertEqualObjects(requestCopy.initialAccessToken, kInitialAccessTokenTestValue); XCTAssertEqualObjects(requestCopy.redirectURIs, request.redirectURIs); XCTAssertEqualObjects(requestCopy.responseTypes, request.responseTypes); XCTAssertEqualObjects(requestCopy.grantTypes, request.grantTypes); @@ -162,6 +171,8 @@ - (void)testURLRequest { XCTAssertEqualObjects(httpRequest.HTTPMethod, @"POST"); XCTAssertEqualObjects([httpRequest valueForHTTPHeaderField:@"Content-Type"], @"application/json"); + XCTAssertEqualObjects([httpRequest valueForHTTPHeaderField:@"Authorization"], + @"Bearer test"); XCTAssertEqualObjects(httpRequest.URL, request.configuration.registrationEndpoint); XCTAssertEqualObjects(parsedJSON[OIDApplicationTypeParam], request.applicationType); XCTAssertEqualObjects(parsedJSON[OIDRedirectURIsParam][0], From 82a7938596d9d7bcc96455c262716718a1dbdb3c Mon Sep 17 00:00:00 2001 From: Joseph Heenan Date: Mon, 24 Sep 2018 13:54:31 +0100 Subject: [PATCH 048/204] Correct supported macOS version in README.md The minimum versions used in the Xcodeproj and the podspec are 10.9, but README.md was stating 10.8 - updated it to 10.9 to match the other references. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aaa7c9754..ebf645a78 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ confidentiality of the client secrets may not work well. #### Supported Versions -AppAuth supports macOS (OS X) 10.8 and above. +AppAuth supports macOS (OS X) 10.9 and above. #### Authorization Server Requirements From 670c082600384a0f625276cbba37084816cc0c75 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 21 Sep 2018 09:49:23 -0700 Subject: [PATCH 049/204] Remove `scope` from the token refresh request as it is redundant Scope is a valid parameter for the Refresh Token request (Sectiom 6 of RFC 6749), however it's optional and when ommitted is treated as equal to the scope originally granted by the resource owner. Since the indented behavior of this convenience method is to create a token refresh with the full scope, it's redundant to include. Related to b5870c0bc6f65cb4004d697722612cd4a1019ab8 but slightly different reason. --- Source/OIDAuthState.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index 9b4ed64cf..fe8a16221 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -439,7 +439,7 @@ - (OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: redirectURL:nil clientID:_lastAuthorizationResponse.request.clientID clientSecret:_lastAuthorizationResponse.request.clientSecret - scope:_lastAuthorizationResponse.request.scope + scope:nil refreshToken:_refreshToken codeVerifier:nil additionalParameters:additionalParameters]; From a007c2da058377b1d0841c600776c2c078e38769 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 26 Sep 2018 19:12:20 -0700 Subject: [PATCH 050/204] More validation of OAuth error responses It's possible a server may respond a response in JSON with the key "error" that does not conform to Section 5.2 of RFC 6749. https://tools.ietf.org/html/rfc6749#section-5.2 --- Source/OIDErrorUtilities.m | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Source/OIDErrorUtilities.m b/Source/OIDErrorUtilities.m index 3e4e305b4..3b3c06075 100644 --- a/Source/OIDErrorUtilities.m +++ b/Source/OIDErrorUtilities.m @@ -66,7 +66,8 @@ + (NSError *)OAuthErrorWithDomain:(NSString *)oAuthErrorDomain // not a valid OAuth error if (![self isOAuthErrorDomain:oAuthErrorDomain] || !errorResponse - || !errorResponse[OIDOAuthErrorFieldError]) { + || !errorResponse[OIDOAuthErrorFieldError] + || ![errorResponse[OIDOAuthErrorFieldError] isKindOfClass:[NSString class]]) { return [[self class] errorWithCode:OIDErrorCodeNetworkError underlyingError:underlyingError description:underlyingError.localizedDescription]; @@ -80,8 +81,18 @@ + (NSError *)OAuthErrorWithDomain:(NSString *)oAuthErrorDomain } NSString *oauthErrorCodeString = errorResponse[OIDOAuthErrorFieldError]; - NSString *oauthErrorMessage = errorResponse[OIDOAuthErrorFieldErrorDescription]; - NSString *oauthErrorURI = errorResponse[OIDOAuthErrorFieldErrorURI]; + 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]; From 2a7a6754fd5b612aa554fa56d6e7853218e2d84f Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 26 Sep 2018 20:23:20 -0700 Subject: [PATCH 051/204] Log header in trace --- Source/OIDAuthorizationService.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 2ce1c07a2..1a83ae082 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -282,8 +282,9 @@ + (void)performTokenRequest:(OIDTokenRequest *)request NSURLRequest *URLRequest = [request URLRequest]; - AppAuthRequestTrace(@"Token Request: %@\nHTTPBody: %@", + AppAuthRequestTrace(@"Token Request: %@\nHeaders:%@\nHTTPBody: %@", URLRequest.URL, + URLRequest.allHTTPHeaderFields, [[NSString alloc] initWithData:URLRequest.HTTPBody encoding:NSUTF8StringEncoding]); From 601d07d7d2574d22c3d39711b345dc843c0f8ad3 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 27 Sep 2018 15:27:57 -0700 Subject: [PATCH 052/204] Version bump -> 1.0.0.beta1 --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 518770748..5b6d810e2 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "0.94.0" + s.version = "1.0.0.beta1" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC From 6cc8dc392ec9f3038ededc84b29d386af9deb924 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 17 Oct 2018 11:24:56 -0700 Subject: [PATCH 053/204] Support extensions with a new AppAuthCore target For Carthage: New AppAuthCore Framework target. For CocoaPods: New AppAuth/Core subspec option. Implementation details: Goal was to support extensions through a target that doesn't contain external user-agent code that can't be compiled, *without* changing how everyone using AppAuth today integrates. This was achieved through adding a new header file AppAuthCore.h with the core files only, and a new Framework target and subspec with just those files. --- .gitignore | 1 + AppAuth.podspec | 30 +- AppAuth.xcodeproj/project.pbxproj | 342 +++++++++++++++++- .../xcschemes/AppAuthCore.xcscheme | 99 +++++ Source/AppAuth.h | 1 - Source/AppAuthCore.h | 43 +++ Source/CoreFramework/AppAuthCore.h | 52 +++ Source/CoreFramework/Info.plist | 26 ++ Source/Framework/AppAuth.h | 26 +- 9 files changed, 595 insertions(+), 25 deletions(-) create mode 100644 AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthCore.xcscheme create mode 100644 Source/AppAuthCore.h create mode 100644 Source/CoreFramework/AppAuthCore.h create mode 100644 Source/CoreFramework/Info.plist diff --git a/.gitignore b/.gitignore index e826d81f4..9134faf61 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ Docs/ xcuserdata/ +Carthage/ diff --git a/AppAuth.podspec b/AppAuth.podspec index 5b6d810e2..3672f0e6f 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -33,16 +33,26 @@ It follows the OAuth 2.0 for Native Apps best current practice s.platforms = { :ios => "7.0", :osx => "10.9", :watchos => "2.0", :tvos => "9.0" } s.source = { :git => "https://github.com/openid/AppAuth-iOS.git", :tag => s.version } - - s.source_files = "Source/*.{h,m}" s.requires_arc = true - # iOS - s.ios.source_files = "Source/iOS/**/*.{h,m}" - s.ios.deployment_target = "7.0" - s.ios.frameworks = "SafariServices", "AuthenticationServices" - - # macOS - s.osx.source_files = "Source/macOS/**/*.{h,m}" - s.osx.deployment_target = '10.9' + # Subspec for the core AppAuth library classes only, suitable for extensions. + s.subspec 'Core' do |core| + core.source_files = "Source/*.{h,m}" + core.exclude_files = "Source/AppAuth.h" + end + + # Subspec for the full AppAuth library, including platform-dependant external user agents. + s.subspec 'ExternalUserAgent' do |externalUserAgent| + + externalUserAgent.source_files = "Source/*.{h,m}" + + # iOS + externalUserAgent.ios.source_files = "Source/iOS/**/*.{h,m}" + externalUserAgent.ios.deployment_target = "7.0" + externalUserAgent.ios.frameworks = "SafariServices", "AuthenticationServices" + + # macOS + externalUserAgent.osx.source_files = "Source/macOS/**/*.{h,m}" + externalUserAgent.osx.deployment_target = '10.9' + end end diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 913481f7d..99a4cd34d 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -115,6 +115,52 @@ 341E70991DE18796004353C1 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; 341E709A1DE18796004353C1 /* OIDAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B91C5D8243000EF209 /* OIDAuthorizationService.m */; }; 341E709B1DE18796004353C1 /* OIDAuthState.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741BB1C5D8243000EF209 /* OIDAuthState.m */; }; + 342F42872177B1FC00574F24 /* OIDFieldMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C41C5D8243000EF209 /* OIDFieldMapping.m */; }; + 342F42882177B1FC00574F24 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 342F42892177B1FC00574F24 /* OIDAuthState.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741BB1C5D8243000EF209 /* OIDAuthState.m */; }; + 342F428B2177B1FC00574F24 /* OIDTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D41C5D8243000EF209 /* OIDTokenResponse.m */; }; + 342F428C2177B1FC00574F24 /* OIDErrorUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C21C5D8243000EF209 /* OIDErrorUtilities.m */; }; + 342F428D2177B1FC00574F24 /* OIDURLQueryComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D81C5D8243000EF209 /* OIDURLQueryComponent.m */; }; + 342F428E2177B1FC00574F24 /* OIDAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */; }; + 342F428F2177B1FC00574F24 /* OIDAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B91C5D8243000EF209 /* OIDAuthorizationService.m */; }; + 342F42902177B1FC00574F24 /* OIDClientMetadataParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F791DE4276800DA0DC3 /* OIDClientMetadataParameters.m */; }; + 342F42912177B1FC00574F24 /* OIDTokenUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D61C5D8243000EF209 /* OIDTokenUtilities.m */; }; + 342F42922177B1FC00574F24 /* OIDServiceDiscovery.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D01C5D8243000EF209 /* OIDServiceDiscovery.m */; }; + 342F42932177B1FC00574F24 /* OIDTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D21C5D8243000EF209 /* OIDTokenRequest.m */; }; + 342F42962177B1FC00574F24 /* OIDServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CE1C5D8243000EF209 /* OIDServiceConfiguration.m */; }; + 342F42972177B1FC00574F24 /* OIDRegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */; }; + 342F42982177B1FC00574F24 /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; + 342F42992177B1FC00574F24 /* OIDScopes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CA1C5D8243000EF209 /* OIDScopes.m */; }; + 342F429A2177B1FC00574F24 /* OIDScopeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CC1C5D8243000EF209 /* OIDScopeUtilities.m */; }; + 342F429B2177B1FC00574F24 /* OIDGrantTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C61C5D8243000EF209 /* OIDGrantTypes.m */; }; + 342F429C2177B1FC00574F24 /* OIDRegistrationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7B1DE42E1000DA0DC3 /* OIDRegistrationRequest.m */; }; + 342F429D2177B1FC00574F24 /* OIDResponseTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C81C5D8243000EF209 /* OIDResponseTypes.m */; }; + 342F429E2177B1FC00574F24 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; + 342F429F2177B1FC00574F24 /* OIDError.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C01C5D8243000EF209 /* OIDError.m */; }; + 342F42A22177B1FC00574F24 /* OIDAuthorizationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42A32177B1FC00574F24 /* OIDScopes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C91C5D8243000EF209 /* OIDScopes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42A42177B1FC00574F24 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42A52177B1FC00574F24 /* OIDAuthStateChangeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BC1C5D8243000EF209 /* OIDAuthStateChangeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42A82177B1FC00574F24 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42A92177B1FC00574F24 /* OIDResponseTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C71C5D8243000EF209 /* OIDResponseTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42AA2177B1FC00574F24 /* OIDTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D11C5D8243000EF209 /* OIDTokenRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42AB2177B1FC00574F24 /* OIDScopeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CB1C5D8243000EF209 /* OIDScopeUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42AC2177B1FC00574F24 /* OIDTokenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D31C5D8243000EF209 /* OIDTokenResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42AD2177B1FC00574F24 /* OIDServiceDiscovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CF1C5D8243000EF209 /* OIDServiceDiscovery.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42AE2177B1FC00574F24 /* OIDGrantTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C51C5D8243000EF209 /* OIDGrantTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42AF2177B1FC00574F24 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42B12177B1FC00574F24 /* OIDRegistrationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 60140F7E1DE4335200DA0DC3 /* OIDRegistrationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42B22177B1FC00574F24 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42B32177B1FC00574F24 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42B42177B1FC00574F24 /* OIDServiceConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CD1C5D8243000EF209 /* OIDServiceConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42B52177B1FC00574F24 /* OIDAuthStateErrorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BD1C5D8243000EF209 /* OIDAuthStateErrorDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42B62177B1FC00574F24 /* OIDAuthorizationService.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B81C5D8243000EF209 /* OIDAuthorizationService.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42B72177B1FC00574F24 /* OIDRegistrationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 60140F7D1DE42E3000DA0DC3 /* OIDRegistrationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42B82177B1FC00574F24 /* OIDAuthState.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BA1C5D8243000EF209 /* OIDAuthState.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42B92177B1FC00574F24 /* OIDErrorUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C11C5D8243000EF209 /* OIDErrorUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42BB2177B1FC00574F24 /* OIDAuthorizationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42BC2177B1FC00574F24 /* OIDTokenUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D51C5D8243000EF209 /* OIDTokenUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 342F42BD2177B1FC00574F24 /* OIDError.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BF1C5D8243000EF209 /* OIDError.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAA5D1E83463400F9D36E /* AppAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 343AAA541E83463400F9D36E /* AppAuth.framework */; }; 343AAA6B1E83465500F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAA6C1E83466B00F9D36E /* OIDAuthorizationService+IOS.h in Headers */ = {isa = PBXBuildFile; fileRef = F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -358,6 +404,24 @@ 347424101E7F4BA000D3E6D6 /* OIDTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D41C5D8243000EF209 /* OIDTokenResponse.m */; }; 347424111E7F4BA000D3E6D6 /* OIDTokenUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D61C5D8243000EF209 /* OIDTokenUtilities.m */; }; 347424121E7F4BA000D3E6D6 /* OIDURLQueryComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D81C5D8243000EF209 /* OIDURLQueryComponent.m */; }; + 348970812177B3B000ABEED4 /* OIDScopesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742081C5D82D3000EF209 /* OIDScopesTests.m */; }; + 348970822177B3B000ABEED4 /* OIDURLQueryComponentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742121C5D82D3000EF209 /* OIDURLQueryComponentTests.m */; }; + 348970832177B3B000ABEED4 /* OIDServiceConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3417420A1C5D82D3000EF209 /* OIDServiceConfigurationTests.m */; }; + 348970842177B3B000ABEED4 /* OIDURLQueryComponentTestsIOS7.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742131C5D82D3000EF209 /* OIDURLQueryComponentTestsIOS7.m */; }; + 348970852177B3B000ABEED4 /* OIDRegistrationResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */; }; + 348970862177B3B000ABEED4 /* OIDServiceDiscoveryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3417420C1C5D82D3000EF209 /* OIDServiceDiscoveryTests.m */; }; + 348970872177B3B000ABEED4 /* OIDAuthStateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742051C5D82D3000EF209 /* OIDAuthStateTests.m */; }; + 348970882177B3B000ABEED4 /* OIDTokenResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742101C5D82D3000EF209 /* OIDTokenResponseTests.m */; }; + 348970892177B3B000ABEED4 /* OIDTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3417420E1C5D82D3000EF209 /* OIDTokenRequestTests.m */; }; + 3489708A2177B3B000ABEED4 /* OIDResponseTypesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742071C5D82D3000EF209 /* OIDResponseTypesTests.m */; }; + 3489708B2177B3B000ABEED4 /* OIDRegistrationRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F821DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m */; }; + 3489708C2177B3B000ABEED4 /* OIDAuthorizationRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742011C5D82D3000EF209 /* OIDAuthorizationRequestTests.m */; }; + 3489708D2177B3B000ABEED4 /* OIDGrantTypesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742061C5D82D3000EF209 /* OIDGrantTypesTests.m */; }; + 3489708E2177B3B000ABEED4 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 3489708F2177B3B000ABEED4 /* OIDAuthorizationResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742031C5D82D3000EF209 /* OIDAuthorizationResponseTests.m */; }; + 348970902177B3B000ABEED4 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; + 348970922177B3B000ABEED4 /* AppAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 343AAA541E83463400F9D36E /* AppAuth.framework */; }; + 3489709C2178F40600ABEED4 /* AppAuthCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3489709A2178F40600ABEED4 /* AppAuthCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34A663291E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34A6632A1E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34A6632B1E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -462,6 +526,13 @@ remoteGlobalIDString = 343AAAC11E8348A900F9D36E; remoteInfo = AppAuth_macOS; }; + 3489707F2177B3B000ABEED4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 340E73741C5D819B0076B1F6 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 343AAA531E83463400F9D36E; + remoteInfo = AppAuth_iOS; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -573,6 +644,7 @@ 341AA4CE1E7F392C00FCA5C6 /* AppAuth-macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "AppAuth-macOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 341AA4ED1E7F39F100FCA5C6 /* AppAuth-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "AppAuth-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 341E707E1DE18744004353C1 /* libAppAuth-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 342F42C32177B1FC00574F24 /* AppAuthCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuthCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAA4D1E8345B600F9D36E /* AppAuth.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppAuth.h; sourceTree = ""; }; 343AAA4E1E8345B600F9D36E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 343AAA541E83463400F9D36E /* AppAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -585,6 +657,10 @@ 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentIOSCustomBrowser.m; path = iOS/OIDExternalUserAgentIOSCustomBrowser.m; sourceTree = ""; }; 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentIOSCustomBrowser.h; path = iOS/OIDExternalUserAgentIOSCustomBrowser.h; sourceTree = ""; }; 347423F61E7F4B5600D3E6D6 /* libAppAuth-watchOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-watchOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 348970972177B3B000ABEED4 /* AppAuthCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuthCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3489709A2178F40600ABEED4 /* AppAuthCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAuthCore.h; sourceTree = ""; }; + 3489709B2178F40600ABEED4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 3489709E21791B0C00ABEED4 /* AppAuthCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppAuthCore.h; sourceTree = ""; }; 34A663261E871DD40060B664 /* OIDIDToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDIDToken.h; sourceTree = ""; }; 34A663271E871DD40060B664 /* OIDIDToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDIDToken.m; sourceTree = ""; }; 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRPProfileCode.m; sourceTree = ""; }; @@ -665,6 +741,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 342F42A02177B1FC00574F24 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 343AAA501E83463400F9D36E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -724,6 +807,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 348970912177B3B000ABEED4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 348970922177B3B000ABEED4 /* AppAuth.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -772,6 +863,8 @@ 343AAAAE1E83489A00F9D36E /* AppAuth_tvOSTests.xctest */, 343AAAC21E8348A900F9D36E /* AppAuth.framework */, 343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */, + 342F42C32177B1FC00574F24 /* AppAuthCore.framework */, + 348970972177B3B000ABEED4 /* AppAuthCoreTests.xctest */, ); name = Products; sourceTree = ""; @@ -779,10 +872,12 @@ 341741AE1C5D8243000EF209 /* Source */ = { isa = PBXGroup; children = ( + 348970992178F40600ABEED4 /* CoreFramework */, 343AAA4C1E8345B600F9D36E /* Framework */, 340DAE241D581FE700EC285B /* macOS */, F6F60FAF1D2BFEF000325CB3 /* iOS */, 341741AF1C5D8243000EF209 /* AppAuth.h */, + 3489709E21791B0C00ABEED4 /* AppAuthCore.h */, 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */, 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */, 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */, @@ -894,6 +989,15 @@ path = Framework; sourceTree = ""; }; + 348970992178F40600ABEED4 /* CoreFramework */ = { + isa = PBXGroup; + children = ( + 3489709A2178F40600ABEED4 /* AppAuthCore.h */, + 3489709B2178F40600ABEED4 /* Info.plist */, + ); + path = CoreFramework; + sourceTree = ""; + }; 34FEA6AB1DB6E083005C9212 /* LoopbackHTTPServer */ = { isa = PBXGroup; children = ( @@ -930,6 +1034,38 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 342F42A12177B1FC00574F24 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 342F42A22177B1FC00574F24 /* OIDAuthorizationResponse.h in Headers */, + 342F42A32177B1FC00574F24 /* OIDScopes.h in Headers */, + 342F42A42177B1FC00574F24 /* OIDExternalUserAgentRequest.h in Headers */, + 342F42A52177B1FC00574F24 /* OIDAuthStateChangeDelegate.h in Headers */, + 342F42A82177B1FC00574F24 /* OIDIDToken.h in Headers */, + 342F42A92177B1FC00574F24 /* OIDResponseTypes.h in Headers */, + 342F42AA2177B1FC00574F24 /* OIDTokenRequest.h in Headers */, + 342F42AB2177B1FC00574F24 /* OIDScopeUtilities.h in Headers */, + 342F42AC2177B1FC00574F24 /* OIDTokenResponse.h in Headers */, + 342F42AD2177B1FC00574F24 /* OIDServiceDiscovery.h in Headers */, + 342F42AE2177B1FC00574F24 /* OIDGrantTypes.h in Headers */, + 342F42AF2177B1FC00574F24 /* OIDURLSessionProvider.h in Headers */, + 342F42B12177B1FC00574F24 /* OIDRegistrationResponse.h in Headers */, + 342F42B22177B1FC00574F24 /* OIDExternalUserAgent.h in Headers */, + 342F42B32177B1FC00574F24 /* OIDExternalUserAgentSession.h in Headers */, + 342F42B42177B1FC00574F24 /* OIDServiceConfiguration.h in Headers */, + 342F42B52177B1FC00574F24 /* OIDAuthStateErrorDelegate.h in Headers */, + 342F42B62177B1FC00574F24 /* OIDAuthorizationService.h in Headers */, + 342F42B72177B1FC00574F24 /* OIDRegistrationRequest.h in Headers */, + 342F42B82177B1FC00574F24 /* OIDAuthState.h in Headers */, + 342F42B92177B1FC00574F24 /* OIDErrorUtilities.h in Headers */, + 342F42BB2177B1FC00574F24 /* OIDAuthorizationRequest.h in Headers */, + 342F42BC2177B1FC00574F24 /* OIDTokenUtilities.h in Headers */, + 3489709C2178F40600ABEED4 /* AppAuthCore.h in Headers */, + 342F42BD2177B1FC00574F24 /* OIDError.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 343AAA511E83463400F9D36E /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1175,6 +1311,24 @@ productReference = 341E707E1DE18744004353C1 /* libAppAuth-tvOS.a */; productType = "com.apple.product-type.library.static"; }; + 342F42842177B1FC00574F24 /* AppAuthCore */ = { + isa = PBXNativeTarget; + buildConfigurationList = 342F42C02177B1FC00574F24 /* Build configuration list for PBXNativeTarget "AppAuthCore" */; + buildPhases = ( + 342F42852177B1FC00574F24 /* Sources */, + 342F42A02177B1FC00574F24 /* Frameworks */, + 342F42A12177B1FC00574F24 /* Headers */, + 342F42BF2177B1FC00574F24 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AppAuthCore; + productName = AppAuth_iOS; + productReference = 342F42C32177B1FC00574F24 /* AppAuthCore.framework */; + productType = "com.apple.product-type.framework"; + }; 343AAA531E83463400F9D36E /* AppAuth_iOS */ = { isa = PBXNativeTarget; buildConfigurationList = 343AAA651E83463400F9D36E /* Build configuration list for PBXNativeTarget "AppAuth_iOS" */; @@ -1318,6 +1472,24 @@ productReference = 347423F61E7F4B5600D3E6D6 /* libAppAuth-watchOS.a */; productType = "com.apple.product-type.library.static"; }; + 3489707D2177B3B000ABEED4 /* AppAuthCoreTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 348970942177B3B000ABEED4 /* Build configuration list for PBXNativeTarget "AppAuthCoreTests" */; + buildPhases = ( + 348970802177B3B000ABEED4 /* Sources */, + 348970912177B3B000ABEED4 /* Frameworks */, + 348970932177B3B000ABEED4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 3489707E2177B3B000ABEED4 /* PBXTargetDependency */, + ); + name = AppAuthCoreTests; + productName = AppAuth_iOSTests; + productReference = 348970972177B3B000ABEED4 /* AppAuthCoreTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1409,6 +1581,8 @@ 343AAAAD1E83489A00F9D36E /* AppAuth_tvOSTests */, 343AAAC11E8348A900F9D36E /* AppAuth_macOS */, 343AAAC91E8348AA00F9D36E /* AppAuth_macOSTests */, + 342F42842177B1FC00574F24 /* AppAuthCore */, + 3489707D2177B3B000ABEED4 /* AppAuthCoreTests */, ); }; /* End PBXProject section */ @@ -1435,6 +1609,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 342F42BF2177B1FC00574F24 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 343AAA521E83463400F9D36E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1484,6 +1665,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 348970932177B3B000ABEED4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1652,6 +1840,35 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 342F42852177B1FC00574F24 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 342F42872177B1FC00574F24 /* OIDFieldMapping.m in Sources */, + 342F42882177B1FC00574F24 /* OIDIDToken.m in Sources */, + 342F42892177B1FC00574F24 /* OIDAuthState.m in Sources */, + 342F428B2177B1FC00574F24 /* OIDTokenResponse.m in Sources */, + 342F428C2177B1FC00574F24 /* OIDErrorUtilities.m in Sources */, + 342F428D2177B1FC00574F24 /* OIDURLQueryComponent.m in Sources */, + 342F428E2177B1FC00574F24 /* OIDAuthorizationRequest.m in Sources */, + 342F428F2177B1FC00574F24 /* OIDAuthorizationService.m in Sources */, + 342F42902177B1FC00574F24 /* OIDClientMetadataParameters.m in Sources */, + 342F42912177B1FC00574F24 /* OIDTokenUtilities.m in Sources */, + 342F42922177B1FC00574F24 /* OIDServiceDiscovery.m in Sources */, + 342F42932177B1FC00574F24 /* OIDTokenRequest.m in Sources */, + 342F42962177B1FC00574F24 /* OIDServiceConfiguration.m in Sources */, + 342F42972177B1FC00574F24 /* OIDRegistrationResponse.m in Sources */, + 342F42982177B1FC00574F24 /* OIDURLSessionProvider.m in Sources */, + 342F42992177B1FC00574F24 /* OIDScopes.m in Sources */, + 342F429A2177B1FC00574F24 /* OIDScopeUtilities.m in Sources */, + 342F429B2177B1FC00574F24 /* OIDGrantTypes.m in Sources */, + 342F429C2177B1FC00574F24 /* OIDRegistrationRequest.m in Sources */, + 342F429D2177B1FC00574F24 /* OIDResponseTypes.m in Sources */, + 342F429E2177B1FC00574F24 /* OIDAuthorizationResponse.m in Sources */, + 342F429F2177B1FC00574F24 /* OIDError.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 343AAA4F1E83463400F9D36E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1875,6 +2092,29 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 348970802177B3B000ABEED4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 348970812177B3B000ABEED4 /* OIDScopesTests.m in Sources */, + 348970822177B3B000ABEED4 /* OIDURLQueryComponentTests.m in Sources */, + 348970832177B3B000ABEED4 /* OIDServiceConfigurationTests.m in Sources */, + 348970842177B3B000ABEED4 /* OIDURLQueryComponentTestsIOS7.m in Sources */, + 348970852177B3B000ABEED4 /* OIDRegistrationResponseTests.m in Sources */, + 348970862177B3B000ABEED4 /* OIDServiceDiscoveryTests.m in Sources */, + 348970872177B3B000ABEED4 /* OIDAuthStateTests.m in Sources */, + 348970882177B3B000ABEED4 /* OIDTokenResponseTests.m in Sources */, + 348970892177B3B000ABEED4 /* OIDTokenRequestTests.m in Sources */, + 3489708A2177B3B000ABEED4 /* OIDResponseTypesTests.m in Sources */, + 3489708B2177B3B000ABEED4 /* OIDRegistrationRequestTests.m in Sources */, + 3489708C2177B3B000ABEED4 /* OIDAuthorizationRequestTests.m in Sources */, + 3489708D2177B3B000ABEED4 /* OIDGrantTypesTests.m in Sources */, + 3489708E2177B3B000ABEED4 /* OIDRPProfileCode.m in Sources */, + 3489708F2177B3B000ABEED4 /* OIDAuthorizationResponseTests.m in Sources */, + 348970902177B3B000ABEED4 /* OIDTokenUtilitiesTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1908,6 +2148,11 @@ target = 343AAAC11E8348A900F9D36E /* AppAuth_macOS */; targetProxy = 343AAACC1E8348AA00F9D36E /* PBXContainerItemProxy */; }; + 3489707E2177B3B000ABEED4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 343AAA531E83463400F9D36E /* AppAuth_iOS */; + targetProxy = 3489707F2177B3B000ABEED4 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -1975,8 +2220,8 @@ GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", "$(inherited)", + "DEBUG=1", ); GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -2033,6 +2278,7 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -2191,6 +2437,54 @@ }; name = Release; }; + 342F42C12177B1FC00574F24 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Source/CoreFramework/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthCore; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 342F42C22177B1FC00574F24 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Source/CoreFramework/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthCore; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; 343AAA661E83463400F9D36E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2507,6 +2801,34 @@ }; name = Release; }; + 348970952177B3B000ABEED4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + HEADER_SEARCH_PATHS = .; + INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-ExtensionTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 348970962177B3B000ABEED4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + HEADER_SEARCH_PATHS = .; + INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-ExtensionTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -2573,6 +2895,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 342F42C02177B1FC00574F24 /* Build configuration list for PBXNativeTarget "AppAuthCore" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 342F42C12177B1FC00574F24 /* Debug */, + 342F42C22177B1FC00574F24 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 343AAA651E83463400F9D36E /* Build configuration list for PBXNativeTarget "AppAuth_iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2645,6 +2976,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 348970942177B3B000ABEED4 /* Build configuration list for PBXNativeTarget "AppAuthCoreTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 348970952177B3B000ABEED4 /* Debug */, + 348970962177B3B000ABEED4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 340E73741C5D819B0076B1F6 /* Project object */; diff --git a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthCore.xcscheme b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthCore.xcscheme new file mode 100644 index 000000000..52e11aa94 --- /dev/null +++ b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthCore.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/AppAuth.h b/Source/AppAuth.h index e7f7faa61..d69772a64 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -57,7 +57,6 @@ #error "Platform Undefined" #endif - /*! @mainpage AppAuth for iOS and macOS @section introduction Introduction diff --git a/Source/AppAuthCore.h b/Source/AppAuthCore.h new file mode 100644 index 000000000..83b22ae50 --- /dev/null +++ b/Source/AppAuthCore.h @@ -0,0 +1,43 @@ +/*! @file AppAuthCore.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 "OIDAuthState.h" +#import "OIDAuthStateChangeDelegate.h" +#import "OIDAuthStateErrorDelegate.h" +#import "OIDAuthorizationRequest.h" +#import "OIDAuthorizationResponse.h" +#import "OIDAuthorizationService.h" +#import "OIDError.h" +#import "OIDErrorUtilities.h" +#import "OIDExternalUserAgent.h" +#import "OIDExternalUserAgentRequest.h" +#import "OIDExternalUserAgentSession.h" +#import "OIDGrantTypes.h" +#import "OIDIDToken.h" +#import "OIDRegistrationRequest.h" +#import "OIDRegistrationResponse.h" +#import "OIDResponseTypes.h" +#import "OIDScopes.h" +#import "OIDScopeUtilities.h" +#import "OIDServiceConfiguration.h" +#import "OIDServiceDiscovery.h" +#import "OIDTokenRequest.h" +#import "OIDTokenResponse.h" +#import "OIDTokenUtilities.h" +#import "OIDURLSessionProvider.h" + diff --git a/Source/CoreFramework/AppAuthCore.h b/Source/CoreFramework/AppAuthCore.h new file mode 100644 index 000000000..14d518542 --- /dev/null +++ b/Source/CoreFramework/AppAuthCore.h @@ -0,0 +1,52 @@ +/*! @file AppAuthCore.h + @brief AppAuth iOS SDK + @copyright + Copyright 2018 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 + +//! Project version number for AppAuthFramework-iOS. +FOUNDATION_EXPORT double AppAuthCoreVersionNumber; + +//! Project version string for AppAuthCoreFramework. +FOUNDATION_EXPORT const unsigned char AppAuthCoreVersionString[]; + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + + diff --git a/Source/CoreFramework/Info.plist b/Source/CoreFramework/Info.plist new file mode 100644 index 000000000..4c93b0d31 --- /dev/null +++ b/Source/CoreFramework/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2017 The AppAuth Authors + NSPrincipalClass + + + diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index 4541a1ced..38631081f 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -1,19 +1,19 @@ /*! @file AppAuth.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 + @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 + 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. + 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 From e15216fe9636ff86265c49a9de15ccdcedb69132 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 17 Oct 2018 12:28:23 -0700 Subject: [PATCH 054/204] Add Julien and Tobias to CONTRIBUTORS --- CONTRIBUTORS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 1c44cfb28..5cce2b2a5 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -17,4 +17,5 @@ Rebecka Gulliksson David Waite Craig Lane https://github.com/ProjectLane Hernan Zalazar https://github.com/hzalaz - +Julien Bodet https://github.com/julienbodet +Tobias Schröpf https://github.com/schroepf From c3599c0eba89191eff29429ebc1f49a43b614348 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 24 Oct 2018 09:34:53 -0700 Subject: [PATCH 055/204] Add Joseph Heenan to CONTRIBUTORS For re-enabling "code id_token" as a partially supported response_type. https://github.com/openid/AppAuth-iOS/pull/298 --- CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 5cce2b2a5..bf6f154f9 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -17,5 +17,6 @@ Rebecka Gulliksson David Waite Craig Lane https://github.com/ProjectLane Hernan Zalazar https://github.com/hzalaz +Joseph Heenan https://github.com/jogu Julien Bodet https://github.com/julienbodet Tobias Schröpf https://github.com/schroepf From 49637500ec6ee94681259a044d65a9cb073ba788 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 24 Oct 2018 11:16:30 -0700 Subject: [PATCH 056/204] Started a ChangeLog seeded with all 2018 changes --- CHANGELOG.md | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..3f2b53a88 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,142 @@ +# AppAuth for iOS and macOS Changelog + +## Unreleased +### Notable Changes + +1. `AppAuth/Core` subspec, and AppAuthCore Framework added to support +iOS extensions. + +## 1.0.0.beta1 (2018-09-27) + +First 1.0.0 beta! HEAD is now tracking changes for the 1.0.0 release. +The `pre-1.0` branch was cut prior to the breaking changes for 1.0.0, +bug fixes for critical issues may be backported for a time. + +### Notable Changes + +1. **All deprecated APIs removed.** Please ensure your code builds on + version 0.95.0 with no deprecation warnings before upgrading! + Notably, if you started with a version of AppAuth prior to 0.93.0 + you will need to follow the instructions in + [Upgrading to 0.93.0](#upgrading-to-0930) +2. Updated for iOS 12, and Xcode 10. **Xcode 10 is now required.** + NB. per policy, AppAuth supports many older versions of iOS and + macOS, but only the current Xcode toolchain. + If you need to stay on old versions of Xcode for some reason, stay + on the pre-1.0 releases. +3. macOS 32-bit support removed. If you need this support, stay on the + pre-1.0 releases. + +### Fixes + +1. All fixes in the 0.95.0 release are incorporated in this release. + +## 0.95.0 (2018-09-27) + +### Fixes + +1. `x-www-form-urlencoded` encoding and decoding should be 100% + spec compliant now, previously the `+` character was not decoded as + 0x20 space. https://github.com/openid/AppAuth-iOS/pull/291 + +2. `scope` no longer sent during token refresh (was redundant) + https://github.com/openid/AppAuth-iOS/pull/301 + +## 0.94.0 (2018-07-13) + +### Fixes +1. `form-urlencode` client ID and client secret in Authorization header + +### Added + +1. Samples have icons now! +2. Output trace logs by defining `_APPAUTHTRACE` + +## 0.93.0 (2018-06-26) + +### Notable Changes + +1. Implements OpenID Connect (ID Token handling) and the OpenID Connect + RP Certification test suite. + https://github.com/openid/AppAuth-iOS/pull/101 + +2. The `OIDAuthorizationUICoordinator` pattern was genericized to + support non-authorization external user-agent flows like logout + (though none are directly implemented by AppAuth, yet). + `OIDAuthorizationUICoordinator*` classes renamed to + `OIDExternalUserAgent*`. + https://github.com/openid/AppAuth-iOS/pull/196 + https://github.com/openid/AppAuth-iOS/pull/212 + See [Upgrading to 0.93.0](#upgrading-to-0930). + +3. Added custom browser support on iOS. Provides several + convenience implementations of alternative external user-agents on + iOS such as Chrome and Firefox. These are intended for + **enterprise use only**, where the app developers have greater + control over the operating environment and have special requirements + that require a custom browser like Chrome. + See the [code example](https://github.com/openid/AppAuth-iOS/issues/200#issuecomment-364610027). + https://github.com/openid/AppAuth-iOS/issues/200 + https://github.com/openid/AppAuth-iOS/pull/201 + +### Upgrading to 0.93.0 + +0.93.0 deprecates several methods. To update your code to avoid the +deprecated methods (which will be required for the 1.0.0 release), +you will need to make changes. + +If you implemented your own `OIDAuthorizationUICoordinator`, or called +the methods which accepted a `UICoordinator` instance, you will need to +update to the new method names. See the deprecation error messages +for the new methods to use in those cases. + +Most users who are using the convenience methods of AppAuth will only +need to make the following 3 minor changes to their AppDelegate: + +#### Import: + +Change +```objc +@protocol OIDAuthorizationFlowSession; +``` +to +```objc +@protocol OIDExternalUserAgentSession; +``` + +#### Property: + +Change +```objc +@property(nonatomic, strong, nullable) id currentAuthorizationFlow; +``` +to +```objc +@property(nonatomic, strong, nullable) idcurrentAuthorizationFlow; +``` + +#### Implementation of `-(BOOL)application:openURL:options:` +Change +```objc +if ([_currentAuthorizationFlow resumeAuthorizationFlowWithURL:url]) { +``` +to +```objc +if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) { +``` + +See also the changes made to the sample which you can copy: +https://github.com/openid/AppAuth-iOS/commit/619bb7c7d5f83cc2ed19380d425ca8afa279644c?diff=unified + + +## 0.92.0 (2018-01-05) + +### Improvements + +1. Added an official Swift sample, and included Swift testing in the + continuous integration tests. + +## Pre 0.92.0 + +No changelog entries exist for changes prior to 2018, please review the +[git history](https://github.com/openid/AppAuth-iOS/commits/0.91.0). From fbeffa6b80c86ec74aa724d0ac4910dca2822571 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 24 Oct 2018 12:42:11 -0700 Subject: [PATCH 057/204] Set APPLICATION_EXTENSION_API_ONLY to YES for AppAuthCore Framework target Resolves #130 --- AppAuth.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 99a4cd34d..9e5286a18 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -2440,6 +2440,7 @@ 342F42C12177B1FC00574F24 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CODE_SIGN_IDENTITY = ""; @@ -2464,6 +2465,7 @@ 342F42C22177B1FC00574F24 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CODE_SIGN_IDENTITY = ""; From 0b0ce1bcc25466b8a83bcd38d16c93300661d702 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Mon, 29 Oct 2018 09:57:45 -0700 Subject: [PATCH 058/204] Remove the setSafariViewControllerFactory API Simplifying OIDExternalUserAgentIOS by removing customization that is only relevant in old versions of iOS that may confuse developers starting on iOS 12+. AppAuth's pluggable `OIDExternalUserAgent` system can be used by users who need customizations, e.g. by copying OIDExternalUserAgentIOS and modifying as needed (no fork required). --- Source/iOS/OIDExternalUserAgentIOS.h | 20 --------------- Source/iOS/OIDExternalUserAgentIOS.m | 38 +--------------------------- 2 files changed, 1 insertion(+), 57 deletions(-) diff --git a/Source/iOS/OIDExternalUserAgentIOS.h b/Source/iOS/OIDExternalUserAgentIOS.h index 9f5ebdfff..98c041c9b 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.h +++ b/Source/iOS/OIDExternalUserAgentIOS.h @@ -24,31 +24,11 @@ NS_ASSUME_NONNULL_BEGIN -/*! @brief Allows library consumers to bootstrap an @c SFSafariViewController as they see fit. - @remarks Useful for customizing tint colors and presentation styles. - */ -@protocol OIDSafariViewControllerFactory - -/*! @brief Creates and returns a new @c SFSafariViewController. - @param URL The URL which the @c SFSafariViewController should load initially. - */ -- (SFSafariViewController *)safariViewControllerWithURL:(NSURL *)URL; - -@end - /*! @brief An iOS specific external user-agent that uses the best possible user-agent available depending on the version of iOS to present the request. */ @interface OIDExternalUserAgentIOS : NSObject -/*! @brief Allows library consumers to change the @c OIDSafariViewControllerFactory used to create - new instances of @c SFSafariViewController. - @remarks Useful for customizing tint colors and presentation styles. - @param factory The @c OIDSafariViewControllerFactory to use for creating new instances of - @c SFSafariViewController. - */ -+ (void)setSafariViewControllerFactory:(id)factory; - /*! @internal @brief Unavailable. Please use @c initWithPresentingViewController: */ diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index 2c834bbc4..3c6ed4b18 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -27,17 +27,6 @@ NS_ASSUME_NONNULL_BEGIN -/** @brief The global/shared Safari view controller factory. Responsible for creating all new - instances of @c SFSafariViewController. - */ -static id __nullable gSafariViewControllerFactory; - -/** @brief The default @c OIDSafariViewControllerFactory which creates new instances of - @c SFSafariViewController using known best practices. - */ -@interface OIDDefaultSafariViewControllerFactory : NSObject -@end - @interface OIDExternalUserAgentIOS () @end @@ -54,21 +43,6 @@ @implementation OIDExternalUserAgentIOS { #pragma clang diagnostic pop } -/** @brief Obtains the current @c OIDSafariViewControllerFactory; creating a new default instance if - required. - */ -+ (id)safariViewControllerFactory { - if (!gSafariViewControllerFactory) { - gSafariViewControllerFactory = [[OIDDefaultSafariViewControllerFactory alloc] init]; - } - return gSafariViewControllerFactory; -} - -+ (void)setSafariViewControllerFactory:(id)factory { - NSAssert(factory, @"Parameter: |factory| must be non-nil."); - gSafariViewControllerFactory = factory; -} - - (nullable instancetype)initWithPresentingViewController: (UIViewController *)presentingViewController { self = [super init]; @@ -145,7 +119,7 @@ - (BOOL)presentExternalUserAgentRequest:(id)request // iOS 9 and 10, use SFSafariViewController } else if (@available(iOS 9.0, *)) { SFSafariViewController *safariVC = - [[[self class] safariViewControllerFactory] safariViewControllerWithURL:requestURL]; + [[SFSafariViewController alloc] initWithURL:requestURL]; safariVC.delegate = self; _safariVC = safariVC; [_presentingViewController presentViewController:safariVC animated:YES completion:nil]; @@ -223,14 +197,4 @@ - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller NS_AV @end -@implementation OIDDefaultSafariViewControllerFactory - -- (SFSafariViewController *)safariViewControllerWithURL:(NSURL *)URL NS_AVAILABLE_IOS(9.0) { - SFSafariViewController *safariViewController = - [[SFSafariViewController alloc] initWithURL:URL entersReaderIfAvailable:NO]; - return safariViewController; -} - -@end - NS_ASSUME_NONNULL_END From 9a81d9140cce706a6cfaa099fadea45836179e70 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Thu, 1 Nov 2018 11:41:14 -0700 Subject: [PATCH 059/204] Update samples section of the README --- README.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/README.md b/README.md index ebf645a78..40e1da079 100644 --- a/README.md +++ b/README.md @@ -337,16 +337,4 @@ Browse the [API documentation](http://openid.github.io/AppAuth-iOS/docs/latest/a ## Included Samples -You can try out the iOS sample included in the source distribution by opening -`Example/Example.xcworkspace`. You can easily convert the Example -workspace to a Pod workspace by deleting the `AppAuth` project, and -[configuring the pod](#setup). You can also -[try out the sample via CocoaPods](#try). Be sure to follow the instructions in -[Example/README.md](Example/README.md) to configure your own OAuth client ID -for use with the example. - -You can try out the macOS sample included in the source distribution by -executing `pod install` in the `Example-Mac` folder, then opening -`Example-Mac.xcworkspace`. Be sure to follow the instructions in -[Example-Mac/README.md](Example-Mac/README.md) to configure your own OAuth -client ID for use with the example. +Sample apps that explore core AppAuth features are available for iOS and macOS, follow the instructions in [Examples/README.md](Examples/README.md) to get started. From 0c822671e56cedf159a6d27ccf0669b689c0f2b5 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 2 Nov 2018 09:38:31 -0700 Subject: [PATCH 060/204] Always dismiss the ASWebAuthenticationSession when resuming Fixes #328 --- Source/iOS/OIDExternalUserAgentIOS.m | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index 3c6ed4b18..aad10fa39 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -149,14 +149,21 @@ - (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(voi #pragma clang diagnostic ignored "-Wpartial-availability" SFSafariViewController *safariVC = _safariVC; SFAuthenticationSession *authenticationVC = _authenticationVC; + ASWebAuthenticationSession *webAuthenticationVC = _webAuthenticationVC; #pragma clang diagnostic pop [self cleanUp]; - if (@available(iOS 11.0, *)) { + if (@available(iOS 12.0, *)) { + // dismiss the ASWebAuthenticationSession + [webAuthenticationVC cancel]; + if (completion) completion(); + } else if (@available(iOS 11.0, *)) { + // dismiss the SFAuthenticationSession [authenticationVC cancel]; if (completion) completion(); } else if (@available(iOS 9.0, *)) { + // dismiss the SFSafariViewController if (safariVC) { [safariVC dismissViewControllerAnimated:YES completion:completion]; } else { From 2217b944ac72c3588ffa110a93ba1cf03a6bea32 Mon Sep 17 00:00:00 2001 From: Vineet Shah Date: Fri, 15 Mar 2019 15:56:26 -0400 Subject: [PATCH 061/204] Weakly link AuthenticationServices framework --- AppAuth.podspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 3672f0e6f..542aca7ba 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -49,7 +49,8 @@ It follows the OAuth 2.0 for Native Apps best current practice # iOS externalUserAgent.ios.source_files = "Source/iOS/**/*.{h,m}" externalUserAgent.ios.deployment_target = "7.0" - externalUserAgent.ios.frameworks = "SafariServices", "AuthenticationServices" + externalUserAgent.ios.frameworks = "SafariServices" + externalUserAgent.ios.weak_frameworks = "AuthenticationServices" # macOS externalUserAgent.osx.source_files = "Source/macOS/**/*.{h,m}" From d1270ecbae5ea705f9b87682f12e3a6c2c925313 Mon Sep 17 00:00:00 2001 From: Alex Taffe Date: Wed, 14 Nov 2018 17:23:08 -0500 Subject: [PATCH 062/204] Add the ability to dictate ports --- Source/macOS/OIDRedirectHTTPHandler.h | 17 +++++++++++++++-- Source/macOS/OIDRedirectHTTPHandler.m | 8 +++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Source/macOS/OIDRedirectHTTPHandler.h b/Source/macOS/OIDRedirectHTTPHandler.h index 50b6584f6..eca431977 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.h +++ b/Source/macOS/OIDRedirectHTTPHandler.h @@ -45,17 +45,30 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithSuccessURL:(nullable NSURL *)successURL; +/*! @brief Starts listening on the loopback interface on a specified port, and returns a URL + with the base address. Use the returned redirect URI to build a @c OIDExternalUserAgentRequest, + and once you initiate the request, set the resulting @c OIDExternalUserAgentSession to + @c currentAuthorizationFlow so the response can be handled. + @param returnError The error if an error occurred while starting the local HTTP server. + @param port The manually specified port, or 0 for a random available port. + @return The URL containing the address of the server with the specified port, or nil if there was an error. + @discussion Each instance of @c OIDRedirectHTTPHandler can only listen for a single response. + Calling this more than once will result in the previous listener being cancelled (equivalent + of @c cancelHTTPListener being called). + */ +- (NSURL *)startHTTPListener:(NSError **)returnError withPort:(uint16_t)port; + /*! @brief Starts listening on the loopback interface on a random available port, and returns a URL with the base address. Use the returned redirect URI to build a @c OIDExternalUserAgentRequest, and once you initiate the request, set the resulting @c OIDExternalUserAgentSession to @c currentAuthorizationFlow so the response can be handled. - @param error The error if an error occurred while starting the local HTTP server. + @param returnError The error if an error occurred while starting the local HTTP server. @return The URL containing the address of the server with the randomly assigned available port. @discussion Each instance of @c OIDRedirectHTTPHandler can only listen for a single response. Calling this more than once will result in the previous listener being cancelled (equivalent of @c cancelHTTPListener being called). */ -- (NSURL *)startHTTPListener:(NSError **)error; +- (NSURL *)startHTTPListener:(NSError **)returnError; /*! @brief Stops listening the loopback interface and sends an cancellation error (in the domain ::OIDGeneralErrorDomain, with the code ::OIDErrorCodeProgramCanceledAuthorizationFlow) to diff --git a/Source/macOS/OIDRedirectHTTPHandler.m b/Source/macOS/OIDRedirectHTTPHandler.m index 28953ad07..6b1814911 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.m +++ b/Source/macOS/OIDRedirectHTTPHandler.m @@ -64,13 +64,14 @@ - (instancetype)initWithSuccessURL:(nullable NSURL *)successURL { return self; } -- (NSURL *)startHTTPListener:(NSError **)returnError { +- (NSURL *)startHTTPListener:(NSError **)returnError withPort:(uint16_t)port { // Cancels any pending requests. [self cancelHTTPListener]; // Starts a HTTP server on the loopback interface. // By not specifying a port, a random available one will be assigned. _httpServ = [[HTTPServer alloc] init]; + [_httpServ setPort:port]; [_httpServ setDelegate:self]; NSError *error = nil; if (![_httpServ start:&error]) { @@ -91,6 +92,11 @@ - (NSURL *)startHTTPListener:(NSError **)returnError { return nil; } +- (NSURL *)startHTTPListener:(NSError **)returnError { + // A port of 0 requests a random available port + return [self startHTTPListener:returnError withPort:0]; +} + - (void)cancelHTTPListener { [self stopHTTPListener]; From 1f2aa580e2dd5366e7de479410cd1d0b9554e284 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 29 Mar 2019 08:59:30 -0700 Subject: [PATCH 063/204] Fix "AppAUth" typo in tvOS library name --- AppAuth.xcodeproj/project.pbxproj | 14 +++++++------- .../xcshareddata/xcschemes/AppAuth_tvOS.xcscheme | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 9e5286a18..a6711dbc3 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -201,7 +201,7 @@ 343AAA911E83478900F9D36E /* OIDTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D21C5D8243000EF209 /* OIDTokenRequest.m */; }; 343AAA921E83478900F9D36E /* OIDTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D41C5D8243000EF209 /* OIDTokenResponse.m */; }; 343AAA931E83478900F9D36E /* OIDTokenUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D61C5D8243000EF209 /* OIDTokenUtilities.m */; }; - 343AAAAF1E83489A00F9D36E /* AppAUth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 343AAAA61E83489A00F9D36E /* AppAUth.framework */; }; + 343AAAAF1E83489A00F9D36E /* AppAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 343AAAA61E83489A00F9D36E /* AppAuth.framework */; }; 343AAACB1E8348AA00F9D36E /* AppAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 343AAAC21E8348A900F9D36E /* AppAuth.framework */; }; 343AAAD91E83493D00F9D36E /* OIDRedirectHTTPHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAEBA1D582AF100EC285B /* OIDRedirectHTTPHandler.m */; }; 343AAADA1E83493D00F9D36E /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; @@ -650,7 +650,7 @@ 343AAA541E83463400F9D36E /* AppAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAA5C1E83463400F9D36E /* AppAuth_iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_iOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAA991E83487200F9D36E /* AppAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 343AAAA61E83489A00F9D36E /* AppAUth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAUth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 343AAAA61E83489A00F9D36E /* AppAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAAAE1E83489A00F9D36E /* AppAuth_tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_tvOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAAC21E8348A900F9D36E /* AppAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_macOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -781,7 +781,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 343AAAAF1E83489A00F9D36E /* AppAUth.framework in Frameworks */, + 343AAAAF1E83489A00F9D36E /* AppAuth.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -859,7 +859,7 @@ 343AAA541E83463400F9D36E /* AppAuth.framework */, 343AAA5C1E83463400F9D36E /* AppAuth_iOSTests.xctest */, 343AAA991E83487200F9D36E /* AppAuth.framework */, - 343AAAA61E83489A00F9D36E /* AppAUth.framework */, + 343AAAA61E83489A00F9D36E /* AppAuth.framework */, 343AAAAE1E83489A00F9D36E /* AppAuth_tvOSTests.xctest */, 343AAAC21E8348A900F9D36E /* AppAuth.framework */, 343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */, @@ -1398,7 +1398,7 @@ ); name = AppAuth_tvOS; productName = AppAuth_tvOS; - productReference = 343AAAA61E83489A00F9D36E /* AppAUth.framework */; + productReference = 343AAAA61E83489A00F9D36E /* AppAuth.framework */; productType = "com.apple.product-type.framework"; }; 343AAAAD1E83489A00F9D36E /* AppAuth_tvOSTests */ = { @@ -2630,7 +2630,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-tvOS"; - PRODUCT_NAME = AppAUth; + PRODUCT_NAME = AppAuth; SDKROOT = appletvos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = 3; @@ -2655,7 +2655,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-tvOS"; - PRODUCT_NAME = AppAUth; + PRODUCT_NAME = AppAuth; SDKROOT = appletvos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = 3; diff --git a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_tvOS.xcscheme b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_tvOS.xcscheme index 3e02fa6d4..47b0457ab 100644 --- a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_tvOS.xcscheme +++ b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_tvOS.xcscheme @@ -15,7 +15,7 @@ @@ -43,7 +43,7 @@ @@ -65,7 +65,7 @@ @@ -83,7 +83,7 @@ From ff1235080248febca66bca7281831d286687362e Mon Sep 17 00:00:00 2001 From: Cassiano Monteiro <47463041+cassianodialpad@users.noreply.github.com> Date: Fri, 29 Mar 2019 16:50:26 -0400 Subject: [PATCH 064/204] Fixed compatibility with ASWebAuthenticationSession/SFAuthenticationSession when in guided access mode ASWebAuthenticationSession and SFAuthenticationSession are not supported in guided access mode (rdar://40809553). --- Source/iOS/OIDExternalUserAgentIOS.m | 145 ++++++++++++++------------- 1 file changed, 76 insertions(+), 69 deletions(-) diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index aad10fa39..17a66ec15 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -61,82 +61,93 @@ - (BOOL)presentExternalUserAgentRequest:(id)request _externalUserAgentFlowInProgress = YES; _session = session; - BOOL openedSafari = NO; + BOOL openedUserAgent = NO; NSURL *requestURL = [request externalUserAgentRequestURL]; // iOS 12 and later, use ASWebAuthenticationSession if (@available(iOS 12.0, *)) { - __weak OIDExternalUserAgentIOS *weakSelf = self; - NSString *redirectScheme = request.redirectScheme; - ASWebAuthenticationSession *authenticationVC = - [[ASWebAuthenticationSession alloc] initWithURL:requestURL - callbackURLScheme:redirectScheme - completionHandler:^(NSURL * _Nullable callbackURL, - NSError * _Nullable error) { - __strong OIDExternalUserAgentIOS *strongSelf = weakSelf; - if (!strongSelf) { - return; - } - strongSelf->_webAuthenticationVC = nil; - if (callbackURL) { - [strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL]; - } else { - NSError *safariError = - [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow - underlyingError:error - description:nil]; - [strongSelf->_session failExternalUserAgentFlowWithError:safariError]; - } - }]; - _webAuthenticationVC = authenticationVC; - openedSafari = [authenticationVC start]; + // ASWebAuthenticationSession doesn't work with guided access (rdar://40809553) + if (!UIAccessibilityIsGuidedAccessEnabled()) { + __weak OIDExternalUserAgentIOS *weakSelf = self; + NSString *redirectScheme = request.redirectScheme; + ASWebAuthenticationSession *authenticationVC = + [[ASWebAuthenticationSession alloc] initWithURL:requestURL + callbackURLScheme:redirectScheme + completionHandler:^(NSURL * _Nullable callbackURL, + NSError * _Nullable error) { + __strong OIDExternalUserAgentIOS *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + strongSelf->_webAuthenticationVC = nil; + if (callbackURL) { + [strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL]; + } else { + NSError *safariError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow + underlyingError:error + description:nil]; + [strongSelf->_session failExternalUserAgentFlowWithError:safariError]; + } + }]; + _webAuthenticationVC = authenticationVC; + openedUserAgent = [authenticationVC start]; + } + } // iOS 11, use SFAuthenticationSession - } else if (@available(iOS 11.0, *)) { - __weak OIDExternalUserAgentIOS *weakSelf = self; - NSString *redirectScheme = request.redirectScheme; - SFAuthenticationSession *authenticationVC = - [[SFAuthenticationSession alloc] initWithURL:requestURL - callbackURLScheme:redirectScheme - completionHandler:^(NSURL * _Nullable callbackURL, - NSError * _Nullable error) { - __strong OIDExternalUserAgentIOS *strongSelf = weakSelf; - if (!strongSelf) { - return; - } - strongSelf->_authenticationVC = nil; - if (callbackURL) { - [strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL]; - } else { - NSError *safariError = - [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow - underlyingError:error - description:@"User cancelled."]; - [strongSelf->_session failExternalUserAgentFlowWithError:safariError]; - } - }]; - _authenticationVC = authenticationVC; - openedSafari = [authenticationVC start]; + if (@available(iOS 11.0, *)) { + // SFAuthenticationSession doesn't work with guided access (rdar://40809553) + if (!openedUserAgent && !UIAccessibilityIsGuidedAccessEnabled()) { + __weak OIDExternalUserAgentIOS *weakSelf = self; + NSString *redirectScheme = request.redirectScheme; + SFAuthenticationSession *authenticationVC = + [[SFAuthenticationSession alloc] initWithURL:requestURL + callbackURLScheme:redirectScheme + completionHandler:^(NSURL * _Nullable callbackURL, + NSError * _Nullable error) { + __strong OIDExternalUserAgentIOS *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + strongSelf->_authenticationVC = nil; + if (callbackURL) { + [strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL]; + } else { + NSError *safariError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow + underlyingError:error + description:@"User cancelled."]; + [strongSelf->_session failExternalUserAgentFlowWithError:safariError]; + } + }]; + _authenticationVC = authenticationVC; + openedUserAgent = [authenticationVC start]; + } + } // iOS 9 and 10, use SFSafariViewController - } else if (@available(iOS 9.0, *)) { - SFSafariViewController *safariVC = - [[SFSafariViewController alloc] initWithURL:requestURL]; - safariVC.delegate = self; - _safariVC = safariVC; - [_presentingViewController presentViewController:safariVC animated:YES completion:nil]; - openedSafari = YES; + if (@available(iOS 9.0, *)) { + if (!openedUserAgent) { + SFSafariViewController *safariVC = + [[SFSafariViewController alloc] initWithURL:requestURL]; + safariVC.delegate = self; + _safariVC = safariVC; + [_presentingViewController presentViewController:safariVC animated:YES completion:nil]; + openedUserAgent = YES; + } + } // iOS 8 and earlier, use mobile Safari - } else { - openedSafari = [[UIApplication sharedApplication] openURL:requestURL]; + if (!openedUserAgent){ + openedUserAgent = [[UIApplication sharedApplication] openURL:requestURL]; } - if (!openedSafari) { + if (!openedUserAgent) { [self cleanUp]; NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeSafariOpenError underlyingError:nil description:@"Unable to open Safari."]; [session failExternalUserAgentFlowWithError:safariError]; } - return openedSafari; + return openedUserAgent; } - (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(void))completion { @@ -154,21 +165,17 @@ - (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(voi [self cleanUp]; - if (@available(iOS 12.0, *)) { + if (webAuthenticationVC) { // dismiss the ASWebAuthenticationSession [webAuthenticationVC cancel]; if (completion) completion(); - } else if (@available(iOS 11.0, *)) { + } else if (authenticationVC) { // dismiss the SFAuthenticationSession [authenticationVC cancel]; if (completion) completion(); - } else if (@available(iOS 9.0, *)) { + } else if (safariVC) { // dismiss the SFSafariViewController - if (safariVC) { - [safariVC dismissViewControllerAnimated:YES completion:completion]; - } else { - if (completion) completion(); - } + [safariVC dismissViewControllerAnimated:YES completion:completion]; } else { if (completion) completion(); } From 6af08938ffe843f7b6862e8efbc930c532811a89 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Mon, 1 Apr 2019 18:01:51 -0700 Subject: [PATCH 065/204] Bump version to 1.0.0.rc1 --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 542aca7ba..88b38f3d6 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.0.0.beta1" + s.version = "1.0.0.rc1" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC From 351d0d586a05e98c1164fd14d81537b84e1ae98b Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 31 May 2019 12:26:07 -0700 Subject: [PATCH 066/204] Bump version to 1.0.0 ^_^ --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 88b38f3d6..ec0f75a87 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.0.0.rc1" + s.version = "1.0.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC From 4062c9cf0473c508b8aec32a9f55cfb1e1dd9448 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Wed, 8 May 2019 09:28:54 -0700 Subject: [PATCH 067/204] Clean up test scopes (#354) --- UnitTests/OIDURLQueryComponentTests.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UnitTests/OIDURLQueryComponentTests.m b/UnitTests/OIDURLQueryComponentTests.m index e8f08c042..65085f683 100644 --- a/UnitTests/OIDURLQueryComponentTests.m +++ b/UnitTests/OIDURLQueryComponentTests.m @@ -91,12 +91,12 @@ - (void)test_formurlencoded_decoding { "stm4iiN_0Qi-n4mEo-jL-85CvQ&scope=%@&authuser=0&session_state=ab78c20&prompt=consent#"; NSString *expectedDecodedScope = - @"https://www.example.com/auth/plus.me https://www.example.com/auth/userinfo.profile"; + @"https://www.example.com/auth/userinfo.email https://www.example.com/auth/userinfo.profile"; // Tests an encoded scope with a '+'-encoded space { NSString* encodedScope = - @"https://www.example.com/auth/plus.me+https://www.example.com/auth/userinfo.profile"; + @"https://www.example.com/auth/userinfo.email+https://www.example.com/auth/userinfo.profile"; NSString *authorizationResponse = [NSString stringWithFormat:responseURLtemplate,encodedScope]; OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] initWithURL:[NSURL URLWithString:authorizationResponse]]; @@ -108,7 +108,7 @@ - (void)test_formurlencoded_decoding { // Tests an encoded scope with a '%20'-encoded space { NSString* encodedScope = - @"https://www.example.com/auth/plus.me%20https://www.example.com/auth/userinfo.profile"; + @"https://www.example.com/auth/userinfo.email%20https://www.example.com/auth/userinfo.profile"; NSString *authorizationResponse = [NSString stringWithFormat:responseURLtemplate,encodedScope]; OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] initWithURL:[NSURL URLWithString:authorizationResponse]]; From d84c0335558b4af7ab21ccd67c73d03427acef38 Mon Sep 17 00:00:00 2001 From: Kevin Cheng Date: Fri, 31 May 2019 13:43:43 -0700 Subject: [PATCH 068/204] Make UIViewController optional in OIDExternalUserAgentIOS for iOS 11+ - Added overloading convenience method for iOS 11 to create OIDAuthState without passing ViewController - Made UIViewController optional in OIDExternalUserAgentIOS - Deprecated old authStateByPresentingAuthorizationRequest --- Source/iOS/OIDAuthState+IOS.h | 21 ++++++++++++++++++++- Source/iOS/OIDAuthState+IOS.m | 9 +++++++++ Source/iOS/OIDExternalUserAgentIOS.h | 7 +++---- Source/iOS/OIDExternalUserAgentIOS.m | 8 ++++++-- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/Source/iOS/OIDAuthState+IOS.h b/Source/iOS/OIDAuthState+IOS.h index 8afc5dcc4..0e44fbd20 100644 --- a/Source/iOS/OIDAuthState+IOS.h +++ b/Source/iOS/OIDAuthState+IOS.h @@ -43,7 +43,26 @@ NS_ASSUME_NONNULL_BEGIN + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest presentingViewController:(UIViewController *)presentingViewController - callback:(OIDAuthStateAuthorizationCallback)callback; + callback:(OIDAuthStateAuthorizationCallback)callback + NS_DEPRECATED_IOS(7, 11, "This method has been deprecated. Call\ + authStateByPresentingAuthorizationRequest without passing\ + UIViewController instead."); + +/*! @brief Convenience method for iOS 11+ devices 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 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) + authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest + callback:(OIDAuthStateAuthorizationCallback)callback API_AVAILABLE(ios(11)); @end diff --git a/Source/iOS/OIDAuthState+IOS.m b/Source/iOS/OIDAuthState+IOS.m index 5e7b1fee8..cd86a624f 100644 --- a/Source/iOS/OIDAuthState+IOS.m +++ b/Source/iOS/OIDAuthState+IOS.m @@ -34,4 +34,13 @@ @implementation OIDAuthState (IOS) callback:callback]; } ++ (id) + authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest + callback:(OIDAuthStateAuthorizationCallback)callback { + OIDExternalUserAgentIOS *externalUserAgent = [[OIDExternalUserAgentIOS alloc] init]; + return [self authStateByPresentingAuthorizationRequest:authorizationRequest + externalUserAgent:externalUserAgent + callback:callback]; +} + @end diff --git a/Source/iOS/OIDExternalUserAgentIOS.h b/Source/iOS/OIDExternalUserAgentIOS.h index 98c041c9b..2b9815629 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.h +++ b/Source/iOS/OIDExternalUserAgentIOS.h @@ -29,17 +29,16 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDExternalUserAgentIOS : NSObject -/*! @internal - @brief Unavailable. Please use @c initWithPresentingViewController: +/*! @brief The convenience initializer for devices with iOS 11+ */ -- (nonnull instancetype)init NS_UNAVAILABLE; +- (nullable instancetype)init API_AVAILABLE(ios(11)); /*! @brief The designated initializer. @param presentingViewController The view controller from which to present the \SFSafariViewController. */ - (nullable instancetype)initWithPresentingViewController: - (UIViewController *)presentingViewController + (nullable UIViewController *)presentingViewController NS_DESIGNATED_INITIALIZER; @end diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index 17a66ec15..3fea94251 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -43,8 +43,12 @@ @implementation OIDExternalUserAgentIOS { #pragma clang diagnostic pop } +- (nullable instancetype)init { + return [self initWithPresentingViewController:nil]; +} + - (nullable instancetype)initWithPresentingViewController: - (UIViewController *)presentingViewController { + (nullable UIViewController *)presentingViewController { self = [super init]; if (self) { _presentingViewController = presentingViewController; @@ -126,7 +130,7 @@ - (BOOL)presentExternalUserAgentRequest:(id)request } // iOS 9 and 10, use SFSafariViewController if (@available(iOS 9.0, *)) { - if (!openedUserAgent) { + if (!openedUserAgent && _presentingViewController) { SFSafariViewController *safariVC = [[SFSafariViewController alloc] initWithURL:requestURL]; safariVC.delegate = self; From 6ea2ac16dcf51040ed34a8a237638dcd8ca800aa Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 31 May 2019 14:20:08 -0700 Subject: [PATCH 069/204] Change the iat claim time skew to a constant Fix the iat error message. --- Source/OIDAuthorizationService.m | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 1a83ae082..41859f4a5 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -39,6 +39,10 @@ */ 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 @@ -467,12 +471,15 @@ + (void)performTokenRequest:(OIDTokenRequest *)request // 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) > 600) { + 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:@"Issued at time is more than 5 minutes before or after " - "the current time"]; + description:message]; dispatch_async(dispatch_get_main_queue(), ^{ callback(nil, invalidIDToken); }); From 053d47fa40e6d12dc9b9b9803160382a065b7e41 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 31 May 2019 14:38:56 -0700 Subject: [PATCH 070/204] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40e1da079..91e23822b 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ OIDAuthorizationRequest *request = clientId:kClientID scopes:@[OIDScopeOpenID, OIDScopeProfile] - redirectURL:KRedirectURI + redirectURL:kRedirectURI responseType:OIDResponseTypeCode additionalParameters:nil]; From 820793e3a2a6a1d0da9fdf2d6e444f972dd199ed Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 31 May 2019 14:36:51 -0700 Subject: [PATCH 071/204] Perform case-insensitive compare on scheme --- Source/OIDAuthorizationService.m | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 41859f4a5..acd58419b 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -96,12 +96,12 @@ - (BOOL)shouldHandleURL:(NSURL *)URL { NSURL *standardizedURL = [URL standardizedURL]; NSURL *standardizedRedirectURL = [_request.redirectURL standardizedURL]; - return OIDIsEqualIncludingNil(standardizedURL.scheme, standardizedRedirectURL.scheme) && - 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); + 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)resumeExternalUserAgentFlowWithURL:(NSURL *)URL { From 28fc6c6d9d2e4b274fb403b03b6236140d4ee64c Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 31 May 2019 17:22:04 -0700 Subject: [PATCH 072/204] Error when the JSON object isn't a NSDictionary --- Source/OIDServiceDiscovery.m | 7 +++++++ UnitTests/OIDServiceDiscoveryTests.m | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/Source/OIDServiceDiscovery.m b/Source/OIDServiceDiscovery.m index ca9628ef1..bbd11d738 100644 --- a/Source/OIDServiceDiscovery.m +++ b/Source/OIDServiceDiscovery.m @@ -93,6 +93,13 @@ - (nullable instancetype)initWithJSONData:(NSData *)serviceDiscoveryJSONData 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]; } diff --git a/UnitTests/OIDServiceDiscoveryTests.m b/UnitTests/OIDServiceDiscoveryTests.m index 3610c1e78..f030019ce 100644 --- a/UnitTests/OIDServiceDiscoveryTests.m +++ b/UnitTests/OIDServiceDiscoveryTests.m @@ -188,6 +188,10 @@ + (NSURL *)googleDiscoveryAuthorizationEndpoint { "ail_verified\",\"exp\",\"family_name\",\"given_name\",\"iat\",\"iss\",\"locale\",\"name\",\"" "picture\",\"sub\"]}"; +static NSString *const kDiscoveryDocumentNotDictionary = + @"[\"code\",\"token\",\"id_token\",\"code token\",\"code id_token\",\"token id_token\",\"code to" + "ken id_token\",\"none\"]"; + /*! @brief Tests that URLs are handled properly when converted from the dictionary's string representation. */ @@ -222,6 +226,18 @@ - (void)testErrorWhenBadFormat { XCTAssertEqual(error.code, OIDErrorCodeJSONDeserializationError, @""); } +/*! @brief Tests that we get an error when the root JSON object isn't a dictionary. + */ +- (void)testErrorWhenRootObjectNotNSDictionary { + NSError *error; + OIDServiceDiscovery *discovery = [[OIDServiceDiscovery alloc] initWithJSON:kDiscoveryDocumentNotDictionary error:&error]; + XCTAssertNil(discovery, @"When initializing a discovery document, it should not return an " + "instance if the root JSON object is not a NSDictionary."); + XCTAssertNotNil(error, @"There should be an error indicating we received bad JSON."); + XCTAssertEqualObjects(error.domain, OIDGeneralErrorDomain, @""); + XCTAssertEqual(error.code, OIDErrorCodeInvalidDiscoveryDocument, @""); +} + /*! @brief Tests that we get an error when the required fields aren't in the source dictionary. */ - (void)testErrorWhenMissingFields { From 68648b9518f497b82b476a92b93312a9d6604987 Mon Sep 17 00:00:00 2001 From: Lucas Farris Date: Thu, 11 Jan 2018 19:51:04 +0100 Subject: [PATCH 073/204] Added the End Session request/response for the RP-Initiated Logout --- AppAuth.xcodeproj/project.pbxproj | 72 ++++++++++ Source/AppAuth.h | 2 + Source/Framework/AppAuth.h | 2 + Source/OIDEndSessionRequest.h | 110 +++++++++++++++ Source/OIDEndSessionRequest.m | 187 ++++++++++++++++++++++++++ Source/OIDEndSessionResponse.h | 62 +++++++++ Source/OIDEndSessionResponse.m | 84 ++++++++++++ Source/OIDServiceDiscovery.h | 6 + Source/OIDServiceDiscovery.m | 5 + UnitTests/OIDEndSessionRequestTests.h | 31 +++++ UnitTests/OIDEndSessionRequestTests.m | 124 +++++++++++++++++ UnitTests/OIDServiceDiscoveryTests.m | 3 + 12 files changed, 688 insertions(+) create mode 100644 Source/OIDEndSessionRequest.h create mode 100644 Source/OIDEndSessionRequest.m create mode 100644 Source/OIDEndSessionResponse.h create mode 100644 Source/OIDEndSessionResponse.m create mode 100644 UnitTests/OIDEndSessionRequestTests.h create mode 100644 UnitTests/OIDEndSessionRequestTests.m diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index a6711dbc3..7a3f6cdbc 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -481,6 +481,36 @@ A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; + A6CEB11A2007E49C009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6CEB11B2007E49D009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6CEB11C2007E49E009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6CEB11D2007E49F009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6CEB11E2007E4A1009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6CEB11F2007E4A2009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6DEAB832017A7030022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB842017A7040022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB852017A7050022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB862017A7060022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB872017A70B0022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB882017A70B0022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB892017A70C0022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB8A2017A7140022AC32 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + A6DEAB8B2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + A6DEAB8C2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + A6DEAB8D2017A7170022AC32 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + A6DEABB02018ECE80022AC32 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB12018ECE80022AC32 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB22018ECE90022AC32 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB32018ECE90022AC32 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB42018ECF20022AC32 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB52018ECF30022AC32 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB62018ECF30022AC32 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB72018ECF40022AC32 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CF37C06E1F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + CF37C06F1F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + CF37C0701F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + CF37C0711F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + CF6431F41F228A980075B6B5 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -686,6 +716,12 @@ A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentRequest.h; sourceTree = ""; }; A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentIOS.m; path = iOS/OIDExternalUserAgentIOS.m; sourceTree = ""; }; A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentIOS.h; path = iOS/OIDExternalUserAgentIOS.h; sourceTree = ""; }; + A6CEB1172007E384009D492A /* OIDEndSessionRequestTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionRequestTests.h; sourceTree = ""; }; + A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionRequestTests.m; sourceTree = ""; }; + CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionRequest.h; sourceTree = ""; }; + CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionRequest.m; sourceTree = ""; }; + CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionResponse.h; sourceTree = ""; }; + CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionResponse.m; sourceTree = ""; }; F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OIDAuthState+IOS.m"; path = "iOS/OIDAuthState+IOS.m"; sourceTree = ""; }; F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OIDAuthorizationService+IOS.m"; path = "iOS/OIDAuthorizationService+IOS.m"; sourceTree = ""; }; F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OIDAuthorizationService+IOS.h"; path = "iOS/OIDAuthorizationService+IOS.h"; sourceTree = ""; }; @@ -900,6 +936,10 @@ A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */, 341741C31C5D8243000EF209 /* OIDFieldMapping.h */, 341741C41C5D8243000EF209 /* OIDFieldMapping.m */, + CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */, + CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */, + CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */, + CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */, 60140F7E1DE4335200DA0DC3 /* OIDRegistrationResponse.h */, 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */, 60140F7D1DE42E3000DA0DC3 /* OIDRegistrationRequest.h */, @@ -945,6 +985,8 @@ 60140F811DE43B4D00DA0DC3 /* OIDRegistrationRequestTests.h */, 341742061C5D82D3000EF209 /* OIDGrantTypesTests.m */, 341742071C5D82D3000EF209 /* OIDResponseTypesTests.m */, + A6CEB1172007E384009D492A /* OIDEndSessionRequestTests.h */, + A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */, 341742081C5D82D3000EF209 /* OIDScopesTests.m */, 341742091C5D82D3000EF209 /* OIDServiceConfigurationTests.h */, 3417420A1C5D82D3000EF209 /* OIDServiceConfigurationTests.m */, @@ -1081,6 +1123,7 @@ 343AAAF71E83499000F9D36E /* OIDTokenRequest.h in Headers */, 343AAAF41E83499000F9D36E /* OIDScopeUtilities.h in Headers */, 343AAAF81E83499000F9D36E /* OIDTokenResponse.h in Headers */, + A6DEABB42018ECF20022AC32 /* OIDEndSessionResponse.h in Headers */, 343AAAF61E83499000F9D36E /* OIDServiceDiscovery.h in Headers */, 343AAAF11E83499000F9D36E /* OIDGrantTypes.h in Headers */, 55A094CF20DFBB10000045D1 /* OIDURLSessionProvider.h in Headers */, @@ -1092,6 +1135,7 @@ 343AAAE91E83499000F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAAE51E83499000F9D36E /* OIDAuthorizationService.h in Headers */, 343AAAF01E83499000F9D36E /* OIDRegistrationRequest.h in Headers */, + A6DEABB02018ECE80022AC32 /* OIDEndSessionRequest.h in Headers */, 343AAAE71E83499000F9D36E /* OIDAuthState.h in Headers */, 343AAAED1E83499000F9D36E /* OIDErrorUtilities.h in Headers */, 343AAA6C1E83466B00F9D36E /* OIDAuthorizationService+IOS.h in Headers */, @@ -1110,6 +1154,7 @@ 343AAB091E83499100F9D36E /* OIDGrantTypes.h in Headers */, 343AAAFF1E83499100F9D36E /* OIDAuthState.h in Headers */, A6DEABA52018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */, + A6DEABB52018ECF30022AC32 /* OIDEndSessionResponse.h in Headers */, 343AAB0D1E83499100F9D36E /* OIDServiceConfiguration.h in Headers */, A6DEABA02018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAAFD1E83499100F9D36E /* OIDAuthorizationService.h in Headers */, @@ -1121,6 +1166,7 @@ A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */, 343AAB0B1E83499100F9D36E /* OIDScopes.h in Headers */, 343AAB9B1E834A8800F9D36E /* AppAuth.h in Headers */, + A6DEABB12018ECE80022AC32 /* OIDEndSessionRequest.h in Headers */, 343AAB001E83499100F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, 343AAB081E83499100F9D36E /* OIDRegistrationRequest.h in Headers */, 34A6632A1E871DD40060B664 /* OIDIDToken.h in Headers */, @@ -1142,6 +1188,7 @@ 343AAB211E83499200F9D36E /* OIDGrantTypes.h in Headers */, 343AAB171E83499200F9D36E /* OIDAuthState.h in Headers */, A6DEABA62018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */, + A6DEABB62018ECF30022AC32 /* OIDEndSessionResponse.h in Headers */, 343AAB251E83499200F9D36E /* OIDServiceConfiguration.h in Headers */, A6DEABA22018E4B60022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAB151E83499200F9D36E /* OIDAuthorizationService.h in Headers */, @@ -1153,6 +1200,7 @@ A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */, 343AAB231E83499200F9D36E /* OIDScopes.h in Headers */, 343AAB9C1E834A8900F9D36E /* AppAuth.h in Headers */, + A6DEABB22018ECE90022AC32 /* OIDEndSessionRequest.h in Headers */, 343AAB181E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, 343AAB201E83499200F9D36E /* OIDRegistrationRequest.h in Headers */, 34A6632B1E871DD40060B664 /* OIDIDToken.h in Headers */, @@ -1176,6 +1224,7 @@ 343AAAE01E83494400F9D36E /* OIDAuthState+Mac.h in Headers */, 343AAADD1E83494400F9D36E /* OIDRedirectHTTPHandler.h in Headers */, 343AAB3C1E83499200F9D36E /* OIDScopeUtilities.h in Headers */, + A6DEABB32018ECE90022AC32 /* OIDEndSessionRequest.h in Headers */, 343AAB3F1E83499200F9D36E /* OIDTokenRequest.h in Headers */, 55A094D220DFBB12000045D1 /* OIDURLSessionProvider.h in Headers */, 343AAB411E83499200F9D36E /* OIDTokenUtilities.h in Headers */, @@ -1187,6 +1236,7 @@ 343AAB3D1E83499200F9D36E /* OIDServiceConfiguration.h in Headers */, 343AAB351E83499200F9D36E /* OIDErrorUtilities.h in Headers */, 343AAB391E83499200F9D36E /* OIDGrantTypes.h in Headers */, + A6DEABB72018ECF40022AC32 /* OIDEndSessionResponse.h in Headers */, 343AAB341E83499200F9D36E /* OIDError.h in Headers */, 343AAB401E83499200F9D36E /* OIDTokenResponse.h in Headers */, 343AAB3A1E83499200F9D36E /* OIDResponseTypes.h in Headers */, @@ -1693,6 +1743,8 @@ 341310CC1E6F944B00D5DEE5 /* OIDServiceDiscovery.m in Sources */, 341310CA1E6F944B00D5DEE5 /* OIDScopeUtilities.m in Sources */, 340DAE5C1D5821AB00EC285B /* OIDAuthorizationService.m in Sources */, + CF37C06F1F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */, + A6DEAB832017A7030022AC32 /* OIDEndSessionResponse.m in Sources */, 341310CD1E6F944B00D5DEE5 /* OIDTokenRequest.m in Sources */, 341310C91E6F944B00D5DEE5 /* OIDScopes.m in Sources */, 341310CE1E6F944B00D5DEE5 /* OIDTokenResponse.m in Sources */, @@ -1716,6 +1768,7 @@ A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentIOS.m in Sources */, 341741E01C5D8243000EF209 /* OIDErrorUtilities.m in Sources */, 34A6632D1E871DD40060B664 /* OIDIDToken.m in Sources */, + CF6431F41F228A980075B6B5 /* OIDEndSessionResponse.m in Sources */, 341741EA1C5D8243000EF209 /* OIDTokenUtilities.m in Sources */, 341741E21C5D8243000EF209 /* OIDGrantTypes.m in Sources */, 60140F7C1DE42E1000DA0DC3 /* OIDRegistrationRequest.m in Sources */, @@ -1727,6 +1780,7 @@ 341741E61C5D8243000EF209 /* OIDServiceConfiguration.m in Sources */, 60140F7A1DE4276800DA0DC3 /* OIDClientMetadataParameters.m in Sources */, 341741DE1C5D8243000EF209 /* OIDAuthState.m in Sources */, + CF37C06E1F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */, 341741DD1C5D8243000EF209 /* OIDAuthorizationService.m in Sources */, 345AE747202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */, 341741EB1C5D8243000EF209 /* OIDURLQueryComponent.m in Sources */, @@ -1756,6 +1810,7 @@ 34A6638B1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 341742171C5D82D3000EF209 /* OIDAuthorizationRequestTests.m in Sources */, 0396974D1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m in Sources */, + A6CEB11A2007E49C009D492A /* OIDEndSessionRequestTests.m in Sources */, 3417421A1C5D82D3000EF209 /* OIDGrantTypesTests.m in Sources */, 3417421B1C5D82D3000EF209 /* OIDResponseTypesTests.m in Sources */, 60140F831DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m in Sources */, @@ -1774,6 +1829,7 @@ 341AA50A1E7F3A9B00FCA5C6 /* OIDScopesTests.m in Sources */, A5EEF29B20D821970044F470 /* OIDTokenUtilitiesTests.m in Sources */, 341AA50F1E7F3A9B00FCA5C6 /* OIDURLQueryComponentTests.m in Sources */, + A6CEB11B2007E49D009D492A /* OIDEndSessionRequestTests.m in Sources */, 341AA50B1E7F3A9B00FCA5C6 /* OIDServiceConfigurationTests.m in Sources */, 341AA50C1E7F3A9B00FCA5C6 /* OIDServiceDiscoveryTests.m in Sources */, 341AA5071E7F3A9B00FCA5C6 /* OIDAuthStateTests.m in Sources */, @@ -1796,6 +1852,7 @@ 341AA4FD1E7F3A9400FCA5C6 /* OIDScopesTests.m in Sources */, A5EEF29C20D821970044F470 /* OIDTokenUtilitiesTests.m in Sources */, 341AA5021E7F3A9400FCA5C6 /* OIDURLQueryComponentTests.m in Sources */, + A6CEB11C2007E49E009D492A /* OIDEndSessionRequestTests.m in Sources */, 341AA4FE1E7F3A9400FCA5C6 /* OIDServiceConfigurationTests.m in Sources */, 341AA4FF1E7F3A9400FCA5C6 /* OIDServiceDiscoveryTests.m in Sources */, 341AA4FA1E7F3A9400FCA5C6 /* OIDAuthStateTests.m in Sources */, @@ -1829,12 +1886,14 @@ 341310D31E6F944D00D5DEE5 /* OIDError.m in Sources */, 341310DE1E6F944D00D5DEE5 /* OIDTokenRequest.m in Sources */, 341310DF1E6F944D00D5DEE5 /* OIDTokenResponse.m in Sources */, + A6DEAB842017A7040022AC32 /* OIDEndSessionResponse.m in Sources */, 341310DC1E6F944D00D5DEE5 /* OIDServiceConfiguration.m in Sources */, 341310BF1E6F943C00D5DEE5 /* OIDClientMetadataParameters.m in Sources */, 341310E01E6F944D00D5DEE5 /* OIDTokenUtilities.m in Sources */, 341E709A1DE18796004353C1 /* OIDAuthorizationService.m in Sources */, 341310D91E6F944D00D5DEE5 /* OIDResponseTypes.m in Sources */, 341310E11E6F944D00D5DEE5 /* OIDURLQueryComponent.m in Sources */, + CF37C0701F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */, 341310D41E6F944D00D5DEE5 /* OIDErrorUtilities.m in Sources */, 341310D81E6F944D00D5DEE5 /* OIDGrantTypes.m in Sources */, ); @@ -1876,6 +1935,7 @@ A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentIOS.m in Sources */, 343AAA881E83478900F9D36E /* OIDFieldMapping.m in Sources */, 34A663311E871DD40060B664 /* OIDIDToken.m in Sources */, + A6DEAB862017A7060022AC32 /* OIDEndSessionResponse.m in Sources */, 343AAA841E83478900F9D36E /* OIDAuthState.m in Sources */, 343AAA701E83467D00F9D36E /* OIDAuthState+IOS.m in Sources */, 343AAA921E83478900F9D36E /* OIDTokenResponse.m in Sources */, @@ -1888,6 +1948,7 @@ 343AAA901E83478900F9D36E /* OIDServiceDiscovery.m in Sources */, 343AAA911E83478900F9D36E /* OIDTokenRequest.m in Sources */, 345AE748202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */, + A6DEAB8A2017A7140022AC32 /* OIDEndSessionRequest.m in Sources */, 343AAA6F1E83467D00F9D36E /* OIDAuthorizationService+IOS.m in Sources */, 343AAA8F1E83478900F9D36E /* OIDServiceConfiguration.m in Sources */, 343AAA891E83478900F9D36E /* OIDRegistrationResponse.m in Sources */, @@ -1920,6 +1981,7 @@ 343AAA731E8346B400F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAA761E8346B400F9D36E /* OIDGrantTypesTests.m in Sources */, 34A6638E1E8865090060B664 /* OIDRPProfileCode.m in Sources */, + A6CEB11D2007E49F009D492A /* OIDEndSessionRequestTests.m in Sources */, 343AAA741E8346B400F9D36E /* OIDAuthorizationResponseTests.m in Sources */, A5EEF29720D821120044F470 /* OIDTokenUtilitiesTests.m in Sources */, ); @@ -1941,8 +2003,10 @@ 343AAB781E8349B000F9D36E /* OIDScopeUtilities.m in Sources */, 343AAB731E8349B000F9D36E /* OIDRegistrationResponse.m in Sources */, 343AAB701E8349B000F9D36E /* OIDError.m in Sources */, + A6DEAB8B2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */, 343AAB7B1E8349B000F9D36E /* OIDTokenRequest.m in Sources */, 343AAB7C1E8349B000F9D36E /* OIDTokenResponse.m in Sources */, + A6DEAB872017A70B0022AC32 /* OIDEndSessionResponse.m in Sources */, 343AAB791E8349B000F9D36E /* OIDServiceConfiguration.m in Sources */, 343AAB6F1E8349B000F9D36E /* OIDClientMetadataParameters.m in Sources */, 343AAB7D1E8349B000F9D36E /* OIDTokenUtilities.m in Sources */, @@ -1970,8 +2034,10 @@ 343AAB641E8349B000F9D36E /* OIDScopeUtilities.m in Sources */, 343AAB5F1E8349B000F9D36E /* OIDRegistrationResponse.m in Sources */, 343AAB5C1E8349B000F9D36E /* OIDError.m in Sources */, + A6DEAB8C2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */, 343AAB671E8349B000F9D36E /* OIDTokenRequest.m in Sources */, 343AAB681E8349B000F9D36E /* OIDTokenResponse.m in Sources */, + A6DEAB882017A70B0022AC32 /* OIDEndSessionResponse.m in Sources */, 343AAB651E8349B000F9D36E /* OIDServiceConfiguration.m in Sources */, 343AAB5B1E8349B000F9D36E /* OIDClientMetadataParameters.m in Sources */, 343AAB691E8349B000F9D36E /* OIDTokenUtilities.m in Sources */, @@ -2001,6 +2067,7 @@ 343AAB7F1E8349CE00F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAB821E8349CE00F9D36E /* OIDGrantTypesTests.m in Sources */, 34A6638F1E8865090060B664 /* OIDRPProfileCode.m in Sources */, + A6CEB11E2007E4A1009D492A /* OIDEndSessionRequestTests.m in Sources */, 343AAB801E8349CE00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, A5EEF29820D8211A0044F470 /* OIDTokenUtilitiesTests.m in Sources */, ); @@ -2014,6 +2081,7 @@ 343AAADC1E83493D00F9D36E /* OIDAuthState+Mac.m in Sources */, 343AAADA1E83493D00F9D36E /* OIDAuthorizationService+Mac.m in Sources */, 343AAB4C1E8349AF00F9D36E /* OIDRegistrationRequest.m in Sources */, + A6DEAB8D2017A7170022AC32 /* OIDEndSessionRequest.m in Sources */, 343AAB451E8349AF00F9D36E /* OIDAuthorizationService.m in Sources */, 343AAB431E8349AF00F9D36E /* OIDAuthorizationRequest.m in Sources */, 343AAAD91E83493D00F9D36E /* OIDRedirectHTTPHandler.m in Sources */, @@ -2036,6 +2104,7 @@ 34AF736D1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */, 343AAB561E8349AF00F9D36E /* OIDURLQueryComponent.m in Sources */, 343AAB4E1E8349AF00F9D36E /* OIDResponseTypes.m in Sources */, + A6DEAB892017A70C0022AC32 /* OIDEndSessionResponse.m in Sources */, 343AAB4A1E8349AF00F9D36E /* OIDFieldMapping.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2058,6 +2127,7 @@ 343AAB8D1E8349CF00F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAB901E8349CF00F9D36E /* OIDGrantTypesTests.m in Sources */, 34A663901E8865090060B664 /* OIDRPProfileCode.m in Sources */, + A6CEB11F2007E4A2009D492A /* OIDEndSessionRequestTests.m in Sources */, 343AAB8E1E8349CF00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, A5EEF29920D8211B0044F470 /* OIDTokenUtilitiesTests.m in Sources */, ); @@ -2081,12 +2151,14 @@ 347424041E7F4BA000D3E6D6 /* OIDError.m in Sources */, 3474240F1E7F4BA000D3E6D6 /* OIDTokenRequest.m in Sources */, 347424101E7F4BA000D3E6D6 /* OIDTokenResponse.m in Sources */, + A6DEAB852017A7050022AC32 /* OIDEndSessionResponse.m in Sources */, 3474240D1E7F4BA000D3E6D6 /* OIDServiceConfiguration.m in Sources */, 347424031E7F4BA000D3E6D6 /* OIDClientMetadataParameters.m in Sources */, 347424111E7F4BA000D3E6D6 /* OIDTokenUtilities.m in Sources */, 347424011E7F4BA000D3E6D6 /* OIDAuthorizationService.m in Sources */, 3474240A1E7F4BA000D3E6D6 /* OIDResponseTypes.m in Sources */, 347424121E7F4BA000D3E6D6 /* OIDURLQueryComponent.m in Sources */, + CF37C0711F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */, 347424051E7F4BA000D3E6D6 /* OIDErrorUtilities.m in Sources */, 347424091E7F4BA000D3E6D6 /* OIDGrantTypes.m in Sources */, ); diff --git a/Source/AppAuth.h b/Source/AppAuth.h index d69772a64..5a6c2f1b1 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -40,6 +40,8 @@ #import "OIDTokenResponse.h" #import "OIDTokenUtilities.h" #import "OIDURLSessionProvider.h" +#import "OIDEndSessionRequest.h" +#import "OIDEndSessionResponse.h" #if TARGET_OS_TV #elif TARGET_OS_WATCH diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index 38631081f..dad6479d8 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -48,6 +48,8 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import +#import +#import #if TARGET_OS_TV #elif TARGET_OS_WATCH diff --git a/Source/OIDEndSessionRequest.h b/Source/OIDEndSessionRequest.h new file mode 100644 index 000000000..08e12af9a --- /dev/null +++ b/Source/OIDEndSessionRequest.h @@ -0,0 +1,110 @@ +/*! @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 + +@class OIDServiceConfiguration; + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDEndSessionRequest : NSObject { + OIDServiceConfiguration *_configuration; + NSURL *_postLogoutRedirectURL; + NSString *_idTokenHint; + NSString *_state; + NSDictionary *_additionalParameters; +} + +/*! @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 *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 *)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 *)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 diff --git a/Source/OIDEndSessionRequest.m b/Source/OIDEndSessionRequest.m new file mode 100644 index 000000000..df0a799e9 --- /dev/null +++ b/Source/OIDEndSessionRequest.m @@ -0,0 +1,187 @@ +/*! @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 + +@synthesize configuration = _configuration; +@synthesize postLogoutRedirectURL = _postLogoutRedirectURL; +@synthesize idTokenHint = _idTokenHint; +@synthesize state = _state; +@synthesize additionalParameters = _additionalParameters; + +- (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 *)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 *)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 - + +- (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.discoveryDocument.endSessionEndpoint, OIDMissingEndSessionEndpointMessage); + + // Construct the URL: + return [query URLByReplacingQueryInURL:_configuration.discoveryDocument.endSessionEndpoint]; +} + +@end diff --git a/Source/OIDEndSessionResponse.h b/Source/OIDEndSessionResponse.h new file mode 100644 index 000000000..6fc0131c3 --- /dev/null +++ b/Source/OIDEndSessionResponse.h @@ -0,0 +1,62 @@ +/*! @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 + +@class OIDEndSessionRequest; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Represents the response to an End Session request. + @see http://openid.net/specs/openid-connect-frontchannel-1_0.html + */ + +@interface OIDEndSessionResponse : NSObject { + OIDEndSessionRequest *_request; + NSString *_state; +} + +/*! @brief The request which was serviced. + */ +@property(nonatomic, readonly) OIDEndSessionRequest *request; + +/*! @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; + +/*! @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 *> *)parameters +NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/OIDEndSessionResponse.m b/Source/OIDEndSessionResponse.m new file mode 100644 index 000000000..7166722ec --- /dev/null +++ b/Source/OIDEndSessionResponse.m @@ -0,0 +1,84 @@ +/*! @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 "OIDEndSessionRequest.h" +#import "OIDDefines.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"; + +@implementation OIDEndSessionResponse + +@synthesize request = _request; +@synthesize state = _state; + +#pragma mark - Initializers + +- (instancetype)init +OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:)) + +- (instancetype)initWithRequest:(OIDEndSessionRequest *)request parameters:(NSDictionary *> *)parameters +{ + self = [super init]; + if (self) { + _request = [request copy]; + _state = [parameters[kStateKey] copy]; + } + return self; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(nullable NSZone *)zone { + // The documentation for NSCopying specifically advises us to return a reference to the original + // instance in the case where instances are immutable (as ours is): + // "Implement NSCopying by retaining the original instead of creating a new copy when the class + // and its contents are immutable." + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + OIDEndSessionRequest *request = [aDecoder decodeObjectOfClass:[OIDEndSessionRequest class] forKey:kRequestKey]; + NSString *state = [aDecoder decodeObjectOfClass:[NSString class] forKey:kStateKey]; + + self = [self initWithRequest:request parameters:@{}]; + if (self) { + _state = state; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_request forKey:kRequestKey]; + [aCoder encodeObject:_state forKey:kStateKey]; +} + +@end diff --git a/Source/OIDServiceDiscovery.h b/Source/OIDServiceDiscovery.h index 982d71005..577700834 100644 --- a/Source/OIDServiceDiscovery.h +++ b/Source/OIDServiceDiscovery.h @@ -78,6 +78,12 @@ NS_ASSUME_NONNULL_BEGIN */ @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 diff --git a/Source/OIDServiceDiscovery.m b/Source/OIDServiceDiscovery.m index bbd11d738..ca81108a8 100644 --- a/Source/OIDServiceDiscovery.m +++ b/Source/OIDServiceDiscovery.m @@ -30,6 +30,7 @@ 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"; @@ -232,6 +233,10 @@ - (nullable NSURL *)registrationEndpoint { return [NSURL URLWithString:_discoveryDictionary[kRegistrationEndpointKey]]; } +- (nullable NSURL *)endSessionEndpoint { + return [NSURL URLWithString:_discoveryDictionary[kEndSessionEndpointKey]]; +} + - (nullable NSArray *)scopesSupported { return _discoveryDictionary[kScopesSupportedKey]; } diff --git a/UnitTests/OIDEndSessionRequestTests.h b/UnitTests/OIDEndSessionRequestTests.h new file mode 100644 index 000000000..48f902757 --- /dev/null +++ b/UnitTests/OIDEndSessionRequestTests.h @@ -0,0 +1,31 @@ +/*! @file OIDServiceDiscoveryTests.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 + +@class OIDEndSessionRequest; + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDEndSessionRequestTests : XCTestCase + ++ (OIDEndSessionRequest *)testInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/UnitTests/OIDEndSessionRequestTests.m b/UnitTests/OIDEndSessionRequestTests.m new file mode 100644 index 000000000..4aadc6b57 --- /dev/null +++ b/UnitTests/OIDEndSessionRequestTests.m @@ -0,0 +1,124 @@ +/*! @file OIDServiceDiscoveryTests.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 "OIDEndSessionRequestTests.h" + +#import "OIDServiceDiscoveryTests.h" +#import "Source/OIDEndSessionRequest.h" +#import "Source/OIDServiceConfiguration.h" +#import "Source/OIDServiceDiscovery.h" + +/*! @brief Test value for the @c redirectURL property. + */ +static NSString *const kTestRedirectURL = @"http://www.google.com/"; + +/*! @brief Test key for the @c additionalParameters property. + */ +static NSString *const kTestAdditionalParameterKey = @"A"; + +/*! @brief Test value for the @c additionalParameters property. + */ +static NSString *const kTestAdditionalParameterValue = @"1"; + +/*! @brief Test value for the @c state property. + */ +static NSString *const kTestState = @"State"; + +/*! @brief Test value for the @c idTokenHint property. + */ +static NSString *const kTestIdTokenHint = @"id-token-hint"; + +@implementation OIDEndSessionRequestTests + ++ (OIDEndSessionRequest *)testInstance { + NSDictionary *additionalParameters = + @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + + OIDServiceDiscovery *discoveryDocument = [[OIDServiceDiscovery alloc] initWithDictionary:[OIDServiceDiscoveryTests completeServiceDiscoveryDictionary] error:nil]; + OIDServiceConfiguration *configuration = [[OIDServiceConfiguration alloc] initWithDiscoveryDocument:discoveryDocument]; + + return [[OIDEndSessionRequest alloc] initWithConfiguration:configuration + idTokenHint:kTestIdTokenHint + postLogoutRedirectURL:[NSURL URLWithString:kTestRedirectURL] + state:kTestState + additionalParameters:additionalParameters]; +} + +/*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying + process and checking to make sure the source and destination instances are equivalent. + */ +- (void)testCopying { + OIDEndSessionRequest *request = [[self class] testInstance]; + + XCTAssertEqualObjects(request.idTokenHint, kTestIdTokenHint); + XCTAssertEqualObjects(request.postLogoutRedirectURL, [NSURL URLWithString:kTestRedirectURL]); + XCTAssertEqualObjects(request.state, kTestState); + XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], + kTestAdditionalParameterValue); + + OIDEndSessionRequest *requestCopy = [request copy]; + + XCTAssertNotNil(requestCopy.configuration); + XCTAssertEqualObjects(requestCopy.configuration, request.configuration); + XCTAssertEqualObjects(requestCopy.postLogoutRedirectURL, request.postLogoutRedirectURL); + XCTAssertEqualObjects(requestCopy.state, request.state); + XCTAssertEqualObjects(requestCopy.idTokenHint, request.idTokenHint); +} + +/*! @brief Tests the @c NSSecureCoding by round-tripping an instance through the coding process and + checking to make sure the source and destination instances are equivalent. + */ +- (void)testSecureCoding { + OIDEndSessionRequest *request = [[self class] testInstance]; + + XCTAssertEqualObjects(request.idTokenHint, kTestIdTokenHint); + XCTAssertEqualObjects(request.postLogoutRedirectURL, [NSURL URLWithString:kTestRedirectURL]); + XCTAssertEqualObjects(request.state, kTestState); + XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], + kTestAdditionalParameterValue); + + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; + OIDEndSessionRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + + XCTAssertNotNil(requestCopy.configuration); + XCTAssertEqualObjects(requestCopy.configuration.authorizationEndpoint, + request.configuration.authorizationEndpoint); + XCTAssertEqualObjects(requestCopy.postLogoutRedirectURL, request.postLogoutRedirectURL); + XCTAssertEqualObjects(requestCopy.state, request.state); + XCTAssertEqualObjects(requestCopy.idTokenHint, request.idTokenHint); +} + +- (void)testLogoutRequestURL { + OIDEndSessionRequest *request = [[self class] testInstance]; + NSURL *endSessionRequestURL = request.endSessionRequestURL; + + NSURLComponents *components = [NSURLComponents componentsWithString:endSessionRequestURL.absoluteString]; + + XCTAssertTrue([endSessionRequestURL.absoluteString hasPrefix:@"https://www.example.com/logout"]); + + NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; + for (NSURLQueryItem *queryItem in components.queryItems) { + query[queryItem.name] = queryItem.value; + } + + XCTAssertEqualObjects(query[@"state"], kTestState); + XCTAssertEqualObjects(query[@"id_token_hint"], kTestIdTokenHint); + XCTAssertEqualObjects(query[@"post_logout_redirect_uri"], kTestRedirectURL); +} + +@end diff --git a/UnitTests/OIDServiceDiscoveryTests.m b/UnitTests/OIDServiceDiscoveryTests.m index f030019ce..b9a13ce86 100644 --- a/UnitTests/OIDServiceDiscoveryTests.m +++ b/UnitTests/OIDServiceDiscoveryTests.m @@ -39,6 +39,7 @@ 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"; @@ -102,6 +103,7 @@ + (NSDictionary *)completeServiceDiscoveryDictionary { kUserinfoEndpointKey : @"User Info Endpoint", kJWKSURLKey : @"http://www.example.com/jwks", kRegistrationEndpointKey : @"Registration Endpoint", + kEndSessionEndpointKey : @"https://www.example.com/logout", kScopesSupportedKey : @"Scopes Supported", kResponseTypesSupportedKey : @"Response Types Supported", kResponseModesSupportedKey : @"Response Modes Supported", @@ -487,6 +489,7 @@ - (void)testField_##_field_ { TestURLFieldBackedBy(userinfoEndpoint, kUserinfoEndpointKey, kTestURL) TestURLFieldBackedBy(jwksURL, kJWKSURLKey, kTestURL) TestURLFieldBackedBy(registrationEndpoint, kRegistrationEndpointKey, kTestURL) +TestURLFieldBackedBy(endSessionEndpoint, kEndSessionEndpointKey, kTestURL) TestFieldBackedBy(scopesSupported, kScopesSupportedKey, @"Scopes Supported") TestFieldBackedBy(responseTypesSupported, kResponseTypesSupportedKey, @"Response Types Supported") TestFieldBackedBy(responseModesSupported, kResponseModesSupportedKey, @"Response Modes Supported") From 49380af003851e9eeff33f925195025c1a36a007 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Mon, 25 Jun 2018 17:50:00 -0700 Subject: [PATCH 074/204] Add additionalParemters support to OIDEndSessionResponse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit – Switched constructor to fieldMap pattern like the rest of AppAuth – Corrected indentation. --- Source/OIDEndSessionResponse.h | 16 ++++-- Source/OIDEndSessionResponse.m | 92 ++++++++++++++++++++++++---------- 2 files changed, 76 insertions(+), 32 deletions(-) diff --git a/Source/OIDEndSessionResponse.h b/Source/OIDEndSessionResponse.h index 6fc0131c3..8fa0d5b64 100644 --- a/Source/OIDEndSessionResponse.h +++ b/Source/OIDEndSessionResponse.h @@ -23,24 +23,30 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief Represents the response to an End Session request. - @see http://openid.net/specs/openid-connect-frontchannel-1_0.html + @see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout */ @interface OIDEndSessionResponse : NSObject { - OIDEndSessionRequest *_request; - NSString *_state; + OIDEndSessionRequest *_request; + NSString *_state; + NSDictionary *> *additionalParameters; } /*! @brief The request which was serviced. */ @property(nonatomic, readonly) OIDEndSessionRequest *request; -/*! @brief REQUIRED if the "state" parameter was present in the client authorization request. The +/*! @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 *> *additionalParameters; + /*! @internal @brief Unavailable. Please use initWithParameters:. */ @@ -55,7 +61,7 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithRequest:(OIDEndSessionRequest *)request parameters:(NSDictionary *> *)parameters -NS_DESIGNATED_INITIALIZER; + NS_DESIGNATED_INITIALIZER; @end diff --git a/Source/OIDEndSessionResponse.m b/Source/OIDEndSessionResponse.m index 7166722ec..b337e4ef0 100644 --- a/Source/OIDEndSessionResponse.m +++ b/Source/OIDEndSessionResponse.m @@ -18,8 +18,9 @@ #import "OIDEndSessionResponse.h" -#import "OIDEndSessionRequest.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. */ @@ -29,56 +30,93 @@ */ static NSString *const kRequestKey = @"request"; +/*! @brief Key used to encode the @c additionalParameters property for + @c NSSecureCoding + */ +static NSString *const kAdditionalParametersKey = @"additionalParameters"; + @implementation OIDEndSessionResponse @synthesize request = _request; @synthesize state = _state; +@synthesize additionalParameters = _additionalParameters; #pragma mark - Initializers - (instancetype)init -OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:)) - -- (instancetype)initWithRequest:(OIDEndSessionRequest *)request parameters:(NSDictionary *> *)parameters -{ - self = [super init]; - if (self) { - _request = [request copy]; - _state = [parameters[kStateKey] copy]; - } - return self; + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:)) + +- (instancetype)initWithRequest:(OIDEndSessionRequest *)request + parameters:(NSDictionary *> *)parameters { + self = [super init]; + if (self) { + _request = [request copy]; + NSDictionary *> *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 *)fieldMap { + static NSMutableDictionary *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; + // 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; + return YES; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { - OIDEndSessionRequest *request = [aDecoder decodeObjectOfClass:[OIDEndSessionRequest class] forKey:kRequestKey]; - NSString *state = [aDecoder decodeObjectOfClass:[NSString class] forKey:kStateKey]; - - self = [self initWithRequest:request parameters:@{}]; - if (self) { - _state = state; - } - return self; + 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]; - [aCoder encodeObject:_state forKey:kStateKey]; + [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 From 9ea91070966ac24259722eb5d5bc7d7867b9843e Mon Sep 17 00:00:00 2001 From: William Denniss Date: Mon, 25 Jun 2018 17:50:33 -0700 Subject: [PATCH 075/204] Implement OIDExternalUserAgentRequest in OIDEndSessionRequest Fixed indentation. --- Source/OIDEndSessionRequest.h | 17 ++-- Source/OIDEndSessionRequest.m | 141 ++++++++++++++++++---------------- 2 files changed, 85 insertions(+), 73 deletions(-) diff --git a/Source/OIDEndSessionRequest.h b/Source/OIDEndSessionRequest.h index 08e12af9a..ca4af40dc 100644 --- a/Source/OIDEndSessionRequest.h +++ b/Source/OIDEndSessionRequest.h @@ -18,16 +18,19 @@ #import +#import "OIDExternalUserAgentRequest.h" + @class OIDServiceConfiguration; NS_ASSUME_NONNULL_BEGIN -@interface OIDEndSessionRequest : NSObject { - OIDServiceConfiguration *_configuration; - NSURL *_postLogoutRedirectURL; - NSString *_idTokenHint; - NSString *_state; - NSDictionary *_additionalParameters; +@interface OIDEndSessionRequest : NSObject + { + OIDServiceConfiguration *_configuration; + NSURL *_postLogoutRedirectURL; + NSString *_idTokenHint; + NSString *_state; + NSDictionary *_additionalParameters; } /*! @brief The service's configuration. @@ -42,7 +45,7 @@ NS_ASSUME_NONNULL_BEGIN */ @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 +/*! @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 diff --git a/Source/OIDEndSessionRequest.m b/Source/OIDEndSessionRequest.m index df0a799e9..8e0a6f393 100644 --- a/Source/OIDEndSessionRequest.m +++ b/Source/OIDEndSessionRequest.m @@ -40,7 +40,6 @@ */ static NSString *const kIdTokenHintKey = @"id_token_hint"; - /*! @brief Key used to encode the @c additionalParameters property for @c NSSecureCoding */ static NSString *const kAdditionalParametersKey = @"additionalParameters"; @@ -76,16 +75,16 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration state:(NSString *)state additionalParameters:(NSDictionary *)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; + 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 @@ -93,95 +92,105 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration postLogoutRedirectURL:(NSURL *)postLogoutRedirectURL additionalParameters:(NSDictionary *)additionalParameters { - return [self initWithConfiguration:configuration - idTokenHint:idTokenHint - postLogoutRedirectURL:postLogoutRedirectURL - state:[[self class] generateState] - additionalParameters: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; + // 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; + 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; + 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]; + [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]; + return [NSString stringWithFormat:@"<%@: %p, request: %@>", + NSStringFromClass([self class]), + (void *)self, + self.endSessionRequestURL]; } + (nullable NSString *)generateState { - return [OIDTokenUtilities randomURLSafeStringWithSize:kStateSizeBytes]; + 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]; + OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init]; - // Add any additional parameters the client has specified. - [query addParameters:_additionalParameters]; + // Add any additional parameters the client has specified. + [query addParameters:_additionalParameters]; - // Add optional parameters, as applicable. - if (_idTokenHint) { - [query addParameter:kIdTokenHintKey value:_idTokenHint]; - } + // Add optional parameters, as applicable. + if (_idTokenHint) { + [query addParameter:kIdTokenHintKey value:_idTokenHint]; + } - if (_postLogoutRedirectURL) { - [query addParameter:kPostLogoutRedirectURLKey value:_postLogoutRedirectURL.absoluteString]; - } + if (_postLogoutRedirectURL) { + [query addParameter:kPostLogoutRedirectURLKey value:_postLogoutRedirectURL.absoluteString]; + } - if (_state) { - [query addParameter:kStateKey value:_state]; - } + if (_state) { + [query addParameter:kStateKey value:_state]; + } - NSAssert(_configuration.discoveryDocument.endSessionEndpoint, OIDMissingEndSessionEndpointMessage); + NSAssert(_configuration.discoveryDocument.endSessionEndpoint, OIDMissingEndSessionEndpointMessage); - // Construct the URL: - return [query URLByReplacingQueryInURL:_configuration.discoveryDocument.endSessionEndpoint]; + // Construct the URL + return [query URLByReplacingQueryInURL:_configuration.discoveryDocument.endSessionEndpoint]; } @end From d62069a0fafee04def61431c8cf6cf589177dd42 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Mon, 25 Jun 2018 17:51:01 -0700 Subject: [PATCH 076/204] Add end_session_endpoint support to OIDServiceConfiguration --- Source/OIDEndSessionRequest.m | 4 ++-- Source/OIDServiceConfiguration.h | 16 ++++++++++++++++ Source/OIDServiceConfiguration.m | 28 ++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/Source/OIDEndSessionRequest.m b/Source/OIDEndSessionRequest.m index 8e0a6f393..f17981514 100644 --- a/Source/OIDEndSessionRequest.m +++ b/Source/OIDEndSessionRequest.m @@ -187,10 +187,10 @@ - (NSURL *)endSessionRequestURL { [query addParameter:kStateKey value:_state]; } - NSAssert(_configuration.discoveryDocument.endSessionEndpoint, OIDMissingEndSessionEndpointMessage); + NSAssert(_configuration.endSessionEndpoint, OIDMissingEndSessionEndpointMessage); // Construct the URL - return [query URLByReplacingQueryInURL:_configuration.discoveryDocument.endSessionEndpoint]; + return [query URLByReplacingQueryInURL:_configuration.endSessionEndpoint]; } @end diff --git a/Source/OIDServiceConfiguration.h b/Source/OIDServiceConfiguration.h index 2221a1da7..a072a478f 100644 --- a/Source/OIDServiceConfiguration.h +++ b/Source/OIDServiceConfiguration.h @@ -50,6 +50,10 @@ typedef void (^OIDServiceConfigurationCreated) */ @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; @@ -92,6 +96,18 @@ typedef void (^OIDServiceConfigurationCreated) 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. */ diff --git a/Source/OIDServiceConfiguration.m b/Source/OIDServiceConfiguration.m index 517c955bd..0f39bfb06 100644 --- a/Source/OIDServiceConfiguration.m +++ b/Source/OIDServiceConfiguration.m @@ -38,6 +38,10 @@ */ 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"; @@ -50,6 +54,7 @@ - (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; @@ -67,6 +72,7 @@ - (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]; @@ -75,6 +81,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint _tokenEndpoint = [tokenEndpoint copy]; _issuer = [issuer copy]; _registrationEndpoint = [registrationEndpoint copy]; + _endSessionEndpoint = [endSessionEndpoint copy]; _discoveryDocument = [discoveryDocument copy]; } return self; @@ -86,6 +93,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:tokenEndpoint issuer:nil registrationEndpoint:nil + endSessionEndpoint:nil discoveryDocument:nil]; } @@ -96,6 +104,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:tokenEndpoint issuer:nil registrationEndpoint:registrationEndpoint + endSessionEndpoint:nil discoveryDocument:nil]; } @@ -106,6 +115,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:tokenEndpoint issuer:issuer registrationEndpoint:nil + endSessionEndpoint:nil discoveryDocument:nil]; } @@ -117,6 +127,20 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)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]; } @@ -125,6 +149,7 @@ - (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *) discoveryDocum tokenEndpoint:discoveryDocument.tokenEndpoint issuer:discoveryDocument.issuer registrationEndpoint:discoveryDocument.registrationEndpoint + endSessionEndpoint:discoveryDocument.endSessionEndpoint discoveryDocument:discoveryDocument]; } @@ -153,6 +178,8 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { 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; @@ -165,6 +192,7 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { tokenEndpoint:tokenEndpoint issuer:issuer registrationEndpoint:registrationEndpoint + endSessionEndpoint:endSessionEndpoint discoveryDocument:discoveryDocument]; } From 1b8fd7906ea7009bbc2b3b28c1b5ce793549a966 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Tue, 26 Jun 2018 11:09:37 -0700 Subject: [PATCH 077/204] Implement OIDC RP-initiated logout actions --- Source/OIDAuthorizationService.h | 23 +++++ Source/OIDAuthorizationService.m | 140 ++++++++++++++++++++++++++++++- 2 files changed, 161 insertions(+), 2 deletions(-) diff --git a/Source/OIDAuthorizationService.h b/Source/OIDAuthorizationService.h index 31c790311..c8fee5358 100644 --- a/Source/OIDAuthorizationService.h +++ b/Source/OIDAuthorizationService.h @@ -21,6 +21,8 @@ @class OIDAuthorization; @class OIDAuthorizationRequest; @class OIDAuthorizationResponse; +@class OIDEndSessionRequest; +@class OIDEndSessionResponse; @class OIDRegistrationRequest; @class OIDRegistrationResponse; @class OIDServiceConfiguration; @@ -47,6 +49,13 @@ typedef void (^OIDDiscoveryCallback)(OIDServiceConfiguration *_Nullable configur 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. @@ -120,6 +129,20 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg externalUserAgent:(id)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) + presentEndSessionRequest:(OIDEndSessionRequest *)request + externalUserAgent:(id)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. diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index acd58419b..a55c697d1 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -21,6 +21,8 @@ #import "OIDAuthorizationRequest.h" #import "OIDAuthorizationResponse.h" #import "OIDDefines.h" +#import "OIDEndSessionRequest.h" +#import "OIDEndSessionResponse.h" #import "OIDErrorUtilities.h" #import "OIDExternalUserAgent.h" #import "OIDExternalUserAgentSession.h" @@ -92,9 +94,14 @@ - (void)cancel { }]; } -- (BOOL)shouldHandleURL:(NSURL *)URL { +/*! @brief Does the redirection URL equal another URL down to the path component? + @param URL The first redirect URI to compare. + @param redirectonURL 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 matchesRedirectonURL:(NSURL *)redirectonURL { NSURL *standardizedURL = [URL standardizedURL]; - NSURL *standardizedRedirectURL = [_request.redirectURL standardizedURL]; + NSURL *standardizedRedirectURL = [redirectonURL standardizedURL]; return [standardizedURL.scheme caseInsensitiveCompare:standardizedRedirectURL.scheme] == NSOrderedSame && OIDIsEqualIncludingNil(standardizedURL.user, standardizedRedirectURL.user) @@ -104,6 +111,10 @@ - (BOOL)shouldHandleURL:(NSURL *)URL { && OIDIsEqualIncludingNil(standardizedURL.path, standardizedRedirectURL.path); } +- (BOOL)shouldHandleURL:(NSURL *)URL { + return [[self class] URL:URL matchesRedirectonURL:_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]) { @@ -178,6 +189,121 @@ - (void)didFinishWithResponse:(nullable OIDAuthorizationResponse *)response @end +@interface OIDEndSessionImplementation : NSObject { + // private variables + OIDEndSessionRequest *_request; + id _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)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 { + [_externalUserAgent dismissExternalUserAgentAnimated:YES completion:^{ + NSError *error = [OIDErrorUtilities + errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow + underlyingError:nil + description:nil]; + [self didFinishWithResponse:nil error:error]; + }]; +} + +- (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 + matchesRedirectonURL:_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 @@ -272,6 +398,16 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL return flowSession; } ++ (id) + presentEndSessionRequest:(OIDEndSessionRequest *)request + externalUserAgent:(id)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 { From d5e8f6af09bea85027b2cd9ef091e1f130ed84dc Mon Sep 17 00:00:00 2001 From: Mattio Date: Thu, 11 Oct 2018 11:49:47 -0400 Subject: [PATCH 078/204] Add the end session endpoint to -description and -encodeWithCoder:, synthesize the endSessionEndpoint property for consistency --- Source/OIDServiceConfiguration.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/OIDServiceConfiguration.m b/Source/OIDServiceConfiguration.m index 0f39bfb06..ca48a8c33 100644 --- a/Source/OIDServiceConfiguration.m +++ b/Source/OIDServiceConfiguration.m @@ -202,6 +202,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:_issuer forKey:kIssuerKey]; [aCoder encodeObject:_registrationEndpoint forKey:kRegistrationEndpointKey]; [aCoder encodeObject:_discoveryDocument forKey:kDiscoveryDocumentKey]; + [aCoder encodeObject:_endSessionEndpoint forKey:kEndSessionEndpointKey]; } #pragma mark - description @@ -209,10 +210,11 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { - (NSString *)description { return [NSString stringWithFormat: @"OIDServiceConfiguration authorizationEndpoint: %@, tokenEndpoint: %@, " - "registrationEndpoint: %@, discoveryDocument: [%@]", + "registrationEndpoint: %@, endSessionEndpoint: %@, discoveryDocument: [%@]", _authorizationEndpoint, _tokenEndpoint, _registrationEndpoint, + _endSessionEndpoint, _discoveryDocument]; } From ea5ad45d356a0219aec6e928952add1686cce974 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 18 Oct 2018 10:31:39 -0700 Subject: [PATCH 079/204] Remove support for 32-bit macOS from EndSession models Mirroring changes made in eb59c89a5ce2224f6c882eba82e394cfd591c7bf. --- Source/OIDEndSessionRequest.h | 8 +------- Source/OIDEndSessionRequest.m | 6 ------ Source/OIDEndSessionResponse.h | 6 +----- Source/OIDEndSessionResponse.m | 4 ---- 4 files changed, 2 insertions(+), 22 deletions(-) diff --git a/Source/OIDEndSessionRequest.h b/Source/OIDEndSessionRequest.h index ca4af40dc..4087e9fa9 100644 --- a/Source/OIDEndSessionRequest.h +++ b/Source/OIDEndSessionRequest.h @@ -25,13 +25,7 @@ NS_ASSUME_NONNULL_BEGIN @interface OIDEndSessionRequest : NSObject - { - OIDServiceConfiguration *_configuration; - NSURL *_postLogoutRedirectURL; - NSString *_idTokenHint; - NSString *_state; - NSDictionary *_additionalParameters; -} + /*! @brief The service's configuration. @remarks This configuration specifies how to connect to a particular OAuth provider. diff --git a/Source/OIDEndSessionRequest.m b/Source/OIDEndSessionRequest.m index f17981514..1e9eb0e22 100644 --- a/Source/OIDEndSessionRequest.m +++ b/Source/OIDEndSessionRequest.m @@ -55,12 +55,6 @@ @implementation OIDEndSessionRequest -@synthesize configuration = _configuration; -@synthesize postLogoutRedirectURL = _postLogoutRedirectURL; -@synthesize idTokenHint = _idTokenHint; -@synthesize state = _state; -@synthesize additionalParameters = _additionalParameters; - - (instancetype)init OID_UNAVAILABLE_USE_INITIALIZER( @selector(initWithConfiguration: diff --git a/Source/OIDEndSessionResponse.h b/Source/OIDEndSessionResponse.h index 8fa0d5b64..ab69b9305 100644 --- a/Source/OIDEndSessionResponse.h +++ b/Source/OIDEndSessionResponse.h @@ -26,11 +26,7 @@ NS_ASSUME_NONNULL_BEGIN @see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout */ -@interface OIDEndSessionResponse : NSObject { - OIDEndSessionRequest *_request; - NSString *_state; - NSDictionary *> *additionalParameters; -} +@interface OIDEndSessionResponse : NSObject /*! @brief The request which was serviced. */ diff --git a/Source/OIDEndSessionResponse.m b/Source/OIDEndSessionResponse.m index b337e4ef0..bedf0cd93 100644 --- a/Source/OIDEndSessionResponse.m +++ b/Source/OIDEndSessionResponse.m @@ -37,10 +37,6 @@ @implementation OIDEndSessionResponse -@synthesize request = _request; -@synthesize state = _state; -@synthesize additionalParameters = _additionalParameters; - #pragma mark - Initializers - (instancetype)init From e46bf966ba0189986f377719455c3656933a1566 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Mon, 10 Jun 2019 20:32:57 -0400 Subject: [PATCH 080/204] Adds a completion block to the session cancel Also ensure to call completion block in all the implementations of dismissExternalUserAgentAnimated:completion: --- Source/OIDAuthorizationService.m | 5 +++++ Source/OIDExternalUserAgentSession.h | 13 +++++++++++++ Source/iOS/OIDExternalUserAgentIOS.m | 1 + Source/macOS/OIDExternalUserAgentMac.m | 1 + 4 files changed, 20 insertions(+) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index a55c697d1..30e6f0040 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -86,11 +86,16 @@ - (void)presentAuthorizationWithExternalUserAgent:(id)exte } - (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(); }]; } diff --git a/Source/OIDExternalUserAgentSession.h b/Source/OIDExternalUserAgentSession.h index f0ee72320..3b886a6c3 100644 --- a/Source/OIDExternalUserAgentSession.h +++ b/Source/OIDExternalUserAgentSession.h @@ -16,6 +16,7 @@ limitations under the License. */ +NS_ASSUME_NONNULL_BEGIN /*! @brief Represents an in-flight external user-agent session. */ @@ -30,6 +31,16 @@ */ - (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. @@ -50,3 +61,5 @@ - (void)failExternalUserAgentFlowWithError:(NSError *)error; @end + +NS_ASSUME_NONNULL_END diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index 3fea94251..6303d6d8e 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -157,6 +157,7 @@ - (BOOL)presentExternalUserAgentRequest:(id)request - (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(void))completion { if (!_externalUserAgentFlowInProgress) { // Ignore this call if there is no authorization flow in progress. + if (completion) completion(); return; } diff --git a/Source/macOS/OIDExternalUserAgentMac.m b/Source/macOS/OIDExternalUserAgentMac.m index d064f406a..4d6eadf3c 100644 --- a/Source/macOS/OIDExternalUserAgentMac.m +++ b/Source/macOS/OIDExternalUserAgentMac.m @@ -56,6 +56,7 @@ - (BOOL)presentExternalUserAgentRequest:(id)request - (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(void))completion { if (!_externalUserAgentFlowInProgress) { // Ignore this call if there is no authorization flow in progress. + if (completion) completion(); return; } // Ideally the browser tab with the URL should be closed here, but the AppAuth library does not From e9c2c7c54d6fa6af0deb2d87f48296ec4bcb9024 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Mon, 10 Jun 2019 22:08:21 -0400 Subject: [PATCH 081/204] Implement missing cancelWithCompletion: in OIDEndSessionImplementation --- Source/OIDAuthorizationService.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 30e6f0040..106fca7b4 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -232,12 +232,17 @@ - (void)presentAuthorizationWithExternalUserAgent:(id)exte } - (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(); }]; } From 0ea66241a6c7ab8cab83a7b268568235ec7da548 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Fri, 14 Jun 2019 20:37:10 -0400 Subject: [PATCH 082/204] Added EndSessionRequest and EndSessionResponse classes to AppAuthCore target --- AppAuth.xcodeproj/project.pbxproj | 43 +++++++++++++++++++------------ 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 7a3f6cdbc..25294442a 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -9,6 +9,10 @@ /* Begin PBXBuildFile section */ 039697461FA8258D003D1FB2 /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; 0396974D1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0396974C1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m */; }; + 06C19E9A22B4749900C19CE1 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 06C19E9B22B474A200C19CE1 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + 06C19E9C22B474A600C19CE1 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 06C19E9D22B474AD00C19CE1 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; 340DAE571D5821A100EC285B /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; 340DAE581D5821A100EC285B /* OIDExternalUserAgentMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentMac.m */; }; 340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */; }; @@ -466,21 +470,6 @@ A5EEF29A20D821960044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; A5EEF29B20D821970044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; A5EEF29C20D821970044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; - A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEAB9F2018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABA02018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABA22018E4B60022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABA32018E4B70022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABA42018E4B90022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABA52018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABA62018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABA72018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; - A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; A6CEB11A2007E49C009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; A6CEB11B2007E49D009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; A6CEB11C2007E49E009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; @@ -498,6 +487,21 @@ A6DEAB8B2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; A6DEAB8C2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; A6DEAB8D2017A7170022AC32 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEAB9E2018E4AE0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEAB9F2018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA02018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA22018E4B60022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA32018E4B70022AC32 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA42018E4B90022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA52018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA62018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABA72018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; + A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; A6DEABB02018ECE80022AC32 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEABB12018ECE80022AC32 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEABB22018ECE90022AC32 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -711,13 +715,13 @@ 60140F841DE43C8C00DA0DC3 /* OIDRegistrationResponseTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRegistrationResponseTests.h; sourceTree = ""; }; 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRegistrationResponseTests.m; sourceTree = ""; }; A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDTokenUtilitiesTests.m; sourceTree = ""; }; + A6CEB1172007E384009D492A /* OIDEndSessionRequestTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionRequestTests.h; sourceTree = ""; }; + A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionRequestTests.m; sourceTree = ""; }; A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgent.h; sourceTree = ""; }; A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentSession.h; sourceTree = ""; }; A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentRequest.h; sourceTree = ""; }; A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentIOS.m; path = iOS/OIDExternalUserAgentIOS.m; sourceTree = ""; }; A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentIOS.h; path = iOS/OIDExternalUserAgentIOS.h; sourceTree = ""; }; - A6CEB1172007E384009D492A /* OIDEndSessionRequestTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionRequestTests.h; sourceTree = ""; }; - A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionRequestTests.m; sourceTree = ""; }; CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionRequest.h; sourceTree = ""; }; CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionRequest.m; sourceTree = ""; }; CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionResponse.h; sourceTree = ""; }; @@ -1085,10 +1089,12 @@ 342F42A42177B1FC00574F24 /* OIDExternalUserAgentRequest.h in Headers */, 342F42A52177B1FC00574F24 /* OIDAuthStateChangeDelegate.h in Headers */, 342F42A82177B1FC00574F24 /* OIDIDToken.h in Headers */, + 06C19E9C22B474A600C19CE1 /* OIDEndSessionRequest.h in Headers */, 342F42A92177B1FC00574F24 /* OIDResponseTypes.h in Headers */, 342F42AA2177B1FC00574F24 /* OIDTokenRequest.h in Headers */, 342F42AB2177B1FC00574F24 /* OIDScopeUtilities.h in Headers */, 342F42AC2177B1FC00574F24 /* OIDTokenResponse.h in Headers */, + 06C19E9A22B4749900C19CE1 /* OIDEndSessionResponse.h in Headers */, 342F42AD2177B1FC00574F24 /* OIDServiceDiscovery.h in Headers */, 342F42AE2177B1FC00574F24 /* OIDGrantTypes.h in Headers */, 342F42AF2177B1FC00574F24 /* OIDURLSessionProvider.h in Headers */, @@ -1610,6 +1616,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 340E73731C5D819B0076B1F6; @@ -1912,6 +1919,7 @@ 342F428E2177B1FC00574F24 /* OIDAuthorizationRequest.m in Sources */, 342F428F2177B1FC00574F24 /* OIDAuthorizationService.m in Sources */, 342F42902177B1FC00574F24 /* OIDClientMetadataParameters.m in Sources */, + 06C19E9D22B474AD00C19CE1 /* OIDEndSessionRequest.m in Sources */, 342F42912177B1FC00574F24 /* OIDTokenUtilities.m in Sources */, 342F42922177B1FC00574F24 /* OIDServiceDiscovery.m in Sources */, 342F42932177B1FC00574F24 /* OIDTokenRequest.m in Sources */, @@ -1921,6 +1929,7 @@ 342F42992177B1FC00574F24 /* OIDScopes.m in Sources */, 342F429A2177B1FC00574F24 /* OIDScopeUtilities.m in Sources */, 342F429B2177B1FC00574F24 /* OIDGrantTypes.m in Sources */, + 06C19E9B22B474A200C19CE1 /* OIDEndSessionResponse.m in Sources */, 342F429C2177B1FC00574F24 /* OIDRegistrationRequest.m in Sources */, 342F429D2177B1FC00574F24 /* OIDResponseTypes.m in Sources */, 342F429E2177B1FC00574F24 /* OIDAuthorizationResponse.m in Sources */, From 687a30e6957bef3e3b36f9cdb7fb45ec5c758bfd Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Fri, 14 Jun 2019 20:17:31 -0400 Subject: [PATCH 083/204] Fixed typo redirectonURL --> redirectionURL --- Source/OIDAuthorizationService.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 106fca7b4..53c521e8e 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -101,12 +101,12 @@ - (void)cancelWithCompletion:(nullable void (^)(void))completion { /*! @brief Does the redirection URL equal another URL down to the path component? @param URL The first redirect URI to compare. - @param redirectonURL The second 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 matchesRedirectonURL:(NSURL *)redirectonURL { ++ (BOOL)URL:(NSURL *)URL matchesRedirectionURL:(NSURL *)redirectionURL { NSURL *standardizedURL = [URL standardizedURL]; - NSURL *standardizedRedirectURL = [redirectonURL standardizedURL]; + NSURL *standardizedRedirectURL = [redirectionURL standardizedURL]; return [standardizedURL.scheme caseInsensitiveCompare:standardizedRedirectURL.scheme] == NSOrderedSame && OIDIsEqualIncludingNil(standardizedURL.user, standardizedRedirectURL.user) @@ -117,7 +117,7 @@ + (BOOL)URL:(NSURL *)URL matchesRedirectonURL:(NSURL *)redirectonURL { } - (BOOL)shouldHandleURL:(NSURL *)URL { - return [[self class] URL:URL matchesRedirectonURL:_request.redirectURL]; + return [[self class] URL:URL matchesRedirectionURL:_request.redirectURL]; } - (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL { @@ -250,7 +250,7 @@ - (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 - matchesRedirectonURL:_request.postLogoutRedirectURL]; + matchesRedirectionURL:_request.postLogoutRedirectURL]; } - (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL { From c2182d434c0249c1750a53101d88545b7e95c0cf Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Thu, 16 May 2019 10:57:34 -0700 Subject: [PATCH 084/204] Clean up OIDTokenRequest docs --- Source/OIDTokenRequest.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/OIDTokenRequest.h b/Source/OIDTokenRequest.h index 5cf9835c2..00e0c6e20 100644 --- a/Source/OIDTokenRequest.h +++ b/Source/OIDTokenRequest.h @@ -108,8 +108,10 @@ NS_ASSUME_NONNULL_BEGIN @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 @@ -131,9 +133,11 @@ NS_ASSUME_NONNULL_BEGIN @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 From 47c69927b3eb868dec93be3807ec24f669dccec7 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Fri, 31 May 2019 11:27:42 -0700 Subject: [PATCH 085/204] Clean up OIDAuthorizationResponse docs --- Source/OIDAuthorizationResponse.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OIDAuthorizationResponse.h b/Source/OIDAuthorizationResponse.h index 8aa7d06f5..e7552fe59 100644 --- a/Source/OIDAuthorizationResponse.h +++ b/Source/OIDAuthorizationResponse.h @@ -90,7 +90,7 @@ NS_ASSUME_NONNULL_BEGIN NSDictionary *> *additionalParameters; /*! @internal - @brief Unavailable. Please use initWithParameters:. + @brief Unavailable. Please use initWithRequest:parameters:. */ - (instancetype)init NS_UNAVAILABLE; From ae38a4a96d7c090c115b439d14b8b4da7b85196a Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Fri, 31 May 2019 11:33:56 -0700 Subject: [PATCH 086/204] Clean up OIDAuthorizationRequest docs --- Source/OIDAuthorizationRequest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OIDAuthorizationRequest.h b/Source/OIDAuthorizationRequest.h index eff3c6482..594f01d87 100644 --- a/Source/OIDAuthorizationRequest.h +++ b/Source/OIDAuthorizationRequest.h @@ -136,7 +136,7 @@ extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256; /*! @internal @brief Unavailable. Please use - @c initWithConfiguration:clientId:scopes:redirectURL:additionalParameters:. + @c initWithConfiguration:clientId:scopes:redirectURL:responseType:additionalParameters:. */ - (instancetype)init NS_UNAVAILABLE; From cf5b15a6e02ee509c5966c5dacbc45a0bef7c5cf Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Thu, 18 Jul 2019 12:17:22 -0700 Subject: [PATCH 087/204] Consider any azp claim when validating aud. --- Source/OIDAuthorizationService.m | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/OIDAuthorizationService.m b/Source/OIDAuthorizationService.m index 53c521e8e..cc749a3f9 100644 --- a/Source/OIDAuthorizationService.m +++ b/Source/OIDAuthorizationService.m @@ -574,10 +574,12 @@ + (void)performTokenRequest:(OIDTokenRequest *)request return; } - // OpenID Connect Core Section 3.1.3.7. rule #3 - // Validates that the audience of the ID Token matches the client ID. + // 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]) { + if (![idToken.audience containsObject:clientID] && + ![idToken.claims[@"azp"] isEqualToString:clientID]) { NSError *invalidIDToken = [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil From 808b0d2d54cd9895d1ffe264e6f4ade8ca9c563f Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 8 Aug 2019 14:46:19 -0700 Subject: [PATCH 088/204] Bump version to 1.1.0 --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index ec0f75a87..ac6ba452b 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.0.0" + s.version = "1.1.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC From d16caab0115fca52f7deb0e93866ab6b657574a2 Mon Sep 17 00:00:00 2001 From: Ilya Date: Mon, 8 Jul 2019 13:56:16 -0700 Subject: [PATCH 089/204] Fix iOS 13 compatibility issue --- Source/iOS/OIDExternalUserAgentIOS.m | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index 6303d6d8e..85875abcf 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -27,8 +27,13 @@ NS_ASSUME_NONNULL_BEGIN +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 +@interface OIDExternalUserAgentIOS () +@end +#else @interface OIDExternalUserAgentIOS () @end +#endif @implementation OIDExternalUserAgentIOS { UIViewController *_presentingViewController; @@ -94,6 +99,11 @@ - (BOOL)presentExternalUserAgentRequest:(id)request [strongSelf->_session failExternalUserAgentFlowWithError:safariError]; } }]; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + if (@available(iOS 13.0, *)) { + authenticationVC.presentationContextProvider = self; + } +#endif _webAuthenticationVC = authenticationVC; openedUserAgent = [authenticationVC start]; } @@ -214,6 +224,14 @@ - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller NS_AV [session failExternalUserAgentFlowWithError:error]; } +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 +#pragma mark - ASWebAuthenticationPresentationContextProviding + +- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session API_AVAILABLE(ios(13.0)){ + return UIApplication.sharedApplication.keyWindow; +} +#endif + @end NS_ASSUME_NONNULL_END From 01c5f557cc7d4234c6987c3f3e1d67c96b28463f Mon Sep 17 00:00:00 2001 From: William Denniss Date: Thu, 15 Aug 2019 17:10:49 -0700 Subject: [PATCH 090/204] Use the UIViewController's window as the presenting anchor Replaces use of the deprecated UIApplication.keyWindow. Undoes some changes made in https://github.com/openid/AppAuth-iOS/pull/340 (https://github.com/openid/AppAuth-iOS/commit/d84c0335558b4af7ab21ccd67c73d03427acef38) - Presenting UIViewController is required again for all versions of iOS - Reversed deprecation of methods Added assertion for iOS 13 to ensure the UIViewController is nonnull as required. --- Source/iOS/OIDAuthState+IOS.h | 24 ++++++------------------ Source/iOS/OIDExternalUserAgentIOS.h | 8 ++++---- Source/iOS/OIDExternalUserAgentIOS.m | 14 +++++++++++--- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/Source/iOS/OIDAuthState+IOS.h b/Source/iOS/OIDAuthState+IOS.h index 0e44fbd20..87ee5f05c 100644 --- a/Source/iOS/OIDAuthState+IOS.h +++ b/Source/iOS/OIDAuthState+IOS.h @@ -34,7 +34,8 @@ NS_ASSUME_NONNULL_BEGIN OIDAuthState.updateWithTokenResponse:error:). @param authorizationRequest The authorization request to present. @param presentingViewController The view controller from which to present the - @c SFSafariViewController. + @c SFSafariViewController. On iOS 13, the window of this UIViewController + is used as the ASPresentationAnchor. @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 @@ -43,26 +44,13 @@ NS_ASSUME_NONNULL_BEGIN + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest presentingViewController:(UIViewController *)presentingViewController - callback:(OIDAuthStateAuthorizationCallback)callback - NS_DEPRECATED_IOS(7, 11, "This method has been deprecated. Call\ - authStateByPresentingAuthorizationRequest without passing\ - UIViewController instead."); + callback:(OIDAuthStateAuthorizationCallback)callback; -/*! @brief Convenience method for iOS 11+ devices 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 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) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest - callback:(OIDAuthStateAuthorizationCallback)callback API_AVAILABLE(ios(11)); + callback:(OIDAuthStateAuthorizationCallback)callback API_AVAILABLE(ios(11)) + __deprecated_msg("This method will not work on iOS 13. Use " + "authStateByPresentingAuthorizationRequest:presentingViewController:callback:"); @end diff --git a/Source/iOS/OIDExternalUserAgentIOS.h b/Source/iOS/OIDExternalUserAgentIOS.h index 2b9815629..48ab3f848 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.h +++ b/Source/iOS/OIDExternalUserAgentIOS.h @@ -29,16 +29,16 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDExternalUserAgentIOS : NSObject -/*! @brief The convenience initializer for devices with iOS 11+ - */ -- (nullable instancetype)init API_AVAILABLE(ios(11)); +- (nullable instancetype)init API_AVAILABLE(ios(11)) + __deprecated_msg("This method will not work on iOS 13, use " + "initWithPresentingViewController:presentingViewController"); /*! @brief The designated initializer. @param presentingViewController The view controller from which to present the \SFSafariViewController. */ - (nullable instancetype)initWithPresentingViewController: - (nullable UIViewController *)presentingViewController + (UIViewController *)presentingViewController NS_DESIGNATED_INITIALIZER; @end diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index 85875abcf..bb55e8c99 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -49,13 +49,21 @@ @implementation OIDExternalUserAgentIOS { } - (nullable instancetype)init { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" return [self initWithPresentingViewController:nil]; +#pragma clang diagnostic pop } - (nullable instancetype)initWithPresentingViewController: - (nullable UIViewController *)presentingViewController { + (UIViewController *)presentingViewController { self = [super init]; if (self) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + NSAssert(presentingViewController != nil, + @"presentingViewController cannot be nil on iOS 13"); +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + _presentingViewController = presentingViewController; } return self; @@ -228,9 +236,9 @@ - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller NS_AV #pragma mark - ASWebAuthenticationPresentationContextProviding - (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session API_AVAILABLE(ios(13.0)){ - return UIApplication.sharedApplication.keyWindow; + return _presentingViewController.view.window; } -#endif +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 @end From b71d95170924c348693a0f39522140f0936b761b Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 16 Aug 2019 10:04:53 -0700 Subject: [PATCH 091/204] Bump version to 1.2.0 --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index ac6ba452b..a5005bc1d 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.1.0" + s.version = "1.2.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC From 49ded21278209b8dc3c0ddeff75851bd2c9d1c89 Mon Sep 17 00:00:00 2001 From: Lori Goldman <35976605+lorig135@users.noreply.github.com> Date: Tue, 20 Aug 2019 10:59:21 -0600 Subject: [PATCH 092/204] Update README.md Excellent! --- README.md | 58 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 91e23822b..e1643cfc7 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ supported due to the security and usability reasons explained in [Section 8.12 of RFC 8252](https://tools.ietf.org/html/rfc8252#section-8.12). It also supports the [PKCE](https://tools.ietf.org/html/rfc7636) extension to -OAuth which was created to secure authorization codes in public clients when +OAuth, which was created to secure authorization codes in public clients when custom URI scheme redirects are used. The library is friendly to other -extensions (standard or otherwise) with the ability to handle additional params +extensions (standard or otherwise), with the ability to handle additional params in all protocol requests and responses. ## Specification @@ -41,10 +41,10 @@ Safari) on earlier versions. Both Custom URI Schemes (all supported versions of iOS) and Universal Links (iOS 9+) can be used with the library. -In general, AppAuth can work with any Authorization Server (AS) that supports -native apps as documented in [RFC 8252](https://tools.ietf.org/html/rfc8252), +In general, AppAuth can work with any authorization server that supports +native apps, as documented in [RFC 8252](https://tools.ietf.org/html/rfc8252), either through custom URI scheme redirects, or universal links. -AS's that assume all clients are web-based or require clients to maintain +Authorization servers that assume all clients are web-based, or require clients to maintain confidentiality of the client secrets may not work well. ### macOS @@ -55,13 +55,13 @@ AppAuth supports macOS (OS X) 10.9 and above. #### Authorization Server Requirements -AppAuth for macOS supports both custom schemes, a loopback HTTP redirects +AppAuth for macOS supports both custom schemes; a loopback HTTP redirects via a small embedded server. -In general, AppAuth can work with any Authorization Server (AS) that supports -native apps as documented in [RFC 8252](https://tools.ietf.org/html/rfc8252), -either through custom URI scheme, or loopback HTTP redirects. -AS's that assume all clients are web-based or require clients to maintain +In general, AppAuth can work with any authorization server that supports +native apps, as documented in [RFC 8252](https://tools.ietf.org/html/rfc8252); +either through custom URI schemes, or loopback HTTP redirects. +Authorization servers that assume all clients are web-based, or require clients to maintain confidentiality of the client secrets may not work well. ## Try @@ -71,7 +71,7 @@ Want to try out AppAuth? Just run: pod try AppAuth Follow the instructions in [Examples/README.md](Examples/README.md) to configure -with your own OAuth client (you need to update 3 configuration points with your +with your own OAuth client (you need to update three configuration points with your client info to try the demo). ## Setup @@ -85,7 +85,7 @@ add the following line to your `Podfile`: pod 'AppAuth' -Then run `pod install`. +Then, run `pod install`. ### Carthage @@ -94,12 +94,12 @@ line to your `Cartfile`: github "openid/AppAuth-iOS" "master" -Then run `carthage bootstrap`. +Then, run `carthage bootstrap`. ### Static Library You can also use AppAuth as a static library. This requires linking the library -and your project and including the headers. Suggested configuration: +and your project, and including the headers. Here is a suggested configuration: 1. Create an Xcode Workspace. 2. Add `AppAuth.xcodeproj` to your Workspace. @@ -110,10 +110,10 @@ Linked Framework and Libraries" section of your target). ## Auth Flow -AppAuth supports both manual interaction with the Authorization Server +AppAuth supports both manual interaction with the authorization server where you need to perform your own token exchanges, as well as convenience methods that perform some of this logic for you. This example uses the -convenience method which returns either an `OIDAuthState` object, or an error. +convenience method, which returns either an `OIDAuthState` object, or an error. `OIDAuthState` is a class that keeps track of the authorization and token requests and responses, and provides a convenience method to call an API with @@ -159,8 +159,8 @@ NSURL *issuer = [NSURL URLWithString:@"https://accounts.google.com"]; ### Authorizing – iOS -First you need to have a property in your AppDelegate to hold the session, in -order to continue the authorization flow from the redirect. +First, you need to have a property in your AppDelegate to hold the session, in +order to continue the authorization flow from the redirect: ```objc // property of the app's AppDelegate @@ -178,9 +178,9 @@ And your main class, a property to store the auth state: Then, initiate the authorization request. By using the `authStateByPresentingAuthorizationRequest` convenience method, the token exchange will be performed automatically, and everything will be protected with -PKCE (if the server supports it). AppAuth also allows you to perform these +PKCE (if the server supports it). AppAuth also lets you perform these requests manually. See the `authNoCodeExchange` method in the included Example -app for a demonstration. +app for a demonstration: ```objc // builds authentication request @@ -216,7 +216,7 @@ appDelegate.currentAuthorizationFlow = The authorization response URL is returned to the app via the iOS openURL app delegate method, so you need to pipe this through to the current -authorization session (created in the previous session). +authorization session (created in the previous session): ```objc - (BOOL)application:(UIApplication *)app @@ -246,20 +246,20 @@ lifecycle for you. > #### :bulb: Alternative: Custom URI Schemes > Custom URI schemes are also supported on macOS, but some browsers display -> an interstitial which reduces the usability. For an example on using custom +> an interstitial, which reduces the usability. For an example on using custom > URI schemes with macOS, See `Example-Mac`. To receive the authorization response using a local HTTP server, first you need to have an instance variable in your main class to retain the HTTP redirect -handler. +handler: ```objc OIDRedirectHTTPHandler *_redirectHTTPHandler; ``` Then, as the port used by the local HTTP server varies, you need to start it -before building the authorization request in order to get the exact redirect -URI to use. +before building the authorization request, in order to get the exact redirect +URI to use: ```objc static NSString *const kSuccessURLString = @@ -277,7 +277,7 @@ Then, initiate the authorization request. By using the exchange will be performed automatically, and everything will be protected with PKCE (if the server supports it). By assigning the return value to the `OIDRedirectHTTPHandler`'s `currentAuthorizationFlow`, the authorization will -continue automatically once the user makes their choice. +continue automatically once the user makes their choice: ```objc // builds authentication request @@ -313,10 +313,10 @@ _redirectHTTPHandler.currentAuthorizationFlow = ### Making API Calls -AppAuth gives you the raw token information, if you need it. However we +AppAuth gives you the raw token information, if you need it. However, we recommend that users of the `OIDAuthState` convenience wrapper use the provided `performActionWithFreshTokens:` method to perform their API calls to avoid -needing to worry about token freshness. +needing to worry about token freshness: ```objc [_authState performActionWithFreshTokens:^(NSString *_Nonnull accessToken, @@ -337,4 +337,4 @@ Browse the [API documentation](http://openid.github.io/AppAuth-iOS/docs/latest/a ## Included Samples -Sample apps that explore core AppAuth features are available for iOS and macOS, follow the instructions in [Examples/README.md](Examples/README.md) to get started. +Sample apps that explore core AppAuth features are available for iOS and macOS; follow the instructions in [Examples/README.md](Examples/README.md) to get started. From e133472423968831c8fe8c40596346759c4af361 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 4 Sep 2019 14:11:39 -0700 Subject: [PATCH 093/204] Update CHANGELOG --- CHANGELOG.md | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f2b53a88..6d00621dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,53 @@ # AppAuth for iOS and macOS Changelog ## Unreleased + +## 1.2.0 + +### Notable Changes + +1. Support for iOS 13 + +## 1.1.0 + +### Notable Changes + +1. [OpenID Connect RP-Initiated Logout](http://openid.net/specs/openid-connect-session-1_0.html#RPLogout) implemented +2. Added logic for the `azp` claim + +### Fixes + +1. Scheme comparison for redirects is now case insensitive +2. Improved error handling during discovery when a non-JSON document + is encountered. + +## 1.0.0 + +1.0.0! 🎉 + +### Notable Changes + +1. **All deprecated APIs removed.** Please ensure your code builds on + version 0.95.0 with no deprecation warnings before upgrading! + Notably, if you started with a version of AppAuth prior to 0.93.0 + you will need to follow the instructions in + [Upgrading to 0.93.0](#upgrading-to-0930) +2. Updated for iOS 12, and Xcode 10. **Xcode 10 is now required.** + NB. per policy, AppAuth supports many older versions of iOS and + macOS, but only the current Xcode toolchain. + If you need to stay on old versions of Xcode for some reason, stay + on the pre-1.0 releases. +3. macOS 32-bit support removed. If you need this support, stay on the + pre-1.0 releases. +4. `AppAuth/Core` subspec, and AppAuthCore Framework added to support + iOS extensions. + +## 1.0.0.beta2 (2018-09-27) + ### Notable Changes 1. `AppAuth/Core` subspec, and AppAuthCore Framework added to support -iOS extensions. + iOS extensions. ## 1.0.0.beta1 (2018-09-27) From e07ba3325e2b5e9cca1c7a022715fe98368dd18d Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Thu, 5 Sep 2019 12:13:46 -0400 Subject: [PATCH 094/204] import missing headers for OIDC logout in AppAuthCore.h --- Source/AppAuthCore.h | 3 ++- Source/CoreFramework/AppAuthCore.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/AppAuthCore.h b/Source/AppAuthCore.h index 83b22ae50..c30af4648 100644 --- a/Source/AppAuthCore.h +++ b/Source/AppAuthCore.h @@ -40,4 +40,5 @@ #import "OIDTokenResponse.h" #import "OIDTokenUtilities.h" #import "OIDURLSessionProvider.h" - +#import "OIDEndSessionRequest.h" +#import "OIDEndSessionResponse.h" diff --git a/Source/CoreFramework/AppAuthCore.h b/Source/CoreFramework/AppAuthCore.h index 14d518542..9db3f3c69 100644 --- a/Source/CoreFramework/AppAuthCore.h +++ b/Source/CoreFramework/AppAuthCore.h @@ -48,5 +48,6 @@ FOUNDATION_EXPORT const unsigned char AppAuthCoreVersionString[]; #import #import #import - +#import +#import From 4d24a7efc87b353037be62087f92cccb67f66095 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Mon, 28 Oct 2019 08:11:30 -0400 Subject: [PATCH 095/204] Update Travis config for Xcode 11 and iOS 13 --- .travis.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index a1ceff4d5..9aacbf321 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,17 @@ # Travis CI config for AppAuth # Use the `wwtd` gem to test locally. language: objective-c -osx_image: xcode10 +osx_image: xcode11 # Tests iOS, macOS and tvOS: both static library and framework schemes. env: # To generate this list, use `xcodebuild -showsdks` to get possible SDK values, and platforms. # Use `instruments -s devices` to get the device name. - - SCHEME=AppAuth-iOS DESTINATION="'platform=iOS Simulator,name=iPhone 6,OS=12.0'" SDK=iphonesimulator12.0 - - SCHEME=AppAuth_iOS DESTINATION="'platform=iOS Simulator,name=iPhone 6,OS=12.0'" SDK=iphonesimulator12.0 - - SCHEME=AppAuth-macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.14 - - SCHEME=AppAuth_macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.14 - - SCHEME=AppAuth-tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=12.0'" SDK=appletvsimulator12.0 - - SCHEME=AppAuth_tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=12.0'" SDK=appletvsimulator12.0 + - SCHEME=AppAuth-iOS DESTINATION="'platform=iOS Simulator,name=iPhone 6,OS=13.0'" SDK=iphonesimulator13.0 + - SCHEME=AppAuth_iOS DESTINATION="'platform=iOS Simulator,name=iPhone 6,OS=13.0'" SDK=iphonesimulator13.0 + - SCHEME=AppAuth-macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.15 + - SCHEME=AppAuth_macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.15 + - SCHEME=AppAuth-tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=13.0'" SDK=appletvsimulator13.0 + - SCHEME=AppAuth_tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=13.0'" SDK=appletvsimulator13.0 before_script: - sudo gem install xcpretty script: From b5447710d7a83efaa0c2d629a1cfbd2318c3b2d3 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Mon, 28 Oct 2019 08:37:07 -0400 Subject: [PATCH 096/204] Update Travis config to use iPhone 11 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9aacbf321..85fe4e005 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ osx_image: xcode11 env: # To generate this list, use `xcodebuild -showsdks` to get possible SDK values, and platforms. # Use `instruments -s devices` to get the device name. - - SCHEME=AppAuth-iOS DESTINATION="'platform=iOS Simulator,name=iPhone 6,OS=13.0'" SDK=iphonesimulator13.0 - - SCHEME=AppAuth_iOS DESTINATION="'platform=iOS Simulator,name=iPhone 6,OS=13.0'" SDK=iphonesimulator13.0 + - SCHEME=AppAuth-iOS DESTINATION="'platform=iOS Simulator,name=iPhone 11,OS=13.0'" SDK=iphonesimulator13.0 + - SCHEME=AppAuth_iOS DESTINATION="'platform=iOS Simulator,name=iPhone 11,OS=13.0'" SDK=iphonesimulator13.0 - SCHEME=AppAuth-macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.15 - SCHEME=AppAuth_macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.15 - SCHEME=AppAuth-tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=13.0'" SDK=appletvsimulator13.0 From b9f2551d71c476ce4419997a8e4ef67822bb0c44 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 20 Nov 2019 12:56:10 -0800 Subject: [PATCH 097/204] Fix typo in variable name --- .../Source/AppAuthExampleViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift index c88459745..fe9c27242 100644 --- a/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift +++ b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift @@ -228,7 +228,7 @@ extension AppAuthExampleViewController { let currentAccessToken: String? = self.authState?.lastTokenResponse?.accessToken - self.authState?.performAction() { (accessToken, idTOken, error) in + self.authState?.performAction() { (accessToken, idToken, error) in if error != nil { self.logMessage("Error fetching fresh tokens: \(error?.localizedDescription ?? "ERROR")") From c8478ced7f2bb278c1afa45422f322a85814fa8f Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Wed, 16 Oct 2019 08:33:32 -0400 Subject: [PATCH 098/204] Add my name to the AUTHORS file --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index b8f81d146..b181d5ace 100644 --- a/AUTHORS +++ b/AUTHORS @@ -11,4 +11,5 @@ Ping Identity equinux AG Craig Lane Hernan Zalazar +Julien Bodet From 7b6c272f515f3de23ccde0b2928744d176443dc3 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Tue, 15 Oct 2019 08:10:08 -0400 Subject: [PATCH 099/204] Support for Mac Catalyst Mac Catalyst support is included via a dedicated OIDExternalUserAgent, rather than modifying the iOS one. This allows the two user agents which are already different (the iOS one supports older versions of iOS with classes that are not in Catalyst) to evolve independently as needed. --- AppAuth.xcodeproj/project.pbxproj | 12 ++ Source/AppAuth.h | 3 +- Source/Framework/AppAuth.h | 3 +- Source/iOS/OIDAuthState+IOS.h | 2 +- Source/iOS/OIDAuthState+IOS.m | 14 +- Source/iOS/OIDAuthorizationService+IOS.m | 9 +- Source/iOS/OIDExternalUserAgentCatalyst.h | 46 ++++++ Source/iOS/OIDExternalUserAgentCatalyst.m | 139 ++++++++++++++++++ Source/iOS/OIDExternalUserAgentIOS.h | 1 + Source/iOS/OIDExternalUserAgentIOS.m | 8 +- .../OIDExternalUserAgentIOSCustomBrowser.h | 1 + .../OIDExternalUserAgentIOSCustomBrowser.m | 4 + 12 files changed, 231 insertions(+), 11 deletions(-) create mode 100644 Source/iOS/OIDExternalUserAgentCatalyst.h create mode 100644 Source/iOS/OIDExternalUserAgentCatalyst.m diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 25294442a..38d1fe396 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -515,6 +515,8 @@ CF37C0701F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; CF37C0711F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; CF6431F41F228A980075B6B5 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + F9A7082E2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A7082C2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F9A7082F2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A7082D2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -730,6 +732,8 @@ F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OIDAuthorizationService+IOS.m"; path = "iOS/OIDAuthorizationService+IOS.m"; sourceTree = ""; }; F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OIDAuthorizationService+IOS.h"; path = "iOS/OIDAuthorizationService+IOS.h"; sourceTree = ""; }; F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OIDAuthState+IOS.h"; path = "iOS/OIDAuthState+IOS.h"; sourceTree = ""; }; + F9A7082C2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentCatalyst.h; path = iOS/OIDExternalUserAgentCatalyst.h; sourceTree = ""; }; + F9A7082D2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentCatalyst.m; path = iOS/OIDExternalUserAgentCatalyst.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1064,6 +1068,8 @@ F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */, A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */, A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */, + F9A7082C2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h */, + F9A7082D2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m */, ); name = iOS; sourceTree = ""; @@ -1124,6 +1130,7 @@ 343AAAE81E83499000F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, 345AE749202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */, 343AAA6B1E83465500F9D36E /* AppAuth.h in Headers */, + F9A7082E2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h in Headers */, 34A663291E871DD40060B664 /* OIDIDToken.h in Headers */, 343AAAF21E83499000F9D36E /* OIDResponseTypes.h in Headers */, 343AAAF71E83499000F9D36E /* OIDTokenRequest.h in Headers */, @@ -1942,6 +1949,7 @@ buildActionMask = 2147483647; files = ( A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentIOS.m in Sources */, + F9A7082F2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m in Sources */, 343AAA881E83478900F9D36E /* OIDFieldMapping.m in Sources */, 34A663311E871DD40060B664 /* OIDIDToken.m in Sources */, A6DEAB862017A7060022AC32 /* OIDEndSessionResponse.m in Sources */, @@ -2537,6 +2545,7 @@ PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthCore; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2562,6 +2571,7 @@ PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthCore; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2586,6 +2596,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOS"; PRODUCT_NAME = AppAuth; SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2610,6 +2621,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOS"; PRODUCT_NAME = AppAuth; SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; diff --git a/Source/AppAuth.h b/Source/AppAuth.h index 5a6c2f1b1..cf3a0e265 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -45,11 +45,12 @@ #if TARGET_OS_TV #elif TARGET_OS_WATCH -#elif TARGET_OS_IOS +#elif TARGET_OS_IOS || TARGET_OS_MACCATALYST #import "OIDAuthState+IOS.h" #import "OIDAuthorizationService+IOS.h" #import "OIDExternalUserAgentIOS.h" #import "OIDExternalUserAgentIOSCustomBrowser.h" +#import "OIDExternalUserAgentCatalyst.h" #elif TARGET_OS_MAC #import "OIDAuthState+Mac.h" #import "OIDAuthorizationService+Mac.h" diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index dad6479d8..4c76aeb2c 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -53,11 +53,12 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #if TARGET_OS_TV #elif TARGET_OS_WATCH -#elif TARGET_OS_IOS +#elif TARGET_OS_IOS || TARGET_OS_MACCATALYST #import #import #import #import +#import "AppAuth/OIDExternalUserAgentCatalyst.h" #elif TARGET_OS_MAC #import #import diff --git a/Source/iOS/OIDAuthState+IOS.h b/Source/iOS/OIDAuthState+IOS.h index 87ee5f05c..559df9ac5 100644 --- a/Source/iOS/OIDAuthState+IOS.h +++ b/Source/iOS/OIDAuthState+IOS.h @@ -48,7 +48,7 @@ NS_ASSUME_NONNULL_BEGIN + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest - callback:(OIDAuthStateAuthorizationCallback)callback API_AVAILABLE(ios(11)) + callback:(OIDAuthStateAuthorizationCallback)callback API_AVAILABLE(ios(11)) API_UNAVAILABLE(macCatalyst) __deprecated_msg("This method will not work on iOS 13. Use " "authStateByPresentingAuthorizationRequest:presentingViewController:callback:"); diff --git a/Source/iOS/OIDAuthState+IOS.m b/Source/iOS/OIDAuthState+IOS.m index cd86a624f..41301ea25 100644 --- a/Source/iOS/OIDAuthState+IOS.m +++ b/Source/iOS/OIDAuthState+IOS.m @@ -17,8 +17,8 @@ */ #import "OIDAuthState+IOS.h" - #import "OIDExternalUserAgentIOS.h" +#import "OIDExternalUserAgentCatalyst.h" @implementation OIDAuthState (IOS) @@ -26,14 +26,19 @@ @implementation OIDAuthState (IOS) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthStateAuthorizationCallback)callback { - OIDExternalUserAgentIOS *externalUserAgent = - [[OIDExternalUserAgentIOS alloc] - initWithPresentingViewController:presentingViewController]; + id externalUserAgent; +#if TARGET_OS_MACCATALYST + externalUserAgent = [[OIDExternalUserAgentCatalyst alloc] + initWithPresentingViewController:presentingViewController]; +#else + externalUserAgent = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController:presentingViewController]; +#endif return [self authStateByPresentingAuthorizationRequest:authorizationRequest externalUserAgent:externalUserAgent callback:callback]; } +#if !TARGET_OS_MACCATALYST + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest callback:(OIDAuthStateAuthorizationCallback)callback { @@ -42,5 +47,6 @@ @implementation OIDAuthState (IOS) externalUserAgent:externalUserAgent callback:callback]; } +#endif @end diff --git a/Source/iOS/OIDAuthorizationService+IOS.m b/Source/iOS/OIDAuthorizationService+IOS.m index 7364f7f13..6242ebdbb 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.m +++ b/Source/iOS/OIDAuthorizationService+IOS.m @@ -17,8 +17,8 @@ */ #import "OIDAuthorizationService+IOS.h" - #import "OIDExternalUserAgentIOS.h" +#import "OIDExternalUserAgentCatalyst.h" NS_ASSUME_NONNULL_BEGIN @@ -27,8 +27,13 @@ @implementation OIDAuthorizationService (IOS) + (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthorizationCallback)callback { - OIDExternalUserAgentIOS *externalUserAgent = [[OIDExternalUserAgentIOS alloc] + id externalUserAgent; +#if TARGET_OS_MACCATALYST + externalUserAgent = [[OIDExternalUserAgentCatalyst alloc] initWithPresentingViewController:presentingViewController]; +#else + externalUserAgent = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController:presentingViewController]; +#endif return [self presentAuthorizationRequest:request externalUserAgent:externalUserAgent callback:callback]; } diff --git a/Source/iOS/OIDExternalUserAgentCatalyst.h b/Source/iOS/OIDExternalUserAgentCatalyst.h new file mode 100644 index 000000000..9f3487319 --- /dev/null +++ b/Source/iOS/OIDExternalUserAgentCatalyst.h @@ -0,0 +1,46 @@ +/*! @file OIDExternalUserAgentCatalyst.h + @brief AppAuth iOS SDK + @copyright + Copyright 2019 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 + +#import "OIDExternalUserAgent.h" + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief A Catalyst specific external user-agent that uses `ASWebAuthenticationSession` to + present the request. +*/ +API_AVAILABLE(macCatalyst(13)) API_UNAVAILABLE(ios) +@interface OIDExternalUserAgentCatalyst : NSObject + +/*! @internal + @brief Unavailable. Please use @c initWithPresentingViewController: + */ +- (nonnull instancetype)init NS_UNAVAILABLE; + +/*! @brief The designated initializer. + @param presentingViewController The view controller from which to present the + \SFSafariViewController. + */ +- (nullable instancetype)initWithPresentingViewController: + (UIViewController *)presentingViewController + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/iOS/OIDExternalUserAgentCatalyst.m b/Source/iOS/OIDExternalUserAgentCatalyst.m new file mode 100644 index 000000000..f7093b10a --- /dev/null +++ b/Source/iOS/OIDExternalUserAgentCatalyst.m @@ -0,0 +1,139 @@ +/*! @file OIDExternalUserAgentCatalyst.m + @brief AppAuth iOS SDK + @copyright + Copyright 2019 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 "OIDExternalUserAgentCatalyst.h" + +#import +#import + +#import "OIDErrorUtilities.h" +#import "OIDExternalUserAgentSession.h" +#import "OIDExternalUserAgentRequest.h" + +#if TARGET_OS_MACCATALYST + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDExternalUserAgentCatalyst () +@end + +@implementation OIDExternalUserAgentCatalyst { + UIViewController *_presentingViewController; + + BOOL _externalUserAgentFlowInProgress; + __weak id _session; + ASWebAuthenticationSession *_webAuthenticationVC; +} + +- (nullable instancetype)initWithPresentingViewController: + (UIViewController *)presentingViewController { + self = [super init]; + if (self) { + _presentingViewController = presentingViewController; + } + return self; +} + +- (BOOL)presentExternalUserAgentRequest:(id)request + session:(id)session { + if (_externalUserAgentFlowInProgress) { + // TODO: Handle errors as authorization is already in progress. + return NO; + } + + _externalUserAgentFlowInProgress = YES; + _session = session; + BOOL openedUserAgent = NO; + NSURL *requestURL = [request externalUserAgentRequestURL]; + + __weak OIDExternalUserAgentCatalyst *weakSelf = self; + NSString *redirectScheme = request.redirectScheme; + ASWebAuthenticationSession *authenticationVC = + [[ASWebAuthenticationSession alloc] initWithURL:requestURL + callbackURLScheme:redirectScheme + completionHandler:^(NSURL * _Nullable callbackURL, + NSError * _Nullable error) { + __strong OIDExternalUserAgentCatalyst *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + strongSelf->_webAuthenticationVC = nil; + if (callbackURL) { + [strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL]; + } else { + NSError *safariError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow + underlyingError:error + description:nil]; + [strongSelf->_session failExternalUserAgentFlowWithError:safariError]; + } + }]; + + authenticationVC.presentationContextProvider = self; + _webAuthenticationVC = authenticationVC; + openedUserAgent = [authenticationVC start]; + + if (!openedUserAgent) { + [self cleanUp]; + NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeSafariOpenError + underlyingError:nil + description:@"Unable to open ASWebAuthenticationSession view controller."]; + [session failExternalUserAgentFlowWithError:safariError]; + } + return openedUserAgent; +} + +- (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(void))completion { + if (!_externalUserAgentFlowInProgress) { + // Ignore this call if there is no authorization flow in progress. + if (completion) completion(); + return; + } + + ASWebAuthenticationSession *webAuthenticationVC = _webAuthenticationVC; + + [self cleanUp]; + + if (webAuthenticationVC) { + // dismiss the ASWebAuthenticationSession + [webAuthenticationVC cancel]; + if (completion) completion(); + } else { + if (completion) completion(); + } +} + +- (void)cleanUp { + // The weak reference to |_session| is set to nil to avoid accidentally using + // it while not in an authorization flow. + _webAuthenticationVC = nil; + _session = nil; + _externalUserAgentFlowInProgress = NO; +} + +#pragma mark - ASWebAuthenticationPresentationContextProviding + +- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session { + return _presentingViewController.view.window; +} + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_MACCATALYST diff --git a/Source/iOS/OIDExternalUserAgentIOS.h b/Source/iOS/OIDExternalUserAgentIOS.h index 48ab3f848..e75225851 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.h +++ b/Source/iOS/OIDExternalUserAgentIOS.h @@ -27,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief An iOS specific external user-agent that uses the best possible user-agent available depending on the version of iOS to present the request. */ +API_UNAVAILABLE(macCatalyst) @interface OIDExternalUserAgentIOS : NSObject - (nullable instancetype)init API_AVAILABLE(ios(11)) diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index bb55e8c99..49396af86 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -25,6 +25,8 @@ #import "OIDExternalUserAgentSession.h" #import "OIDExternalUserAgentRequest.h" +#if !TARGET_OS_MACCATALYST + NS_ASSUME_NONNULL_BEGIN #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 @@ -90,8 +92,8 @@ - (BOOL)presentExternalUserAgentRequest:(id)request ASWebAuthenticationSession *authenticationVC = [[ASWebAuthenticationSession alloc] initWithURL:requestURL callbackURLScheme:redirectScheme - completionHandler:^(NSURL * _Nullable callbackURL, - NSError * _Nullable error) { + completionHandler:^(NSURL * _Nullable callbackURL, + NSError * _Nullable error) { __strong OIDExternalUserAgentIOS *strongSelf = weakSelf; if (!strongSelf) { return; @@ -243,3 +245,5 @@ - (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuth @end NS_ASSUME_NONNULL_END + +#endif // !TARGET_OS_MACCATALYST diff --git a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.h b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.h index 8d1dc9367..5b0427636 100644 --- a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.h +++ b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.h @@ -35,6 +35,7 @@ typedef NSURL *_Nullable (^OIDCustomBrowserURLTransformation)(NSURL *_Nullable r for browsers that require other modifications to the URL. If the browser is not installed the user will be prompted to install it. */ +API_UNAVAILABLE(macCatalyst) @interface OIDExternalUserAgentIOSCustomBrowser : NSObject /*! @brief URL transformation block for the browser. diff --git a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m index 7bca4c7e0..499b14c3b 100644 --- a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m +++ b/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m @@ -25,6 +25,8 @@ #import "OIDErrorUtilities.h" #import "OIDURLQueryComponent.h" +#if !TARGET_OS_MACCATALYST + NS_ASSUME_NONNULL_BEGIN @implementation OIDExternalUserAgentIOSCustomBrowser @@ -159,3 +161,5 @@ - (void)dismissExternalUserAgentAnimated:(BOOL)animated @end NS_ASSUME_NONNULL_END + +#endif // !TARGET_OS_MACCATALYST From 8524261285ff493664e68e3804e94ef16b6e79d2 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Tue, 19 Nov 2019 10:17:11 -0800 Subject: [PATCH 100/204] Update TARGET_OS_MACCATALYST preprocessor comments --- Source/iOS/OIDAuthState+IOS.m | 6 +++--- Source/iOS/OIDAuthorizationService+IOS.m | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/iOS/OIDAuthState+IOS.m b/Source/iOS/OIDAuthState+IOS.m index 41301ea25..bc1ee1777 100644 --- a/Source/iOS/OIDAuthState+IOS.m +++ b/Source/iOS/OIDAuthState+IOS.m @@ -30,9 +30,9 @@ @implementation OIDAuthState (IOS) #if TARGET_OS_MACCATALYST externalUserAgent = [[OIDExternalUserAgentCatalyst alloc] initWithPresentingViewController:presentingViewController]; -#else +#else // TARGET_OS_MACCATALYST externalUserAgent = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController:presentingViewController]; -#endif +#endif // TARGET_OS_MACCATALYST return [self authStateByPresentingAuthorizationRequest:authorizationRequest externalUserAgent:externalUserAgent callback:callback]; @@ -47,6 +47,6 @@ @implementation OIDAuthState (IOS) externalUserAgent:externalUserAgent callback:callback]; } -#endif +#endif // !TARGET_OS_MACCATALYST @end diff --git a/Source/iOS/OIDAuthorizationService+IOS.m b/Source/iOS/OIDAuthorizationService+IOS.m index 6242ebdbb..ed6858fbf 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.m +++ b/Source/iOS/OIDAuthorizationService+IOS.m @@ -31,9 +31,9 @@ @implementation OIDAuthorizationService (IOS) #if TARGET_OS_MACCATALYST externalUserAgent = [[OIDExternalUserAgentCatalyst alloc] initWithPresentingViewController:presentingViewController]; -#else +#else // TARGET_OS_MACCATALYST externalUserAgent = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController:presentingViewController]; -#endif +#endif // TARGET_OS_MACCATALYST return [self presentAuthorizationRequest:request externalUserAgent:externalUserAgent callback:callback]; } From 5d1d8ede8c7ff221973838a8ca2ca50901d7c39e Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Tue, 19 Nov 2019 10:33:28 -0800 Subject: [PATCH 101/204] Nil the ASWebAuthenticationSession object along with the rest --- Source/iOS/OIDExternalUserAgentIOS.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index 49396af86..98f8719c5 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -211,6 +211,7 @@ - (void)cleanUp { // them while not in an authorization flow. _safariVC = nil; _authenticationVC = nil; + _webAuthenticationVC = nil; _session = nil; _externalUserAgentFlowInProgress = NO; } From 565558677c66b2e3e03bfe51886f804ab1e68ebc Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 20 Nov 2019 12:46:38 -0800 Subject: [PATCH 102/204] Add Swift code samples to the README --- README.md | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e1643cfc7..fca19ab9c 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,7 @@ authorization state of the session. You can configure AppAuth by specifying the endpoints directly: +Objective-C ```objc NSURL *authorizationEndpoint = [NSURL URLWithString:@"https://accounts.google.com/o/oauth2/v2/auth"]; @@ -138,8 +139,19 @@ OIDServiceConfiguration *configuration = // perform the auth request... ``` +Swift +```swift +let authorizationEndpoint = URL(string: "https://accounts.google.com/o/oauth2/v2/auth")! +let tokenEndpoint = URL(string: "https://www.googleapis.com/oauth2/v4/token")! +let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint, + tokenEndpoint: tokenEndpoint) + +// perform the auth request... +``` + Or through discovery: +Objective-C ```objc NSURL *issuer = [NSURL URLWithString:@"https://accounts.google.com"]; @@ -157,23 +169,59 @@ NSURL *issuer = [NSURL URLWithString:@"https://accounts.google.com"]; }]; ``` +Swift +```swift +let issuer = URL(string: "https://accounts.google.com")! + +// discovers endpoints +OIDAuthorizationService.discoverConfiguration(forIssuer: issuer) { configuration, error in + guard let config = configuration else { + print("Error retrieving discovery document: \(error?.localizedDescription ?? "Unknown error")") + return + } + + // perform the auth request... +} +``` + ### Authorizing – iOS -First, you need to have a property in your AppDelegate to hold the session, in -order to continue the authorization flow from the redirect: +First, you need to have a property in your `UIApplicationDelegate` +implementation to hold the session, in order to continue the authorization flow +from the redirect. In this example, the implementation of this delegate is +a class named `AppDelegate`, if your app's application delegate has a different +name, please update the class name in samples below accordingly. +Objective-C ```objc +@interface AppDelegate : UIResponder // property of the app's AppDelegate -@property(nonatomic, strong, nullable) - id currentAuthorizationFlow; +@property(nonatomic, strong, nullable) id currentAuthorizationFlow; +@end ``` +Swift +```swift +class AppDelegate: UIResponder, UIApplicationDelegate { + // property of the app's AppDelegate + var currentAuthorizationFlow: OIDExternalUserAgentSession? +} +``` + + And your main class, a property to store the auth state: +Objective-C ```objc // property of the containing class @property(nonatomic, strong, nullable) OIDAuthState *authState; ``` +Swift +```swift +// property of the containing class +private var authState: OIDAuthState? +``` + Then, initiate the authorization request. By using the `authStateByPresentingAuthorizationRequest` convenience method, the token @@ -182,6 +230,7 @@ PKCE (if the server supports it). AppAuth also lets you perform these requests manually. See the `authNoCodeExchange` method in the included Example app for a demonstration: +Objective-C ```objc // builds authentication request OIDAuthorizationRequest *request = @@ -212,12 +261,42 @@ appDelegate.currentAuthorizationFlow = }]; ``` +Swift +```swift +// builds authentication request +let request = OIDAuthorizationRequest(configuration: configuration, + clientId: clientID, + clientSecret: clientSecret, + scopes: [OIDScopeOpenID, OIDScopeProfile], + redirectURL: redirectURI, + responseType: OIDResponseTypeCode, + additionalParameters: nil) + +// performs authentication request +print("Initiating authorization request with scope: \(request.scope ?? "nil")") + +let appDelegate = UIApplication.shared.delegate as! AppDelegate + +appDelegate.currentAuthorizationFlow = + OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in + if let authState = authState { + self.setAuthState(authState) + print("Got authorization tokens. Access token: " + + "\(authState.lastTokenResponse?.accessToken ?? "nil")") + } else { + print("Authorization error: \(error?.localizedDescription ?? "Unknown error")") + self.setAuthState(nil) + } +} +``` + *Handling the Redirect* The authorization response URL is returned to the app via the iOS openURL app delegate method, so you need to pipe this through to the current authorization session (created in the previous session): +Objective-C ```objc - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url @@ -235,6 +314,25 @@ authorization session (created in the previous session): } ``` +Swift +```swift +func application(_ app: UIApplication, + open url: URL, + options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { + // Sends the URL to the current authorization flow (if any) which will + // process it if it relates to an authorization response. + if let authorizationFlow = self.currentAuthorizationFlow, + authorizationFlow.resumeExternalUserAgentFlow(with: url) { + self.currentAuthorizationFlow = nil + return true + } + + // Your additional URL handling (if any) + + return false +} +``` + ### Authorizing – MacOS On macOS, the most popular way to get the authorization response redirect is to @@ -253,6 +351,7 @@ To receive the authorization response using a local HTTP server, first you need to have an instance variable in your main class to retain the HTTP redirect handler: +Objective-C ```objc OIDRedirectHTTPHandler *_redirectHTTPHandler; ``` @@ -261,6 +360,7 @@ Then, as the port used by the local HTTP server varies, you need to start it before building the authorization request, in order to get the exact redirect URI to use: +Objective-C ```objc static NSString *const kSuccessURLString = @"http://openid.github.io/AppAuth-iOS/redirect/"; @@ -318,6 +418,7 @@ recommend that users of the `OIDAuthState` convenience wrapper use the provided `performActionWithFreshTokens:` method to perform their API calls to avoid needing to worry about token freshness: +Objective-C ```objc [_authState performActionWithFreshTokens:^(NSString *_Nonnull accessToken, NSString *_Nonnull idToken, @@ -331,6 +432,26 @@ needing to worry about token freshness: }]; ``` +Swift +```swift +let userinfoEndpoint = URL(string:"https://openidconnect.googleapis.com/v1/userinfo")! +self.authState?.performAction() { (accessToken, idToken, error) in + + if error != nil { + print("Error fetching fresh tokens: \(error?.localizedDescription ?? "Unknown error")") + return + } + guard let accessToken = accessToken else { + return + } + + // Add Bearer token to request + var urlRequest = URLRequest(url: userinfoEndpoint) + urlRequest.allHTTPHeaderFields = ["Authorization": "Bearer \(accessToken)"] + + // Perform request... +} +``` ## API Documentation Browse the [API documentation](http://openid.github.io/AppAuth-iOS/docs/latest/annotated.html). From dcecec2038add567a61d93f4ee309f7c27cad234 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Wed, 20 Nov 2019 12:55:07 -0800 Subject: [PATCH 103/204] Add documentation on customizing the external user agent --- README.md | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/README.md b/README.md index fca19ab9c..44b0da186 100644 --- a/README.md +++ b/README.md @@ -452,6 +452,108 @@ self.authState?.performAction() { (accessToken, idToken, error) in // Perform request... } ``` + +### Custom User-Agents + +Each OAuth flow involves presenting an external user-agent to the user, that +allows them to interact with the OAuth authorization server. Typical examples +of a user-agent are the user's browser, or an in-app browser tab incarnation +like `ASWebAuthenticationSession` on iOS. + +AppAuth ships with several implementations of an external user-agent out of the +box, including defaults for iOS and macOS suitable for most cases. The default +user-agents typically share persistent cookies with the system default browser, +to improve the chance that the user doesn't need to sign-in all over again. + +It is possible to change the user-agent that AppAuth uses, and even write your +own - all without needing to fork the library. + +All implementations of the external user-agent, be they included or created by +you need to conform to the +[`OIDExternalUserAgent`](http://openid.github.io/AppAuth-iOS/docs/latest/protocol_o_i_d_external_user_agent-p.html) +protocol. + +Instances of the `OIDExternalUserAgent`are passed into +[`OIDAuthState.authStateByPresentingAuthorizationRequest:externalUserAgent:callback`](http://openid.github.io/AppAuth-iOS/docs/latest/interface_o_i_d_auth_state.html#ac762fe2bf95c116f0b437419be211fa1) +and/or +[`OIDAuthorizationService.presentAuthorizationRequest:externalUserAgent:callback:`](http://openid.github.io/AppAuth-iOS/docs/latest/interface_o_i_d_authorization_service.html#ae551f8e6887366a46e49b09b37389b8f) +rather than using the platform-specific convenience methods (which use the +default user-agents for their respective platforms), like +[`OIDAuthState.authStateByPresentingAuthorizationRequest:presentingViewController:callback:`](http://openid.github.io/AppAuth-iOS/docs/latest/category_o_i_d_auth_state_07_i_o_s_08.html#ae32fd0732cd3192cd5219f2655a4c85c). + +Popular use-cases for writing your own user-agent implementation include needing +to style the user-agent in ways not supported by AppAuth, and implementing a +fully custom flow with your own business logic. You can take one of the existing +implementations as a starting point to copy, rename, and customize to your +needs. + +#### Custom Browser User-Agent + +AppAuth for iOS includes a few extra user-agent implementations which you can +try, or use as a reference for your own implementation. One of them, +[`OIDExternalUserAgentIOSCustomBrowser`](http://openid.github.io/AppAuth-iOS/docs/latest/interface_o_i_d_external_user_agent_i_o_s_custom_browser.html) +enables you to use a different browser for authentication, like Chrome for iOS +or Firefox for iOS. + +Here's how to configure AppAuth to use a custom browser using the +`OIDExternalUserAgentIOSCustomBrowser` user agent: + +First, add the following array to your +[Info.plist](https://github.com/openid/AppAuth-iOS/blob/135f99d2cb4e9d18d310ac2588b905e612461561/Examples/Example-iOS_ObjC/Source/Info.plist#L34) +(in XCode, right click -> Open As -> Source Code) + +``` + LSApplicationQueriesSchemes + + googlechromes + opera-https + firefox + +``` + +This is required so that AppAuth can test for the browser and open the app store +if it's not installed (the default behavior of this user-agent). You only need +to include the URL scheme of the actual browser you intend to use. + +Objective-C +```objc +// performs authentication request +AppDelegate *appDelegate = + (AppDelegate *)[UIApplication sharedApplication].delegate; +id userAgent = + [OIDExternalUserAgentIOSCustomBrowser CustomBrowserChrome]; +appDelegate.currentAuthorizationFlow = + [OIDAuthState authStateByPresentingAuthorizationRequest:request + externalUserAgent:self + callback:^(OIDAuthState *_Nullable authState, + NSError *_Nullable error) { + if (authState) { + NSLog(@"Got authorization tokens. Access token: %@", + authState.lastTokenResponse.accessToken); + [self setAuthState:authState]; + } else { + NSLog(@"Authorization error: %@", [error localizedDescription]); + [self setAuthState:nil]; + } +}]; +``` + +That's it! With those two changes (which you can try on the included sample), +AppAuth will use Chrome iOS for the authorization request (and open Chrome in +the App Store if it's not installed). + +⚠️**Note: the `OIDExternalUserAgentIOSCustomBrowser` user-agent is not intended for consumer apps**. It is designed for +advanced enterprise use-cases where the app developers have greater control over +the operating environment and have special requirements that require a custom +browser like Chrome. + +You don't need to stop with the included external user agents either! Since the +[`OIDExternalUserAgent`](http://openid.github.io/AppAuth-iOS/docs/latest/protocol_o_i_d_external_user_agent-p.html) +protocol is part of AppAuth's public API, you can implement your own versions of +it. In the above example, +`userAgent = [OIDExternalUserAgentIOSCustomBrowser CustomBrowserChrome]` would +be replaced with an instantiation of your user-agent implementation. + ## API Documentation Browse the [API documentation](http://openid.github.io/AppAuth-iOS/docs/latest/annotated.html). From 1cf3ae7d9ec1fa66819eab8cf3675482467aed86 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 22 Nov 2019 15:07:04 -0800 Subject: [PATCH 104/204] Bump version to 1.3.0 --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index a5005bc1d..9beea11ee 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.2.0" + s.version = "1.3.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC From 7e0b5c100d69caac34372efdad34b22b3464ddea Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 22 Nov 2019 15:16:46 -0800 Subject: [PATCH 105/204] Add ChangeLog for 1.3.0 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d00621dc..64ed8c133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # AppAuth for iOS and macOS Changelog -## Unreleased +## 1.3.0 + +### Notable Changes + +1. Support for Mac Catalyst ## 1.2.0 From 217ee92c1c0139445de5ed643a260844976e3f06 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Mon, 16 Mar 2020 13:09:45 -0400 Subject: [PATCH 106/204] Remove 'UIWebView' in comment to avoid warning when submiting apps to App Store Connect --- Source/AppAuth.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/AppAuth.h b/Source/AppAuth.h index cf3a0e265..4f779df38 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -75,7 +75,7 @@ It follows the best practices set out in [RFC 8252 - OAuth 2.0 for Native Apps](https://tools.ietf.org/html/rfc8252) including using `SFAuthenticationSession` and `SFSafariViewController` on iOS - for the auth request. `UIWebView` and `WKWebView` are explicitly *not* + for the auth request. Web view and `WKWebView` are explicitly *not* supported due to the security and usability reasons explained in [Section 8.12 of RFC 8252](https://tools.ietf.org/html/rfc8252#section-8.12). From 9639273133342cee9f9cf6106106232f352a7216 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Fri, 8 May 2020 10:20:14 -0400 Subject: [PATCH 107/204] Add my name to the podspec authors --- AppAuth.podspec | 1 + 1 file changed, 1 insertion(+) diff --git a/AppAuth.podspec b/AppAuth.podspec index 9beea11ee..26252c87e 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -23,6 +23,7 @@ It follows the OAuth 2.0 for Native Apps best current practice s.license = "Apache License, Version 2.0" s.authors = { "William Denniss" => "wdenniss@google.com", "Steven E Wright" => "stevewright@google.com", + "Julien Bodet" => "julien.bodet92@gmail.com" } # Note: While watchOS and tvOS are specified here, only iOS and macOS have From 803353ab878d28ce87e11bec0936d4af389af3c6 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Fri, 8 May 2020 19:16:32 -0400 Subject: [PATCH 108/204] Bump version to 1.3.1 --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 26252c87e..a3dc69c0c 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.3.0" + s.version = "1.3.1" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC From c63e3926c5f141db43a7d02e8c24af3463479f7b Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Fri, 8 May 2020 19:17:04 -0400 Subject: [PATCH 109/204] Add ChangeLog for 1.3.1 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64ed8c133..cb829815e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # AppAuth for iOS and macOS Changelog +## 1.3.1 + +### Fixes + +1. Removed `UIWebView` reference in comment + ## 1.3.0 ### Notable Changes From d1bd7ace42fcc4f80051b1ebc963bd19d7e3d0ec Mon Sep 17 00:00:00 2001 From: Karim Date: Sun, 31 May 2020 01:12:10 +0100 Subject: [PATCH 110/204] Add Swift Package Manager Support The structure of the project was changed to support Swift PM. The Podspec was also updated to reflect this new structure. --- .travis.yml | 54 ++++--- AppAuth.podspec | 8 +- AppAuth.xcodeproj/project.pbxproj | 150 ++++++++++-------- Package.swift | 71 +++++++++ README.md | 25 ++- Source/{ => AppAuth}/iOS/OIDAuthState+IOS.h | 6 + Source/{ => AppAuth}/iOS/OIDAuthState+IOS.m | 6 + .../iOS/OIDAuthorizationService+IOS.h | 6 + .../iOS/OIDAuthorizationService+IOS.m | 6 + .../iOS/OIDExternalUserAgentCatalyst.h | 6 + .../iOS/OIDExternalUserAgentCatalyst.m | 6 + .../iOS/OIDExternalUserAgentIOS.h | 6 + .../iOS/OIDExternalUserAgentIOS.m | 6 + .../OIDExternalUserAgentIOSCustomBrowser.h | 6 + .../OIDExternalUserAgentIOSCustomBrowser.m | 6 + .../OIDLoopbackHTTPServer.h | 6 +- .../OIDLoopbackHTTPServer.m | 6 + Source/{ => AppAuth}/macOS/OIDAuthState+Mac.h | 6 + Source/{ => AppAuth}/macOS/OIDAuthState+Mac.m | 6 + .../macOS/OIDAuthorizationService+Mac.h | 6 + .../macOS/OIDAuthorizationService+Mac.m | 6 + .../macOS/OIDExternalUserAgentMac.h | 6 + .../macOS/OIDExternalUserAgentMac.m | 6 + .../macOS/OIDRedirectHTTPHandler.h | 6 + .../macOS/OIDRedirectHTTPHandler.m | 6 + Source/{ => AppAuthCore}/OIDAuthState.h | 0 Source/{ => AppAuthCore}/OIDAuthState.m | 0 .../OIDAuthStateChangeDelegate.h | 0 .../OIDAuthStateErrorDelegate.h | 0 .../OIDAuthorizationRequest.h | 0 .../OIDAuthorizationRequest.m | 0 .../OIDAuthorizationResponse.h | 0 .../OIDAuthorizationResponse.m | 0 .../OIDAuthorizationService.h | 0 .../OIDAuthorizationService.m | 0 .../OIDClientMetadataParameters.h | 0 .../OIDClientMetadataParameters.m | 0 Source/{ => AppAuthCore}/OIDDefines.h | 0 .../{ => AppAuthCore}/OIDEndSessionRequest.h | 0 .../{ => AppAuthCore}/OIDEndSessionRequest.m | 0 .../{ => AppAuthCore}/OIDEndSessionResponse.h | 0 .../{ => AppAuthCore}/OIDEndSessionResponse.m | 0 Source/{ => AppAuthCore}/OIDError.h | 0 Source/{ => AppAuthCore}/OIDError.m | 0 Source/{ => AppAuthCore}/OIDErrorUtilities.h | 0 Source/{ => AppAuthCore}/OIDErrorUtilities.m | 0 .../{ => AppAuthCore}/OIDExternalUserAgent.h | 0 .../OIDExternalUserAgentRequest.h | 0 .../OIDExternalUserAgentSession.h | 0 Source/{ => AppAuthCore}/OIDFieldMapping.h | 0 Source/{ => AppAuthCore}/OIDFieldMapping.m | 0 Source/{ => AppAuthCore}/OIDGrantTypes.h | 0 Source/{ => AppAuthCore}/OIDGrantTypes.m | 0 Source/{ => AppAuthCore}/OIDIDToken.h | 0 Source/{ => AppAuthCore}/OIDIDToken.m | 0 .../OIDRegistrationRequest.h | 0 .../OIDRegistrationRequest.m | 0 .../OIDRegistrationResponse.h | 0 .../OIDRegistrationResponse.m | 0 Source/{ => AppAuthCore}/OIDResponseTypes.h | 0 Source/{ => AppAuthCore}/OIDResponseTypes.m | 0 Source/{ => AppAuthCore}/OIDScopeUtilities.h | 0 Source/{ => AppAuthCore}/OIDScopeUtilities.m | 0 Source/{ => AppAuthCore}/OIDScopes.h | 0 Source/{ => AppAuthCore}/OIDScopes.m | 0 .../OIDServiceConfiguration.h | 0 .../OIDServiceConfiguration.m | 0 .../{ => AppAuthCore}/OIDServiceDiscovery.h | 0 .../{ => AppAuthCore}/OIDServiceDiscovery.m | 0 Source/{ => AppAuthCore}/OIDTokenRequest.h | 0 Source/{ => AppAuthCore}/OIDTokenRequest.m | 0 Source/{ => AppAuthCore}/OIDTokenResponse.h | 0 Source/{ => AppAuthCore}/OIDTokenResponse.m | 0 Source/{ => AppAuthCore}/OIDTokenUtilities.h | 0 Source/{ => AppAuthCore}/OIDTokenUtilities.m | 0 .../{ => AppAuthCore}/OIDURLQueryComponent.h | 0 .../{ => AppAuthCore}/OIDURLQueryComponent.m | 0 .../{ => AppAuthCore}/OIDURLSessionProvider.h | 0 .../{ => AppAuthCore}/OIDURLSessionProvider.m | 0 UnitTests/OIDAuthStateTests.h | 8 +- UnitTests/OIDAuthStateTests.m | 16 +- UnitTests/OIDAuthorizationRequestTests.m | 11 +- UnitTests/OIDAuthorizationResponseTests.m | 11 +- UnitTests/OIDEndSessionRequestTests.m | 11 +- UnitTests/OIDGrantTypesTests.m | 6 +- UnitTests/OIDRegistrationRequestTests.m | 11 +- UnitTests/OIDRegistrationResponseTests.m | 9 +- UnitTests/OIDResponseTypesTests.m | 6 +- UnitTests/OIDScopesTests.m | 6 +- UnitTests/OIDServiceConfigurationTests.m | 13 +- UnitTests/OIDServiceDiscoveryTests.m | 8 +- UnitTests/OIDSwiftTests.swift | 4 + UnitTests/OIDTokenRequestTests.m | 15 +- UnitTests/OIDTokenResponseTests.m | 9 +- UnitTests/OIDTokenUtilitiesTests.m | 7 +- UnitTests/OIDURLQueryComponentTests.m | 6 +- UnitTests/OIDURLQueryComponentTestsIOS7.m | 6 +- 97 files changed, 450 insertions(+), 141 deletions(-) create mode 100644 Package.swift rename Source/{ => AppAuth}/iOS/OIDAuthState+IOS.h (95%) rename Source/{ => AppAuth}/iOS/OIDAuthState+IOS.m (94%) rename Source/{ => AppAuth}/iOS/OIDAuthorizationService+IOS.h (93%) rename Source/{ => AppAuth}/iOS/OIDAuthorizationService+IOS.m (93%) rename Source/{ => AppAuth}/iOS/OIDExternalUserAgentCatalyst.h (92%) rename Source/{ => AppAuth}/iOS/OIDExternalUserAgentCatalyst.m (97%) rename Source/{ => AppAuth}/iOS/OIDExternalUserAgentIOS.h (92%) rename Source/{ => AppAuth}/iOS/OIDExternalUserAgentIOS.m (98%) rename Source/{ => AppAuth}/iOS/OIDExternalUserAgentIOSCustomBrowser.h (97%) rename Source/{ => AppAuth}/iOS/OIDExternalUserAgentIOSCustomBrowser.m (98%) rename Source/{ => AppAuth}/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h (98%) rename Source/{ => AppAuth}/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m (99%) rename Source/{ => AppAuth}/macOS/OIDAuthState+Mac.h (96%) rename Source/{ => AppAuth}/macOS/OIDAuthState+Mac.m (94%) rename Source/{ => AppAuth}/macOS/OIDAuthorizationService+Mac.h (95%) rename Source/{ => AppAuth}/macOS/OIDAuthorizationService+Mac.m (94%) rename Source/{ => AppAuth}/macOS/OIDExternalUserAgentMac.h (93%) rename Source/{ => AppAuth}/macOS/OIDExternalUserAgentMac.m (97%) rename Source/{ => AppAuth}/macOS/OIDRedirectHTTPHandler.h (98%) rename Source/{ => AppAuth}/macOS/OIDRedirectHTTPHandler.m (98%) rename Source/{ => AppAuthCore}/OIDAuthState.h (100%) rename Source/{ => AppAuthCore}/OIDAuthState.m (100%) rename Source/{ => AppAuthCore}/OIDAuthStateChangeDelegate.h (100%) rename Source/{ => AppAuthCore}/OIDAuthStateErrorDelegate.h (100%) rename Source/{ => AppAuthCore}/OIDAuthorizationRequest.h (100%) rename Source/{ => AppAuthCore}/OIDAuthorizationRequest.m (100%) rename Source/{ => AppAuthCore}/OIDAuthorizationResponse.h (100%) rename Source/{ => AppAuthCore}/OIDAuthorizationResponse.m (100%) rename Source/{ => AppAuthCore}/OIDAuthorizationService.h (100%) rename Source/{ => AppAuthCore}/OIDAuthorizationService.m (100%) rename Source/{ => AppAuthCore}/OIDClientMetadataParameters.h (100%) rename Source/{ => AppAuthCore}/OIDClientMetadataParameters.m (100%) rename Source/{ => AppAuthCore}/OIDDefines.h (100%) rename Source/{ => AppAuthCore}/OIDEndSessionRequest.h (100%) rename Source/{ => AppAuthCore}/OIDEndSessionRequest.m (100%) rename Source/{ => AppAuthCore}/OIDEndSessionResponse.h (100%) rename Source/{ => AppAuthCore}/OIDEndSessionResponse.m (100%) rename Source/{ => AppAuthCore}/OIDError.h (100%) rename Source/{ => AppAuthCore}/OIDError.m (100%) rename Source/{ => AppAuthCore}/OIDErrorUtilities.h (100%) rename Source/{ => AppAuthCore}/OIDErrorUtilities.m (100%) rename Source/{ => AppAuthCore}/OIDExternalUserAgent.h (100%) rename Source/{ => AppAuthCore}/OIDExternalUserAgentRequest.h (100%) rename Source/{ => AppAuthCore}/OIDExternalUserAgentSession.h (100%) rename Source/{ => AppAuthCore}/OIDFieldMapping.h (100%) rename Source/{ => AppAuthCore}/OIDFieldMapping.m (100%) rename Source/{ => AppAuthCore}/OIDGrantTypes.h (100%) rename Source/{ => AppAuthCore}/OIDGrantTypes.m (100%) rename Source/{ => AppAuthCore}/OIDIDToken.h (100%) rename Source/{ => AppAuthCore}/OIDIDToken.m (100%) rename Source/{ => AppAuthCore}/OIDRegistrationRequest.h (100%) rename Source/{ => AppAuthCore}/OIDRegistrationRequest.m (100%) rename Source/{ => AppAuthCore}/OIDRegistrationResponse.h (100%) rename Source/{ => AppAuthCore}/OIDRegistrationResponse.m (100%) rename Source/{ => AppAuthCore}/OIDResponseTypes.h (100%) rename Source/{ => AppAuthCore}/OIDResponseTypes.m (100%) rename Source/{ => AppAuthCore}/OIDScopeUtilities.h (100%) rename Source/{ => AppAuthCore}/OIDScopeUtilities.m (100%) rename Source/{ => AppAuthCore}/OIDScopes.h (100%) rename Source/{ => AppAuthCore}/OIDScopes.m (100%) rename Source/{ => AppAuthCore}/OIDServiceConfiguration.h (100%) rename Source/{ => AppAuthCore}/OIDServiceConfiguration.m (100%) rename Source/{ => AppAuthCore}/OIDServiceDiscovery.h (100%) rename Source/{ => AppAuthCore}/OIDServiceDiscovery.m (100%) rename Source/{ => AppAuthCore}/OIDTokenRequest.h (100%) rename Source/{ => AppAuthCore}/OIDTokenRequest.m (100%) rename Source/{ => AppAuthCore}/OIDTokenResponse.h (100%) rename Source/{ => AppAuthCore}/OIDTokenResponse.m (100%) rename Source/{ => AppAuthCore}/OIDTokenUtilities.h (100%) rename Source/{ => AppAuthCore}/OIDTokenUtilities.m (100%) rename Source/{ => AppAuthCore}/OIDURLQueryComponent.h (100%) rename Source/{ => AppAuthCore}/OIDURLQueryComponent.m (100%) rename Source/{ => AppAuthCore}/OIDURLSessionProvider.h (100%) rename Source/{ => AppAuthCore}/OIDURLSessionProvider.m (100%) diff --git a/.travis.yml b/.travis.yml index 85fe4e005..288293628 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,37 @@ # Travis CI config for AppAuth # Use the `wwtd` gem to test locally. language: objective-c -osx_image: xcode11 -# Tests iOS, macOS and tvOS: both static library and framework schemes. -env: -# To generate this list, use `xcodebuild -showsdks` to get possible SDK values, and platforms. -# Use `instruments -s devices` to get the device name. - - SCHEME=AppAuth-iOS DESTINATION="'platform=iOS Simulator,name=iPhone 11,OS=13.0'" SDK=iphonesimulator13.0 - - SCHEME=AppAuth_iOS DESTINATION="'platform=iOS Simulator,name=iPhone 11,OS=13.0'" SDK=iphonesimulator13.0 - - SCHEME=AppAuth-macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.15 - - SCHEME=AppAuth_macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.15 - - SCHEME=AppAuth-tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=13.0'" SDK=appletvsimulator13.0 - - SCHEME=AppAuth_tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=13.0'" SDK=appletvsimulator13.0 -before_script: - - sudo gem install xcpretty -script: -# Breaking down this command: -# eval is used here, otherwise environment variables are not present. -# `set -o pipefail && ` is so that the return code isn't gobbled by xcpretty (per https://github.com/supermarin/xcpretty#usage) -# then it's standard xcodebuild | xcpretty. - - eval "set -o pipefail && xcodebuild -project AppAuth.xcodeproj -scheme $SCHEME -sdk $SDK -destination $DESTINATION -enableCodeCoverage YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES OTHERCFLAGS='-Werror' test | xcpretty" -after_success: - - bash <(curl -s https://codecov.io/bash) + +jobs: + include: + - stage: Swift PM + osx_image: xcode11.4 + script: + # Build the project using Swift PM + - swift build + # Test the project using Swift PM + - swift test + + - stage: Xcode Tests + osx_image: xcode11 + # Tests iOS, macOS and tvOS: both static library and framework schemes. + env: + # To generate this list, use `xcodebuild -showsdks` to get possible SDK values, and platforms. + # Use `instruments -s devices` to get the device name. + - SCHEME=AppAuth-iOS DESTINATION="'platform=iOS Simulator,name=iPhone 11,OS=13.0'" SDK=iphonesimulator13.0 + - SCHEME=AppAuth_iOS DESTINATION="'platform=iOS Simulator,name=iPhone 11,OS=13.0'" SDK=iphonesimulator13.0 + - SCHEME=AppAuth-macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.15 + - SCHEME=AppAuth_macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.15 + - SCHEME=AppAuth-tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=13.0'" SDK=appletvsimulator13.0 + - SCHEME=AppAuth_tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=13.0'" SDK=appletvsimulator13.0 + before_script: + - sudo gem install xcpretty + script: + # Breaking down this command: + # eval is used here, otherwise environment variables are not present. + # `set -o pipefail && ` is so that the return code isn't gobbled by xcpretty (per https://github.com/supermarin/xcpretty#usage) + # then it's standard xcodebuild | xcpretty. + - eval "set -o pipefail && xcodebuild -project AppAuth.xcodeproj -scheme $SCHEME -sdk $SDK -destination $DESTINATION -enableCodeCoverage YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES OTHERCFLAGS='-Werror' test | xcpretty" + after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/AppAuth.podspec b/AppAuth.podspec index a3dc69c0c..94ccadc72 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -38,23 +38,23 @@ It follows the OAuth 2.0 for Native Apps best current practice # Subspec for the core AppAuth library classes only, suitable for extensions. s.subspec 'Core' do |core| - core.source_files = "Source/*.{h,m}" + core.source_files = "Source/*.{h,m}", "Source/AppAuthCore/*.{h,m}" core.exclude_files = "Source/AppAuth.h" end # Subspec for the full AppAuth library, including platform-dependant external user agents. s.subspec 'ExternalUserAgent' do |externalUserAgent| - externalUserAgent.source_files = "Source/*.{h,m}" + externalUserAgent.source_files = "Source/*.{h,m}", "Source/AppAuthCore/*.{h,m}", "Source/AppAuth/*.{h,m}" # iOS - externalUserAgent.ios.source_files = "Source/iOS/**/*.{h,m}" + externalUserAgent.ios.source_files = "Source/AppAuth/iOS/**/*.{h,m}" externalUserAgent.ios.deployment_target = "7.0" externalUserAgent.ios.frameworks = "SafariServices" externalUserAgent.ios.weak_frameworks = "AuthenticationServices" # macOS - externalUserAgent.osx.source_files = "Source/macOS/**/*.{h,m}" + externalUserAgent.osx.source_files = "Source/AppAuth/macOS/**/*.{h,m}" externalUserAgent.osx.deployment_target = '10.9' end end diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 38d1fe396..bac5ad53b 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -690,8 +690,8 @@ 343AAAAE1E83489A00F9D36E /* AppAuth_tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_tvOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAAC21E8348A900F9D36E /* AppAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_macOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentIOSCustomBrowser.m; path = iOS/OIDExternalUserAgentIOSCustomBrowser.m; sourceTree = ""; }; - 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentIOSCustomBrowser.h; path = iOS/OIDExternalUserAgentIOSCustomBrowser.h; sourceTree = ""; }; + 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDExternalUserAgentIOSCustomBrowser.m; sourceTree = ""; }; + 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentIOSCustomBrowser.h; sourceTree = ""; }; 347423F61E7F4B5600D3E6D6 /* libAppAuth-watchOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-watchOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 348970972177B3B000ABEED4 /* AppAuthCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuthCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3489709A2178F40600ABEED4 /* AppAuthCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAuthCore.h; sourceTree = ""; }; @@ -722,18 +722,18 @@ A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgent.h; sourceTree = ""; }; A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentSession.h; sourceTree = ""; }; A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentRequest.h; sourceTree = ""; }; - A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentIOS.m; path = iOS/OIDExternalUserAgentIOS.m; sourceTree = ""; }; - A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentIOS.h; path = iOS/OIDExternalUserAgentIOS.h; sourceTree = ""; }; + A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDExternalUserAgentIOS.m; sourceTree = ""; }; + A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentIOS.h; sourceTree = ""; }; CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionRequest.h; sourceTree = ""; }; CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionRequest.m; sourceTree = ""; }; CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionResponse.h; sourceTree = ""; }; CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionResponse.m; sourceTree = ""; }; - F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OIDAuthState+IOS.m"; path = "iOS/OIDAuthState+IOS.m"; sourceTree = ""; }; - F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OIDAuthorizationService+IOS.m"; path = "iOS/OIDAuthorizationService+IOS.m"; sourceTree = ""; }; - F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OIDAuthorizationService+IOS.h"; path = "iOS/OIDAuthorizationService+IOS.h"; sourceTree = ""; }; - F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OIDAuthState+IOS.h"; path = "iOS/OIDAuthState+IOS.h"; sourceTree = ""; }; - F9A7082C2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentCatalyst.h; path = iOS/OIDExternalUserAgentCatalyst.h; sourceTree = ""; }; - F9A7082D2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentCatalyst.m; path = iOS/OIDExternalUserAgentCatalyst.m; sourceTree = ""; }; + F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OIDAuthState+IOS.m"; sourceTree = ""; }; + F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OIDAuthorizationService+IOS.m"; sourceTree = ""; }; + F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OIDAuthorizationService+IOS.h"; sourceTree = ""; }; + F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OIDAuthState+IOS.h"; sourceTree = ""; }; + F9A7082C2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentCatalyst.h; sourceTree = ""; }; + F9A7082D2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDExternalUserAgentCatalyst.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -918,64 +918,10 @@ children = ( 348970992178F40600ABEED4 /* CoreFramework */, 343AAA4C1E8345B600F9D36E /* Framework */, - 340DAE241D581FE700EC285B /* macOS */, - F6F60FAF1D2BFEF000325CB3 /* iOS */, + 8A9B9D5E24561EC40055353E /* AppAuthCore */, + 8A9B9D632456227D0055353E /* AppAuth */, 341741AF1C5D8243000EF209 /* AppAuth.h */, 3489709E21791B0C00ABEED4 /* AppAuthCore.h */, - 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */, - 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */, - 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */, - 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */, - 341741B81C5D8243000EF209 /* OIDAuthorizationService.h */, - 341741B91C5D8243000EF209 /* OIDAuthorizationService.m */, - 341741BA1C5D8243000EF209 /* OIDAuthState.h */, - 341741BB1C5D8243000EF209 /* OIDAuthState.m */, - 341741BC1C5D8243000EF209 /* OIDAuthStateChangeDelegate.h */, - 341741BD1C5D8243000EF209 /* OIDAuthStateErrorDelegate.h */, - 60140F781DE4262000DA0DC3 /* OIDClientMetadataParameters.h */, - 60140F791DE4276800DA0DC3 /* OIDClientMetadataParameters.m */, - 341741BE1C5D8243000EF209 /* OIDDefines.h */, - 341741BF1C5D8243000EF209 /* OIDError.h */, - 341741C01C5D8243000EF209 /* OIDError.m */, - 341741C11C5D8243000EF209 /* OIDErrorUtilities.h */, - 341741C21C5D8243000EF209 /* OIDErrorUtilities.m */, - A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */, - A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */, - A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */, - 341741C31C5D8243000EF209 /* OIDFieldMapping.h */, - 341741C41C5D8243000EF209 /* OIDFieldMapping.m */, - CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */, - CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */, - CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */, - CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */, - 60140F7E1DE4335200DA0DC3 /* OIDRegistrationResponse.h */, - 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */, - 60140F7D1DE42E3000DA0DC3 /* OIDRegistrationRequest.h */, - 60140F7B1DE42E1000DA0DC3 /* OIDRegistrationRequest.m */, - 341741C51C5D8243000EF209 /* OIDGrantTypes.h */, - 341741C61C5D8243000EF209 /* OIDGrantTypes.m */, - 34A663261E871DD40060B664 /* OIDIDToken.h */, - 34A663271E871DD40060B664 /* OIDIDToken.m */, - 341741C71C5D8243000EF209 /* OIDResponseTypes.h */, - 341741C81C5D8243000EF209 /* OIDResponseTypes.m */, - 341741C91C5D8243000EF209 /* OIDScopes.h */, - 341741CA1C5D8243000EF209 /* OIDScopes.m */, - 341741CB1C5D8243000EF209 /* OIDScopeUtilities.h */, - 341741CC1C5D8243000EF209 /* OIDScopeUtilities.m */, - 341741CD1C5D8243000EF209 /* OIDServiceConfiguration.h */, - 341741CE1C5D8243000EF209 /* OIDServiceConfiguration.m */, - 341741CF1C5D8243000EF209 /* OIDServiceDiscovery.h */, - 341741D01C5D8243000EF209 /* OIDServiceDiscovery.m */, - 341741D11C5D8243000EF209 /* OIDTokenRequest.h */, - 341741D21C5D8243000EF209 /* OIDTokenRequest.m */, - 341741D31C5D8243000EF209 /* OIDTokenResponse.h */, - 341741D41C5D8243000EF209 /* OIDTokenResponse.m */, - 341741D51C5D8243000EF209 /* OIDTokenUtilities.h */, - 341741D61C5D8243000EF209 /* OIDTokenUtilities.m */, - 341741D71C5D8243000EF209 /* OIDURLQueryComponent.h */, - 341741D81C5D8243000EF209 /* OIDURLQueryComponent.m */, - 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */, - 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */, ); path = Source; sourceTree = ""; @@ -1057,6 +1003,76 @@ path = LoopbackHTTPServer; sourceTree = ""; }; + 8A9B9D5E24561EC40055353E /* AppAuthCore */ = { + isa = PBXGroup; + children = ( + 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */, + 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */, + 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */, + 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */, + 341741B81C5D8243000EF209 /* OIDAuthorizationService.h */, + 341741B91C5D8243000EF209 /* OIDAuthorizationService.m */, + 341741BA1C5D8243000EF209 /* OIDAuthState.h */, + 341741BB1C5D8243000EF209 /* OIDAuthState.m */, + 341741BC1C5D8243000EF209 /* OIDAuthStateChangeDelegate.h */, + 341741BD1C5D8243000EF209 /* OIDAuthStateErrorDelegate.h */, + 60140F781DE4262000DA0DC3 /* OIDClientMetadataParameters.h */, + 60140F791DE4276800DA0DC3 /* OIDClientMetadataParameters.m */, + 341741BE1C5D8243000EF209 /* OIDDefines.h */, + 341741BF1C5D8243000EF209 /* OIDError.h */, + 341741C01C5D8243000EF209 /* OIDError.m */, + 341741C11C5D8243000EF209 /* OIDErrorUtilities.h */, + 341741C21C5D8243000EF209 /* OIDErrorUtilities.m */, + A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */, + A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */, + A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */, + 341741C31C5D8243000EF209 /* OIDFieldMapping.h */, + 341741C41C5D8243000EF209 /* OIDFieldMapping.m */, + CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */, + CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */, + CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */, + CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */, + 60140F7E1DE4335200DA0DC3 /* OIDRegistrationResponse.h */, + 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */, + 60140F7D1DE42E3000DA0DC3 /* OIDRegistrationRequest.h */, + 60140F7B1DE42E1000DA0DC3 /* OIDRegistrationRequest.m */, + 341741C51C5D8243000EF209 /* OIDGrantTypes.h */, + 341741C61C5D8243000EF209 /* OIDGrantTypes.m */, + 34A663261E871DD40060B664 /* OIDIDToken.h */, + 34A663271E871DD40060B664 /* OIDIDToken.m */, + 341741C71C5D8243000EF209 /* OIDResponseTypes.h */, + 341741C81C5D8243000EF209 /* OIDResponseTypes.m */, + 341741C91C5D8243000EF209 /* OIDScopes.h */, + 341741CA1C5D8243000EF209 /* OIDScopes.m */, + 341741CB1C5D8243000EF209 /* OIDScopeUtilities.h */, + 341741CC1C5D8243000EF209 /* OIDScopeUtilities.m */, + 341741CD1C5D8243000EF209 /* OIDServiceConfiguration.h */, + 341741CE1C5D8243000EF209 /* OIDServiceConfiguration.m */, + 341741CF1C5D8243000EF209 /* OIDServiceDiscovery.h */, + 341741D01C5D8243000EF209 /* OIDServiceDiscovery.m */, + 341741D11C5D8243000EF209 /* OIDTokenRequest.h */, + 341741D21C5D8243000EF209 /* OIDTokenRequest.m */, + 341741D31C5D8243000EF209 /* OIDTokenResponse.h */, + 341741D41C5D8243000EF209 /* OIDTokenResponse.m */, + 341741D51C5D8243000EF209 /* OIDTokenUtilities.h */, + 341741D61C5D8243000EF209 /* OIDTokenUtilities.m */, + 341741D71C5D8243000EF209 /* OIDURLQueryComponent.h */, + 341741D81C5D8243000EF209 /* OIDURLQueryComponent.m */, + 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */, + 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */, + ); + path = AppAuthCore; + sourceTree = ""; + }; + 8A9B9D632456227D0055353E /* AppAuth */ = { + isa = PBXGroup; + children = ( + 340DAE241D581FE700EC285B /* macOS */, + F6F60FAF1D2BFEF000325CB3 /* iOS */, + ); + path = AppAuth; + sourceTree = ""; + }; F6F60FAF1D2BFEF000325CB3 /* iOS */ = { isa = PBXGroup; children = ( @@ -1071,7 +1087,7 @@ F9A7082C2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h */, F9A7082D2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m */, ); - name = iOS; + path = iOS; sourceTree = ""; }; /* End PBXGroup section */ diff --git a/Package.swift b/Package.swift new file mode 100644 index 000000000..0e180a383 --- /dev/null +++ b/Package.swift @@ -0,0 +1,71 @@ +// swift-tools-version:5.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. +import PackageDescription + +/*! @file Package.swift + @brief AppAuth iOS SDK + @copyright + Copyright 2020 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. +*/ + +let package = Package( + name: "AppAuth", + platforms: [ + .macOS(.v10_10), + .iOS(.v8), + .tvOS(.v9), + .watchOS(.v2) + ], + products: [ + .library( + name: "AppAuthCore", + targets: ["AppAuthCore"]), + .library( + name: "AppAuth", + targets: ["AppAuth"]) + ], + dependencies: [], + targets: [ + .target( + name: "AppAuthCore", + path: "Source/AppAuthCore", + publicHeadersPath: "" + ), + .target( + name: "AppAuth", + dependencies: ["AppAuthCore"], + path: "Source/AppAuth", + sources: ["iOS", "macOS"], + publicHeadersPath: "", + cSettings: [ + .headerSearchPath("iOS"), + .headerSearchPath("macOS"), + .headerSearchPath("macOS/LoopbackHTTPServer"), + ] + ), + .testTarget( + name: "AppAuthCoreTests", + dependencies: ["AppAuthCore"], + path: "UnitTests", + exclude: ["OIDSwiftTests.swift"] + ), + .testTarget( + name: "AppAuthCoreSwiftTests", + dependencies: ["AppAuthCore"], + path: "UnitTests", + sources: ["OIDSwiftTests.swift"] + ) + ] +) diff --git a/README.md b/README.md index 44b0da186..5f54c2c39 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,18 @@ client info to try the demo). ## Setup -AppAuth supports three options for dependency management. +AppAuth supports four options for dependency management. + +### Swift Package Manager + +With [Swift Package Manager](https://swift.org/package-manager), +add the following `dependency` to your `Package.swift`: + +```swift +dependencies: [ + .package(url: "https://github.com/openid/AppAuth-iOS.git", .upToNextMajor(from: "1.3.0")) +] +``` ### CocoaPods @@ -503,12 +514,12 @@ First, add the following array to your (in XCode, right click -> Open As -> Source Code) ``` - LSApplicationQueriesSchemes - - googlechromes - opera-https - firefox - + LSApplicationQueriesSchemes + + googlechromes + opera-https + firefox + ``` This is required so that AppAuth can test for the browser and open the app store diff --git a/Source/iOS/OIDAuthState+IOS.h b/Source/AppAuth/iOS/OIDAuthState+IOS.h similarity index 95% rename from Source/iOS/OIDAuthState+IOS.h rename to Source/AppAuth/iOS/OIDAuthState+IOS.h index 559df9ac5..99ca45734 100644 --- a/Source/iOS/OIDAuthState+IOS.h +++ b/Source/AppAuth/iOS/OIDAuthState+IOS.h @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + #import #import "OIDAuthState.h" @@ -55,3 +59,5 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST diff --git a/Source/iOS/OIDAuthState+IOS.m b/Source/AppAuth/iOS/OIDAuthState+IOS.m similarity index 94% rename from Source/iOS/OIDAuthState+IOS.m rename to Source/AppAuth/iOS/OIDAuthState+IOS.m index bc1ee1777..9f3a4e8c8 100644 --- a/Source/iOS/OIDAuthState+IOS.m +++ b/Source/AppAuth/iOS/OIDAuthState+IOS.m @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + #import "OIDAuthState+IOS.h" #import "OIDExternalUserAgentIOS.h" #import "OIDExternalUserAgentCatalyst.h" @@ -50,3 +54,5 @@ @implementation OIDAuthState (IOS) #endif // !TARGET_OS_MACCATALYST @end + +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST diff --git a/Source/iOS/OIDAuthorizationService+IOS.h b/Source/AppAuth/iOS/OIDAuthorizationService+IOS.h similarity index 93% rename from Source/iOS/OIDAuthorizationService+IOS.h rename to Source/AppAuth/iOS/OIDAuthorizationService+IOS.h index 5cdc6cfe4..c6e14f194 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.h +++ b/Source/AppAuth/iOS/OIDAuthorizationService+IOS.h @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + #import #import "OIDAuthorizationService.h" @@ -42,3 +46,5 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST diff --git a/Source/iOS/OIDAuthorizationService+IOS.m b/Source/AppAuth/iOS/OIDAuthorizationService+IOS.m similarity index 93% rename from Source/iOS/OIDAuthorizationService+IOS.m rename to Source/AppAuth/iOS/OIDAuthorizationService+IOS.m index ed6858fbf..05ccff5db 100644 --- a/Source/iOS/OIDAuthorizationService+IOS.m +++ b/Source/AppAuth/iOS/OIDAuthorizationService+IOS.m @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + #import "OIDAuthorizationService+IOS.h" #import "OIDExternalUserAgentIOS.h" #import "OIDExternalUserAgentCatalyst.h" @@ -40,3 +44,5 @@ @implementation OIDAuthorizationService (IOS) @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST diff --git a/Source/iOS/OIDExternalUserAgentCatalyst.h b/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h similarity index 92% rename from Source/iOS/OIDExternalUserAgentCatalyst.h rename to Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h index 9f3487319..d98d4413c 100644 --- a/Source/iOS/OIDExternalUserAgentCatalyst.h +++ b/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + #import #import "OIDExternalUserAgent.h" @@ -44,3 +48,5 @@ API_AVAILABLE(macCatalyst(13)) API_UNAVAILABLE(ios) @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST diff --git a/Source/iOS/OIDExternalUserAgentCatalyst.m b/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m similarity index 97% rename from Source/iOS/OIDExternalUserAgentCatalyst.m rename to Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m index f7093b10a..fc9cef5c0 100644 --- a/Source/iOS/OIDExternalUserAgentCatalyst.m +++ b/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + #import "OIDExternalUserAgentCatalyst.h" #import @@ -137,3 +141,5 @@ - (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuth NS_ASSUME_NONNULL_END #endif // TARGET_OS_MACCATALYST + +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST diff --git a/Source/iOS/OIDExternalUserAgentIOS.h b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h similarity index 92% rename from Source/iOS/OIDExternalUserAgentIOS.h rename to Source/AppAuth/iOS/OIDExternalUserAgentIOS.h index e75225851..7261c050d 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.h +++ b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + #import #import "OIDExternalUserAgent.h" @@ -45,3 +49,5 @@ API_UNAVAILABLE(macCatalyst) @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m similarity index 98% rename from Source/iOS/OIDExternalUserAgentIOS.m rename to Source/AppAuth/iOS/OIDExternalUserAgentIOS.m index 98f8719c5..728f0868d 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + #import "OIDExternalUserAgentIOS.h" #import @@ -248,3 +252,5 @@ - (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuth NS_ASSUME_NONNULL_END #endif // !TARGET_OS_MACCATALYST + +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST diff --git a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.h b/Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h similarity index 97% rename from Source/iOS/OIDExternalUserAgentIOSCustomBrowser.h rename to Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h index 5b0427636..2032e8c91 100644 --- a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.h +++ b/Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + #import #import "OIDExternalUserAgent.h" @@ -105,3 +109,5 @@ API_UNAVAILABLE(macCatalyst) @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST diff --git a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m b/Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m similarity index 98% rename from Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m rename to Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m index 499b14c3b..51d2e56cf 100644 --- a/Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m +++ b/Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + #import "OIDExternalUserAgentIOSCustomBrowser.h" #import @@ -163,3 +167,5 @@ - (void)dismissExternalUserAgentAnimated:(BOOL)animated NS_ASSUME_NONNULL_END #endif // !TARGET_OS_MACCATALYST + +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST diff --git a/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h b/Source/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h similarity index 98% rename from Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h rename to Source/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h index b78bfa51d..ed2ac8794 100644 --- a/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h +++ b/Source/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h @@ -20,6 +20,10 @@ // https://developer.apple.com/library/mac/samplecode/MiniSOAP/Introduction/Intro.html // Modified to limit connections to the loopback interface only. +#import + +#if TARGET_OS_OSX + #import #import @@ -185,4 +189,4 @@ typedef enum { @end - +#endif // TARGET_OS_OSX diff --git a/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m b/Source/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m similarity index 99% rename from Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m rename to Source/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m index 5c9d23bef..dca381c04 100644 --- a/Source/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m +++ b/Source/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_OSX + #import "OIDLoopbackHTTPServer.h" #include #include @@ -610,3 +614,5 @@ - (BOOL)hasIPv6Socket { @end #pragma GCC diagnostic pop + +#endif // TARGET_OS_OSX diff --git a/Source/macOS/OIDAuthState+Mac.h b/Source/AppAuth/macOS/OIDAuthState+Mac.h similarity index 96% rename from Source/macOS/OIDAuthState+Mac.h rename to Source/AppAuth/macOS/OIDAuthState+Mac.h index e412a7886..f32d27c08 100644 --- a/Source/macOS/OIDAuthState+Mac.h +++ b/Source/AppAuth/macOS/OIDAuthState+Mac.h @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_OSX + #import "OIDAuthState.h" NS_ASSUME_NONNULL_BEGIN @@ -43,3 +47,5 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_OSX diff --git a/Source/macOS/OIDAuthState+Mac.m b/Source/AppAuth/macOS/OIDAuthState+Mac.m similarity index 94% rename from Source/macOS/OIDAuthState+Mac.m rename to Source/AppAuth/macOS/OIDAuthState+Mac.m index ce99a7370..16b061724 100644 --- a/Source/macOS/OIDAuthState+Mac.m +++ b/Source/AppAuth/macOS/OIDAuthState+Mac.m @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_OSX + #import "OIDAuthState+Mac.h" #import "OIDExternalUserAgentMac.h" @@ -32,3 +36,5 @@ @implementation OIDAuthState (Mac) } @end + +#endif // TARGET_OS_OSX diff --git a/Source/macOS/OIDAuthorizationService+Mac.h b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h similarity index 95% rename from Source/macOS/OIDAuthorizationService+Mac.h rename to Source/AppAuth/macOS/OIDAuthorizationService+Mac.h index 405d25ce7..37de63f16 100644 --- a/Source/macOS/OIDAuthorizationService+Mac.h +++ b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_OSX + #import "OIDAuthorizationService.h" NS_ASSUME_NONNULL_BEGIN @@ -37,3 +41,5 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_OSX diff --git a/Source/macOS/OIDAuthorizationService+Mac.m b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m similarity index 94% rename from Source/macOS/OIDAuthorizationService+Mac.m rename to Source/AppAuth/macOS/OIDAuthorizationService+Mac.m index 781394253..c0abec8a4 100644 --- a/Source/macOS/OIDAuthorizationService+Mac.m +++ b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_OSX + #import "OIDAuthorizationService+Mac.h" #import "OIDExternalUserAgentMac.h" @@ -35,3 +39,5 @@ @implementation OIDAuthorizationService (Mac) @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_OSX diff --git a/Source/macOS/OIDExternalUserAgentMac.h b/Source/AppAuth/macOS/OIDExternalUserAgentMac.h similarity index 93% rename from Source/macOS/OIDExternalUserAgentMac.h rename to Source/AppAuth/macOS/OIDExternalUserAgentMac.h index ed2d2c276..b7122aee0 100644 --- a/Source/macOS/OIDExternalUserAgentMac.h +++ b/Source/AppAuth/macOS/OIDExternalUserAgentMac.h @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_OSX + #import "OIDExternalUserAgent.h" NS_ASSUME_NONNULL_BEGIN @@ -28,3 +32,5 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_OSX diff --git a/Source/macOS/OIDExternalUserAgentMac.m b/Source/AppAuth/macOS/OIDExternalUserAgentMac.m similarity index 97% rename from Source/macOS/OIDExternalUserAgentMac.m rename to Source/AppAuth/macOS/OIDExternalUserAgentMac.m index 4d6eadf3c..c35a7a327 100644 --- a/Source/macOS/OIDExternalUserAgentMac.m +++ b/Source/AppAuth/macOS/OIDExternalUserAgentMac.m @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_OSX + #import "OIDExternalUserAgentMac.h" #import @@ -73,3 +77,5 @@ - (void)cleanUp { @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_MAC diff --git a/Source/macOS/OIDRedirectHTTPHandler.h b/Source/AppAuth/macOS/OIDRedirectHTTPHandler.h similarity index 98% rename from Source/macOS/OIDRedirectHTTPHandler.h rename to Source/AppAuth/macOS/OIDRedirectHTTPHandler.h index eca431977..831eb0bb3 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.h +++ b/Source/AppAuth/macOS/OIDRedirectHTTPHandler.h @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_OSX + #import NS_ASSUME_NONNULL_BEGIN @@ -82,3 +86,5 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_OSX diff --git a/Source/macOS/OIDRedirectHTTPHandler.m b/Source/AppAuth/macOS/OIDRedirectHTTPHandler.m similarity index 98% rename from Source/macOS/OIDRedirectHTTPHandler.m rename to Source/AppAuth/macOS/OIDRedirectHTTPHandler.m index 6b1814911..8a3df6cc2 100644 --- a/Source/macOS/OIDRedirectHTTPHandler.m +++ b/Source/AppAuth/macOS/OIDRedirectHTTPHandler.m @@ -16,6 +16,10 @@ limitations under the License. */ +#import + +#if TARGET_OS_OSX + #import "OIDRedirectHTTPHandler.h" #import "OIDAuthorizationService.h" @@ -169,3 +173,5 @@ - (void)dealloc { } @end + +#endif // TARGET_OS_MAC diff --git a/Source/OIDAuthState.h b/Source/AppAuthCore/OIDAuthState.h similarity index 100% rename from Source/OIDAuthState.h rename to Source/AppAuthCore/OIDAuthState.h diff --git a/Source/OIDAuthState.m b/Source/AppAuthCore/OIDAuthState.m similarity index 100% rename from Source/OIDAuthState.m rename to Source/AppAuthCore/OIDAuthState.m diff --git a/Source/OIDAuthStateChangeDelegate.h b/Source/AppAuthCore/OIDAuthStateChangeDelegate.h similarity index 100% rename from Source/OIDAuthStateChangeDelegate.h rename to Source/AppAuthCore/OIDAuthStateChangeDelegate.h diff --git a/Source/OIDAuthStateErrorDelegate.h b/Source/AppAuthCore/OIDAuthStateErrorDelegate.h similarity index 100% rename from Source/OIDAuthStateErrorDelegate.h rename to Source/AppAuthCore/OIDAuthStateErrorDelegate.h diff --git a/Source/OIDAuthorizationRequest.h b/Source/AppAuthCore/OIDAuthorizationRequest.h similarity index 100% rename from Source/OIDAuthorizationRequest.h rename to Source/AppAuthCore/OIDAuthorizationRequest.h diff --git a/Source/OIDAuthorizationRequest.m b/Source/AppAuthCore/OIDAuthorizationRequest.m similarity index 100% rename from Source/OIDAuthorizationRequest.m rename to Source/AppAuthCore/OIDAuthorizationRequest.m diff --git a/Source/OIDAuthorizationResponse.h b/Source/AppAuthCore/OIDAuthorizationResponse.h similarity index 100% rename from Source/OIDAuthorizationResponse.h rename to Source/AppAuthCore/OIDAuthorizationResponse.h diff --git a/Source/OIDAuthorizationResponse.m b/Source/AppAuthCore/OIDAuthorizationResponse.m similarity index 100% rename from Source/OIDAuthorizationResponse.m rename to Source/AppAuthCore/OIDAuthorizationResponse.m diff --git a/Source/OIDAuthorizationService.h b/Source/AppAuthCore/OIDAuthorizationService.h similarity index 100% rename from Source/OIDAuthorizationService.h rename to Source/AppAuthCore/OIDAuthorizationService.h diff --git a/Source/OIDAuthorizationService.m b/Source/AppAuthCore/OIDAuthorizationService.m similarity index 100% rename from Source/OIDAuthorizationService.m rename to Source/AppAuthCore/OIDAuthorizationService.m diff --git a/Source/OIDClientMetadataParameters.h b/Source/AppAuthCore/OIDClientMetadataParameters.h similarity index 100% rename from Source/OIDClientMetadataParameters.h rename to Source/AppAuthCore/OIDClientMetadataParameters.h diff --git a/Source/OIDClientMetadataParameters.m b/Source/AppAuthCore/OIDClientMetadataParameters.m similarity index 100% rename from Source/OIDClientMetadataParameters.m rename to Source/AppAuthCore/OIDClientMetadataParameters.m diff --git a/Source/OIDDefines.h b/Source/AppAuthCore/OIDDefines.h similarity index 100% rename from Source/OIDDefines.h rename to Source/AppAuthCore/OIDDefines.h diff --git a/Source/OIDEndSessionRequest.h b/Source/AppAuthCore/OIDEndSessionRequest.h similarity index 100% rename from Source/OIDEndSessionRequest.h rename to Source/AppAuthCore/OIDEndSessionRequest.h diff --git a/Source/OIDEndSessionRequest.m b/Source/AppAuthCore/OIDEndSessionRequest.m similarity index 100% rename from Source/OIDEndSessionRequest.m rename to Source/AppAuthCore/OIDEndSessionRequest.m diff --git a/Source/OIDEndSessionResponse.h b/Source/AppAuthCore/OIDEndSessionResponse.h similarity index 100% rename from Source/OIDEndSessionResponse.h rename to Source/AppAuthCore/OIDEndSessionResponse.h diff --git a/Source/OIDEndSessionResponse.m b/Source/AppAuthCore/OIDEndSessionResponse.m similarity index 100% rename from Source/OIDEndSessionResponse.m rename to Source/AppAuthCore/OIDEndSessionResponse.m diff --git a/Source/OIDError.h b/Source/AppAuthCore/OIDError.h similarity index 100% rename from Source/OIDError.h rename to Source/AppAuthCore/OIDError.h diff --git a/Source/OIDError.m b/Source/AppAuthCore/OIDError.m similarity index 100% rename from Source/OIDError.m rename to Source/AppAuthCore/OIDError.m diff --git a/Source/OIDErrorUtilities.h b/Source/AppAuthCore/OIDErrorUtilities.h similarity index 100% rename from Source/OIDErrorUtilities.h rename to Source/AppAuthCore/OIDErrorUtilities.h diff --git a/Source/OIDErrorUtilities.m b/Source/AppAuthCore/OIDErrorUtilities.m similarity index 100% rename from Source/OIDErrorUtilities.m rename to Source/AppAuthCore/OIDErrorUtilities.m diff --git a/Source/OIDExternalUserAgent.h b/Source/AppAuthCore/OIDExternalUserAgent.h similarity index 100% rename from Source/OIDExternalUserAgent.h rename to Source/AppAuthCore/OIDExternalUserAgent.h diff --git a/Source/OIDExternalUserAgentRequest.h b/Source/AppAuthCore/OIDExternalUserAgentRequest.h similarity index 100% rename from Source/OIDExternalUserAgentRequest.h rename to Source/AppAuthCore/OIDExternalUserAgentRequest.h diff --git a/Source/OIDExternalUserAgentSession.h b/Source/AppAuthCore/OIDExternalUserAgentSession.h similarity index 100% rename from Source/OIDExternalUserAgentSession.h rename to Source/AppAuthCore/OIDExternalUserAgentSession.h diff --git a/Source/OIDFieldMapping.h b/Source/AppAuthCore/OIDFieldMapping.h similarity index 100% rename from Source/OIDFieldMapping.h rename to Source/AppAuthCore/OIDFieldMapping.h diff --git a/Source/OIDFieldMapping.m b/Source/AppAuthCore/OIDFieldMapping.m similarity index 100% rename from Source/OIDFieldMapping.m rename to Source/AppAuthCore/OIDFieldMapping.m diff --git a/Source/OIDGrantTypes.h b/Source/AppAuthCore/OIDGrantTypes.h similarity index 100% rename from Source/OIDGrantTypes.h rename to Source/AppAuthCore/OIDGrantTypes.h diff --git a/Source/OIDGrantTypes.m b/Source/AppAuthCore/OIDGrantTypes.m similarity index 100% rename from Source/OIDGrantTypes.m rename to Source/AppAuthCore/OIDGrantTypes.m diff --git a/Source/OIDIDToken.h b/Source/AppAuthCore/OIDIDToken.h similarity index 100% rename from Source/OIDIDToken.h rename to Source/AppAuthCore/OIDIDToken.h diff --git a/Source/OIDIDToken.m b/Source/AppAuthCore/OIDIDToken.m similarity index 100% rename from Source/OIDIDToken.m rename to Source/AppAuthCore/OIDIDToken.m diff --git a/Source/OIDRegistrationRequest.h b/Source/AppAuthCore/OIDRegistrationRequest.h similarity index 100% rename from Source/OIDRegistrationRequest.h rename to Source/AppAuthCore/OIDRegistrationRequest.h diff --git a/Source/OIDRegistrationRequest.m b/Source/AppAuthCore/OIDRegistrationRequest.m similarity index 100% rename from Source/OIDRegistrationRequest.m rename to Source/AppAuthCore/OIDRegistrationRequest.m diff --git a/Source/OIDRegistrationResponse.h b/Source/AppAuthCore/OIDRegistrationResponse.h similarity index 100% rename from Source/OIDRegistrationResponse.h rename to Source/AppAuthCore/OIDRegistrationResponse.h diff --git a/Source/OIDRegistrationResponse.m b/Source/AppAuthCore/OIDRegistrationResponse.m similarity index 100% rename from Source/OIDRegistrationResponse.m rename to Source/AppAuthCore/OIDRegistrationResponse.m diff --git a/Source/OIDResponseTypes.h b/Source/AppAuthCore/OIDResponseTypes.h similarity index 100% rename from Source/OIDResponseTypes.h rename to Source/AppAuthCore/OIDResponseTypes.h diff --git a/Source/OIDResponseTypes.m b/Source/AppAuthCore/OIDResponseTypes.m similarity index 100% rename from Source/OIDResponseTypes.m rename to Source/AppAuthCore/OIDResponseTypes.m diff --git a/Source/OIDScopeUtilities.h b/Source/AppAuthCore/OIDScopeUtilities.h similarity index 100% rename from Source/OIDScopeUtilities.h rename to Source/AppAuthCore/OIDScopeUtilities.h diff --git a/Source/OIDScopeUtilities.m b/Source/AppAuthCore/OIDScopeUtilities.m similarity index 100% rename from Source/OIDScopeUtilities.m rename to Source/AppAuthCore/OIDScopeUtilities.m diff --git a/Source/OIDScopes.h b/Source/AppAuthCore/OIDScopes.h similarity index 100% rename from Source/OIDScopes.h rename to Source/AppAuthCore/OIDScopes.h diff --git a/Source/OIDScopes.m b/Source/AppAuthCore/OIDScopes.m similarity index 100% rename from Source/OIDScopes.m rename to Source/AppAuthCore/OIDScopes.m diff --git a/Source/OIDServiceConfiguration.h b/Source/AppAuthCore/OIDServiceConfiguration.h similarity index 100% rename from Source/OIDServiceConfiguration.h rename to Source/AppAuthCore/OIDServiceConfiguration.h diff --git a/Source/OIDServiceConfiguration.m b/Source/AppAuthCore/OIDServiceConfiguration.m similarity index 100% rename from Source/OIDServiceConfiguration.m rename to Source/AppAuthCore/OIDServiceConfiguration.m diff --git a/Source/OIDServiceDiscovery.h b/Source/AppAuthCore/OIDServiceDiscovery.h similarity index 100% rename from Source/OIDServiceDiscovery.h rename to Source/AppAuthCore/OIDServiceDiscovery.h diff --git a/Source/OIDServiceDiscovery.m b/Source/AppAuthCore/OIDServiceDiscovery.m similarity index 100% rename from Source/OIDServiceDiscovery.m rename to Source/AppAuthCore/OIDServiceDiscovery.m diff --git a/Source/OIDTokenRequest.h b/Source/AppAuthCore/OIDTokenRequest.h similarity index 100% rename from Source/OIDTokenRequest.h rename to Source/AppAuthCore/OIDTokenRequest.h diff --git a/Source/OIDTokenRequest.m b/Source/AppAuthCore/OIDTokenRequest.m similarity index 100% rename from Source/OIDTokenRequest.m rename to Source/AppAuthCore/OIDTokenRequest.m diff --git a/Source/OIDTokenResponse.h b/Source/AppAuthCore/OIDTokenResponse.h similarity index 100% rename from Source/OIDTokenResponse.h rename to Source/AppAuthCore/OIDTokenResponse.h diff --git a/Source/OIDTokenResponse.m b/Source/AppAuthCore/OIDTokenResponse.m similarity index 100% rename from Source/OIDTokenResponse.m rename to Source/AppAuthCore/OIDTokenResponse.m diff --git a/Source/OIDTokenUtilities.h b/Source/AppAuthCore/OIDTokenUtilities.h similarity index 100% rename from Source/OIDTokenUtilities.h rename to Source/AppAuthCore/OIDTokenUtilities.h diff --git a/Source/OIDTokenUtilities.m b/Source/AppAuthCore/OIDTokenUtilities.m similarity index 100% rename from Source/OIDTokenUtilities.m rename to Source/AppAuthCore/OIDTokenUtilities.m diff --git a/Source/OIDURLQueryComponent.h b/Source/AppAuthCore/OIDURLQueryComponent.h similarity index 100% rename from Source/OIDURLQueryComponent.h rename to Source/AppAuthCore/OIDURLQueryComponent.h diff --git a/Source/OIDURLQueryComponent.m b/Source/AppAuthCore/OIDURLQueryComponent.m similarity index 100% rename from Source/OIDURLQueryComponent.m rename to Source/AppAuthCore/OIDURLQueryComponent.m diff --git a/Source/OIDURLSessionProvider.h b/Source/AppAuthCore/OIDURLSessionProvider.h similarity index 100% rename from Source/OIDURLSessionProvider.h rename to Source/AppAuthCore/OIDURLSessionProvider.h diff --git a/Source/OIDURLSessionProvider.m b/Source/AppAuthCore/OIDURLSessionProvider.m similarity index 100% rename from Source/OIDURLSessionProvider.m rename to Source/AppAuthCore/OIDURLSessionProvider.m diff --git a/UnitTests/OIDAuthStateTests.h b/UnitTests/OIDAuthStateTests.h index 5be7d6059..2f1a62ab1 100644 --- a/UnitTests/OIDAuthStateTests.h +++ b/UnitTests/OIDAuthStateTests.h @@ -18,8 +18,12 @@ #import -#import "Source/OIDAuthStateChangeDelegate.h" -#import "Source/OIDAuthStateErrorDelegate.h" +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDAuthStateChangeDelegate.h" +#import "Source/AppAuthCore/OIDAuthStateErrorDelegate.h" +#endif @class OIDAuthState; diff --git a/UnitTests/OIDAuthStateTests.m b/UnitTests/OIDAuthStateTests.m index 97d3caa01..4d7c3a8b7 100644 --- a/UnitTests/OIDAuthStateTests.m +++ b/UnitTests/OIDAuthStateTests.m @@ -21,11 +21,17 @@ #import "OIDAuthorizationResponseTests.h" #import "OIDRegistrationResponseTests.h" #import "OIDTokenResponseTests.h" -#import "Source/OIDAuthState.h" -#import "Source/OIDAuthorizationResponse.h" -#import "Source/OIDErrorUtilities.h" -#import "Source/OIDRegistrationResponse.h" -#import "Source/OIDTokenResponse.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDAuthState.h" +#import "Source/AppAuthCore/OIDAuthorizationResponse.h" +#import "Source/AppAuthCore/OIDErrorUtilities.h" +#import "Source/AppAuthCore/OIDRegistrationResponse.h" +#import "Source/AppAuthCore/OIDTokenResponse.h" +#endif + #import "OIDTokenRequestTests.h" // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/OIDAuthorizationRequestTests.m b/UnitTests/OIDAuthorizationRequestTests.m index 93b4f5982..06bfc6c13 100644 --- a/UnitTests/OIDAuthorizationRequestTests.m +++ b/UnitTests/OIDAuthorizationRequestTests.m @@ -19,9 +19,14 @@ #import "OIDAuthorizationRequestTests.h" #import "OIDServiceConfigurationTests.h" -#import "Source/OIDAuthorizationRequest.h" -#import "Source/OIDScopeUtilities.h" -#import "Source/OIDServiceConfiguration.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDAuthorizationRequest.h" +#import "Source/AppAuthCore/OIDScopeUtilities.h" +#import "Source/AppAuthCore/OIDServiceConfiguration.h" +#endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of // the XCTAssert___ macros. diff --git a/UnitTests/OIDAuthorizationResponseTests.m b/UnitTests/OIDAuthorizationResponseTests.m index 6206ccab8..10e48eaa8 100644 --- a/UnitTests/OIDAuthorizationResponseTests.m +++ b/UnitTests/OIDAuthorizationResponseTests.m @@ -19,9 +19,14 @@ #import "OIDAuthorizationResponseTests.h" #import "OIDAuthorizationRequestTests.h" -#import "Source/OIDAuthorizationRequest.h" -#import "Source/OIDAuthorizationResponse.h" -#import "Source/OIDGrantTypes.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDAuthorizationRequest.h" +#import "Source/AppAuthCore/OIDAuthorizationResponse.h" +#import "Source/AppAuthCore/OIDGrantTypes.h" +#endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of // the XCTAssert___ macros. diff --git a/UnitTests/OIDEndSessionRequestTests.m b/UnitTests/OIDEndSessionRequestTests.m index 4aadc6b57..4620d3b82 100644 --- a/UnitTests/OIDEndSessionRequestTests.m +++ b/UnitTests/OIDEndSessionRequestTests.m @@ -19,9 +19,14 @@ #import "OIDEndSessionRequestTests.h" #import "OIDServiceDiscoveryTests.h" -#import "Source/OIDEndSessionRequest.h" -#import "Source/OIDServiceConfiguration.h" -#import "Source/OIDServiceDiscovery.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDEndSessionRequest.h" +#import "Source/AppAuthCore/OIDServiceConfiguration.h" +#import "Source/AppAuthCore/OIDServiceDiscovery.h" +#endif /*! @brief Test value for the @c redirectURL property. */ diff --git a/UnitTests/OIDGrantTypesTests.m b/UnitTests/OIDGrantTypesTests.m index fb4c9e3fd..8c06d5701 100644 --- a/UnitTests/OIDGrantTypesTests.m +++ b/UnitTests/OIDGrantTypesTests.m @@ -18,7 +18,11 @@ #import -#import "Source/OIDGrantTypes.h" +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDGrantTypes.h" +#endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of // the XCTAssert___ macros. diff --git a/UnitTests/OIDRegistrationRequestTests.m b/UnitTests/OIDRegistrationRequestTests.m index 424f3cc31..5fbbd585e 100644 --- a/UnitTests/OIDRegistrationRequestTests.m +++ b/UnitTests/OIDRegistrationRequestTests.m @@ -19,9 +19,14 @@ #import "OIDRegistrationRequestTests.h" #import "OIDServiceConfigurationTests.h" -#import "Source/OIDClientMetadataParameters.h" -#import "Source/OIDRegistrationRequest.h" -#import "Source/OIDServiceConfiguration.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDClientMetadataParameters.h" +#import "Source/AppAuthCore/OIDRegistrationRequest.h" +#import "Source/AppAuthCore/OIDServiceConfiguration.h" +#endif /*! @brief Test key for the @c additionalParameters property. */ diff --git a/UnitTests/OIDRegistrationResponseTests.m b/UnitTests/OIDRegistrationResponseTests.m index f679e1adf..f747cc244 100644 --- a/UnitTests/OIDRegistrationResponseTests.m +++ b/UnitTests/OIDRegistrationResponseTests.m @@ -20,8 +20,13 @@ #import "OIDClientMetadataParameters.h" #import "OIDRegistrationRequestTests.h" -#import "Source/OIDRegistrationRequest.h" -#import "Source/OIDRegistrationResponse.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDRegistrationRequest.h" +#import "Source/AppAuthCore/OIDRegistrationResponse.h" +#endif /*! @brief The test value for the @c clientID property. */ diff --git a/UnitTests/OIDResponseTypesTests.m b/UnitTests/OIDResponseTypesTests.m index b55863afc..5402ebbf5 100644 --- a/UnitTests/OIDResponseTypesTests.m +++ b/UnitTests/OIDResponseTypesTests.m @@ -18,7 +18,11 @@ #import -#import "Source/OIDResponseTypes.h" +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDResponseTypes.h" +#endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of // the XCTAssert___ macros. diff --git a/UnitTests/OIDScopesTests.m b/UnitTests/OIDScopesTests.m index f6ffb9a6f..2d9173748 100644 --- a/UnitTests/OIDScopesTests.m +++ b/UnitTests/OIDScopesTests.m @@ -18,7 +18,11 @@ #import -#import "Source/OIDScopes.h" +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDScopes.h" +#endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of // the XCTAssert___ macros. diff --git a/UnitTests/OIDServiceConfigurationTests.m b/UnitTests/OIDServiceConfigurationTests.m index ff7e30932..3be785e60 100644 --- a/UnitTests/OIDServiceConfigurationTests.m +++ b/UnitTests/OIDServiceConfigurationTests.m @@ -21,10 +21,15 @@ #import #import "OIDServiceDiscoveryTests.h" -#import "Source/OIDAuthorizationService.h" -#import "Source/OIDError.h" -#import "Source/OIDServiceConfiguration.h" -#import "Source/OIDServiceDiscovery.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDAuthorizationService.h" +#import "Source/AppAuthCore/OIDError.h" +#import "Source/AppAuthCore/OIDServiceConfiguration.h" +#import "Source/AppAuthCore/OIDServiceDiscovery.h" +#endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of // the XCTAssert___ macros. diff --git a/UnitTests/OIDServiceDiscoveryTests.m b/UnitTests/OIDServiceDiscoveryTests.m index b9a13ce86..574cd794f 100644 --- a/UnitTests/OIDServiceDiscoveryTests.m +++ b/UnitTests/OIDServiceDiscoveryTests.m @@ -18,8 +18,12 @@ #import "OIDServiceDiscoveryTests.h" -#import "Source/OIDError.h" -#import "Source/OIDServiceDiscovery.h" +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDError.h" +#import "Source/AppAuthCore/OIDServiceDiscovery.h" +#endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of // the XCTAssert___ macros. diff --git a/UnitTests/OIDSwiftTests.swift b/UnitTests/OIDSwiftTests.swift index bd3536922..59ee443cc 100644 --- a/UnitTests/OIDSwiftTests.swift +++ b/UnitTests/OIDSwiftTests.swift @@ -19,6 +19,10 @@ import Foundation import XCTest +#if SWIFT_PACKAGE +import AppAuthCore +#endif + /*! @brief Unit tests to verify Swift compatability. */ class OIDSwiftTests: XCTestCase { diff --git a/UnitTests/OIDTokenRequestTests.m b/UnitTests/OIDTokenRequestTests.m index 3913b8325..4211ef70a 100644 --- a/UnitTests/OIDTokenRequestTests.m +++ b/UnitTests/OIDTokenRequestTests.m @@ -20,11 +20,16 @@ #import "OIDAuthorizationResponseTests.h" #import "OIDServiceConfigurationTests.h" -#import "Source/OIDAuthorizationRequest.h" -#import "Source/OIDAuthorizationResponse.h" -#import "Source/OIDScopeUtilities.h" -#import "Source/OIDServiceConfiguration.h" -#import "Source/OIDTokenRequest.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDAuthorizationRequest.h" +#import "Source/AppAuthCore/OIDAuthorizationResponse.h" +#import "Source/AppAuthCore/OIDScopeUtilities.h" +#import "Source/AppAuthCore/OIDServiceConfiguration.h" +#import "Source/AppAuthCore/OIDTokenRequest.h" +#endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of // the XCTAssert___ macros. diff --git a/UnitTests/OIDTokenResponseTests.m b/UnitTests/OIDTokenResponseTests.m index 1b4051f5f..5d0a8f8ae 100644 --- a/UnitTests/OIDTokenResponseTests.m +++ b/UnitTests/OIDTokenResponseTests.m @@ -19,8 +19,13 @@ #import "OIDAuthorizationResponseTests.h" #import "OIDTokenRequestTests.h" -#import "Source/OIDTokenRequest.h" -#import "Source/OIDTokenResponse.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDTokenRequest.h" +#import "Source/AppAuthCore/OIDTokenResponse.h" +#endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of // the XCTAssert___ macros. diff --git a/UnitTests/OIDTokenUtilitiesTests.m b/UnitTests/OIDTokenUtilitiesTests.m index e4da624de..0a7ff15ec 100644 --- a/UnitTests/OIDTokenUtilitiesTests.m +++ b/UnitTests/OIDTokenUtilitiesTests.m @@ -18,8 +18,11 @@ #import -#import "Source/OIDTokenUtilities.h" - +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDTokenUtilities.h" +#endif @interface OIDTokenUtilitiesTests : XCTestCase @end diff --git a/UnitTests/OIDURLQueryComponentTests.m b/UnitTests/OIDURLQueryComponentTests.m index 65085f683..d0eec3e5c 100644 --- a/UnitTests/OIDURLQueryComponentTests.m +++ b/UnitTests/OIDURLQueryComponentTests.m @@ -18,7 +18,11 @@ #import "OIDURLQueryComponentTests.h" -#import "Source/OIDURLQueryComponent.h" +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDURLQueryComponent.h" +#endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of // the XCTAssert___ macros. diff --git a/UnitTests/OIDURLQueryComponentTestsIOS7.m b/UnitTests/OIDURLQueryComponentTestsIOS7.m index 707ba5fbb..af26ef107 100644 --- a/UnitTests/OIDURLQueryComponentTestsIOS7.m +++ b/UnitTests/OIDURLQueryComponentTestsIOS7.m @@ -18,7 +18,11 @@ #import "OIDURLQueryComponentTests.h" -#import "Source/OIDURLQueryComponent.h" +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDURLQueryComponent.h" +#endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of // the XCTAssert___ macros. From 951d4e75b41f67229082720c4b5b882371078bf1 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Tue, 2 Jun 2020 19:38:59 -0400 Subject: [PATCH 111/204] Bump podspec version to 1.4.0 --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 94ccadc72..819560619 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.3.1" + s.version = "1.4.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC From 01131d68346c8ae552961c768d583c715fbe1410 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Tue, 2 Jun 2020 19:39:50 -0400 Subject: [PATCH 112/204] Added changelog for 1.4.0 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb829815e..7ebb29dee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # AppAuth for iOS and macOS Changelog +## 1.4.0 + +### Added + +1. Support for Swift Package Manager + ## 1.3.1 ### Fixes From 36f12d0871b2a63a7b00d0fe160319fff5ba0f6d Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Tue, 2 Jun 2020 20:57:26 -0400 Subject: [PATCH 113/204] Add badges to README --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f54c2c39..66405977c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ ![AppAuth for iOS and macOS](https://rawgit.com/openid/AppAuth-iOS/master/appauth_lockup.svg) [![Build Status](https://travis-ci.org/openid/AppAuth-iOS.svg?branch=master)](https://travis-ci.org/openid/AppAuth-iOS) -[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![codecov](https://codecov.io/gh/openid/AppAuth-iOS/branch/master/graph/badge.svg)](https://codecov.io/gh/openid/AppAuth-iOS) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg?style=flat)](https://github.com/Carthage/Carthage) +[![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager) +[![Pod Version](https://img.shields.io/cocoapods/v/AppAuth.svg?style=flat)](https://cocoapods.org/pods/AppAuth) +[![Pod License](https://img.shields.io/cocoapods/l/AppAuth.svg?style=flat)](https://github.com/openid/AppAuth-iOS/blob/master/LICENSE) +[![Pod Platform](https://img.shields.io/cocoapods/p/AppAuth.svg?style=flat)](https://cocoapods.org/pods/AppAuth) +[![Catalyst compatible](https://img.shields.io/badge/Catalyst-compatible-brightgreen.svg?style=flat)](https://developer.apple.com/documentation/xcode/creating_a_mac_version_of_your_ipad_app) AppAuth for iOS and macOS is a client SDK for communicating with [OAuth 2.0](https://tools.ietf.org/html/rfc6749) and From df840e881d2f9d80c8af037c6acf9573175ecaff Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Tue, 9 Jun 2020 11:55:28 -0400 Subject: [PATCH 114/204] Make Core, ExternalUserAgent the default subspecs Make ExternalUserAgent subspec depend on Core. Use deployment_target instead of platform. --- AppAuth.podspec | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 819560619..73ff1c808 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -31,7 +31,10 @@ It follows the OAuth 2.0 for Native Apps best current practice # classes of AppAuth with tokens on watchOS and tvOS, but currently the # library won't help you obtain authorization grants on those platforms. - s.platforms = { :ios => "7.0", :osx => "10.9", :watchos => "2.0", :tvos => "9.0" } + s.ios.deployment_target = "7.0" + s.osx.deployment_target = "10.9" + s.watchos.deployment_target = "2.0" + s.tvos.deployment_target = "9.0" s.source = { :git => "https://github.com/openid/AppAuth-iOS.git", :tag => s.version } s.requires_arc = true @@ -44,8 +47,9 @@ It follows the OAuth 2.0 for Native Apps best current practice # Subspec for the full AppAuth library, including platform-dependant external user agents. s.subspec 'ExternalUserAgent' do |externalUserAgent| - - externalUserAgent.source_files = "Source/*.{h,m}", "Source/AppAuthCore/*.{h,m}", "Source/AppAuth/*.{h,m}" + externalUserAgent.dependency 'AppAuth/Core' + + externalUserAgent.source_files = "Source/AppAuth.h", "Source/AppAuth/*.{h,m}" # iOS externalUserAgent.ios.source_files = "Source/AppAuth/iOS/**/*.{h,m}" @@ -55,6 +59,8 @@ It follows the OAuth 2.0 for Native Apps best current practice # macOS externalUserAgent.osx.source_files = "Source/AppAuth/macOS/**/*.{h,m}" - externalUserAgent.osx.deployment_target = '10.9' + externalUserAgent.osx.deployment_target = '10.9' end + + s.default_subspec = 'Core', 'ExternalUserAgent' end From e8d2864225ae8fce4714d44d74a4073e25190686 Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Fri, 10 Jul 2020 14:28:18 -0400 Subject: [PATCH 115/204] Move OIDExternalAgentCustomBrowser into its own subspec and framework Created the EntepriseUserAgent subspec, and AppAuthEnterpriseUserAgent framework target to house this and future external user agents aimed at enterprises. --- AppAuth.podspec | 12 +- AppAuth.xcodeproj/project.pbxproj | 272 +++++++++++++++++- .../AppAuthEnterpriseUserAgent.xcscheme | 67 +++++ .../xcschemes/AppAuth_tvOS.xcscheme | 22 +- Package.swift | 15 +- README.md | 41 ++- Source/AppAuth.h | 1 - Source/AppAuthEnterpriseUserAgent.h | 21 ++ .../OIDExternalUserAgentIOSCustomBrowser.h | 0 .../OIDExternalUserAgentIOSCustomBrowser.m | 0 .../AppAuthEnterpriseUserAgent.h | 59 ++++ .../EnterpriseUserAgentFramework/Info.plist | 26 ++ Source/Framework/AppAuth.h | 1 - 13 files changed, 509 insertions(+), 28 deletions(-) create mode 100644 AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthEnterpriseUserAgent.xcscheme create mode 100644 Source/AppAuthEnterpriseUserAgent.h rename Source/{AppAuth => AppAuthEnterpriseUserAgent}/iOS/OIDExternalUserAgentIOSCustomBrowser.h (100%) rename Source/{AppAuth => AppAuthEnterpriseUserAgent}/iOS/OIDExternalUserAgentIOSCustomBrowser.m (100%) create mode 100644 Source/EnterpriseUserAgentFramework/AppAuthEnterpriseUserAgent.h create mode 100644 Source/EnterpriseUserAgentFramework/Info.plist diff --git a/AppAuth.podspec b/AppAuth.podspec index 73ff1c808..b34dbe3f1 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -41,8 +41,7 @@ It follows the OAuth 2.0 for Native Apps best current practice # Subspec for the core AppAuth library classes only, suitable for extensions. s.subspec 'Core' do |core| - core.source_files = "Source/*.{h,m}", "Source/AppAuthCore/*.{h,m}" - core.exclude_files = "Source/AppAuth.h" + core.source_files = "Source/AppAuthCore.h", "Source/AppAuthCore/*.{h,m}" end # Subspec for the full AppAuth library, including platform-dependant external user agents. @@ -62,5 +61,12 @@ It follows the OAuth 2.0 for Native Apps best current practice externalUserAgent.osx.deployment_target = '10.9' end - s.default_subspec = 'Core', 'ExternalUserAgent' + s.subspec 'EnterpriseUserAgent' do |enterpriseUserAgent| + enterpriseUserAgent.dependency 'AppAuth/Core' + + enterpriseUserAgent.ios.source_files = "Source/AppAuthEnterpriseUserAgent.h", "Source/AppAuthEnterpriseUserAgent/iOS/**/*.{h,m}" + enterpriseUserAgent.ios.deployment_target = "7.0" + end + + s.default_subspecs = 'Core', 'ExternalUserAgent' end diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index bac5ad53b..058bb712b 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -13,6 +13,67 @@ 06C19E9B22B474A200C19CE1 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; 06C19E9C22B474A600C19CE1 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 06C19E9D22B474AD00C19CE1 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + 2D0BB86C249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D0BB86B249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B81F249053190005B197 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; + 2D91B820249053190005B197 /* OIDExternalUserAgentCatalyst.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A7082D2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m */; }; + 2D91B821249053190005B197 /* OIDFieldMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C41C5D8243000EF209 /* OIDFieldMapping.m */; }; + 2D91B822249053190005B197 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 2D91B823249053190005B197 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + 2D91B824249053190005B197 /* OIDAuthState.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741BB1C5D8243000EF209 /* OIDAuthState.m */; }; + 2D91B825249053190005B197 /* OIDAuthState+IOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */; }; + 2D91B826249053190005B197 /* OIDTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D41C5D8243000EF209 /* OIDTokenResponse.m */; }; + 2D91B827249053190005B197 /* OIDErrorUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C21C5D8243000EF209 /* OIDErrorUtilities.m */; }; + 2D91B828249053190005B197 /* OIDURLQueryComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D81C5D8243000EF209 /* OIDURLQueryComponent.m */; }; + 2D91B829249053190005B197 /* OIDAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */; }; + 2D91B82A249053190005B197 /* OIDAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B91C5D8243000EF209 /* OIDAuthorizationService.m */; }; + 2D91B82B249053190005B197 /* OIDClientMetadataParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F791DE4276800DA0DC3 /* OIDClientMetadataParameters.m */; }; + 2D91B82C249053190005B197 /* OIDTokenUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D61C5D8243000EF209 /* OIDTokenUtilities.m */; }; + 2D91B82D249053190005B197 /* OIDServiceDiscovery.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D01C5D8243000EF209 /* OIDServiceDiscovery.m */; }; + 2D91B82E249053190005B197 /* OIDTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D21C5D8243000EF209 /* OIDTokenRequest.m */; }; + 2D91B82F249053190005B197 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */; }; + 2D91B830249053190005B197 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + 2D91B831249053190005B197 /* OIDAuthorizationService+IOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */; }; + 2D91B832249053190005B197 /* OIDServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CE1C5D8243000EF209 /* OIDServiceConfiguration.m */; }; + 2D91B833249053190005B197 /* OIDRegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */; }; + 2D91B834249053190005B197 /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; + 2D91B835249053190005B197 /* OIDScopes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CA1C5D8243000EF209 /* OIDScopes.m */; }; + 2D91B836249053190005B197 /* OIDScopeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CC1C5D8243000EF209 /* OIDScopeUtilities.m */; }; + 2D91B837249053190005B197 /* OIDGrantTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C61C5D8243000EF209 /* OIDGrantTypes.m */; }; + 2D91B838249053190005B197 /* OIDRegistrationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7B1DE42E1000DA0DC3 /* OIDRegistrationRequest.m */; }; + 2D91B839249053190005B197 /* OIDResponseTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C81C5D8243000EF209 /* OIDResponseTypes.m */; }; + 2D91B83A249053190005B197 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; + 2D91B83B249053190005B197 /* OIDError.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C01C5D8243000EF209 /* OIDError.m */; }; + 2D91B83E249053190005B197 /* OIDAuthorizationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B83F249053190005B197 /* OIDScopes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C91C5D8243000EF209 /* OIDScopes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B840249053190005B197 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B841249053190005B197 /* OIDAuthStateChangeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BC1C5D8243000EF209 /* OIDAuthStateChangeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B842249053190005B197 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B844249053190005B197 /* OIDExternalUserAgentCatalyst.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A7082C2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B845249053190005B197 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B846249053190005B197 /* OIDResponseTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C71C5D8243000EF209 /* OIDResponseTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B847249053190005B197 /* OIDTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D11C5D8243000EF209 /* OIDTokenRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B848249053190005B197 /* OIDScopeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CB1C5D8243000EF209 /* OIDScopeUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B849249053190005B197 /* OIDTokenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D31C5D8243000EF209 /* OIDTokenResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B84A249053190005B197 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B84B249053190005B197 /* OIDServiceDiscovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CF1C5D8243000EF209 /* OIDServiceDiscovery.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B84C249053190005B197 /* OIDGrantTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C51C5D8243000EF209 /* OIDGrantTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B84D249053190005B197 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B84E249053190005B197 /* OIDAuthState+IOS.h in Headers */ = {isa = PBXBuildFile; fileRef = F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B84F249053190005B197 /* OIDRegistrationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 60140F7E1DE4335200DA0DC3 /* OIDRegistrationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B850249053190005B197 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B851249053190005B197 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B852249053190005B197 /* OIDServiceConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CD1C5D8243000EF209 /* OIDServiceConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B853249053190005B197 /* OIDAuthStateErrorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BD1C5D8243000EF209 /* OIDAuthStateErrorDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B854249053190005B197 /* OIDAuthorizationService.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B81C5D8243000EF209 /* OIDAuthorizationService.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B855249053190005B197 /* OIDRegistrationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 60140F7D1DE42E3000DA0DC3 /* OIDRegistrationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B856249053190005B197 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B857249053190005B197 /* OIDAuthState.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BA1C5D8243000EF209 /* OIDAuthState.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B858249053190005B197 /* OIDErrorUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C11C5D8243000EF209 /* OIDErrorUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B859249053190005B197 /* OIDAuthorizationService+IOS.h in Headers */ = {isa = PBXBuildFile; fileRef = F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B85A249053190005B197 /* OIDAuthorizationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B85B249053190005B197 /* OIDTokenUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D51C5D8243000EF209 /* OIDTokenUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B85C249053190005B197 /* OIDError.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BF1C5D8243000EF209 /* OIDError.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D91B85D249053190005B197 /* OIDExternalUserAgentIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 340DAE571D5821A100EC285B /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; 340DAE581D5821A100EC285B /* OIDExternalUserAgentMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentMac.m */; }; 340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */; }; @@ -384,9 +445,6 @@ 343AAB9B1E834A8800F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB9C1E834A8900F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 343AAB9D1E834A8A00F9D36E /* AppAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 343AAA4D1E8345B600F9D36E /* AppAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 345AE747202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */; }; - 345AE748202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */; }; - 345AE749202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 347423E41E7F3C4000D3E6D6 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; 347423FF1E7F4BA000D3E6D6 /* OIDAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */; }; 347424001E7F4BA000D3E6D6 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; @@ -605,6 +663,10 @@ 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDURLSessionProvider.h; sourceTree = ""; }; 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDURLSessionProvider.m; sourceTree = ""; }; 0396974C1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDURLSessionProviderTests.m; sourceTree = ""; }; + 2D0BB86A249D5B75005BA653 /* AppAuthEnterpriseUserAgent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppAuthEnterpriseUserAgent.h; sourceTree = ""; }; + 2D0BB86B249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAuthEnterpriseUserAgent.h; sourceTree = ""; }; + 2D91B862249053190005B197 /* AppAuthEnterpriseUserAgent.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuthEnterpriseUserAgent.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D91B868249177180005B197 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 340DAE251D581FE700EC285B /* OIDAuthorizationService+Mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OIDAuthorizationService+Mac.h"; sourceTree = ""; }; 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OIDAuthorizationService+Mac.m"; sourceTree = ""; }; 340DAE271D581FE700EC285B /* OIDExternalUserAgentMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentMac.h; sourceTree = ""; }; @@ -737,6 +799,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 2D91B83C249053190005B197 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 340DAE4B1D58216A00EC285B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -862,6 +931,32 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 2D47AAD2249988770059B5A4 /* AppAuthEnterpriseUserAgent */ = { + isa = PBXGroup; + children = ( + 2D47AAD3249988B90059B5A4 /* iOS */, + ); + path = AppAuthEnterpriseUserAgent; + sourceTree = ""; + }; + 2D47AAD3249988B90059B5A4 /* iOS */ = { + isa = PBXGroup; + children = ( + 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */, + 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */, + ); + path = iOS; + sourceTree = ""; + }; + 2D91B867249177180005B197 /* EnterpriseUserAgentFramework */ = { + isa = PBXGroup; + children = ( + 2D0BB86B249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h */, + 2D91B868249177180005B197 /* Info.plist */, + ); + path = EnterpriseUserAgentFramework; + sourceTree = ""; + }; 340DAE241D581FE700EC285B /* macOS */ = { isa = PBXGroup; children = ( @@ -909,6 +1004,7 @@ 343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */, 342F42C32177B1FC00574F24 /* AppAuthCore.framework */, 348970972177B3B000ABEED4 /* AppAuthCoreTests.xctest */, + 2D91B862249053190005B197 /* AppAuthEnterpriseUserAgent.framework */, ); name = Products; sourceTree = ""; @@ -916,12 +1012,15 @@ 341741AE1C5D8243000EF209 /* Source */ = { isa = PBXGroup; children = ( + 2D47AAD2249988770059B5A4 /* AppAuthEnterpriseUserAgent */, + 2D91B867249177180005B197 /* EnterpriseUserAgentFramework */, 348970992178F40600ABEED4 /* CoreFramework */, 343AAA4C1E8345B600F9D36E /* Framework */, 8A9B9D5E24561EC40055353E /* AppAuthCore */, 8A9B9D632456227D0055353E /* AppAuth */, 341741AF1C5D8243000EF209 /* AppAuth.h */, 3489709E21791B0C00ABEED4 /* AppAuthCore.h */, + 2D0BB86A249D5B75005BA653 /* AppAuthEnterpriseUserAgent.h */, ); path = Source; sourceTree = ""; @@ -1078,8 +1177,6 @@ children = ( F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */, F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */, - 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */, - 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */, F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */, F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */, A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */, @@ -1093,6 +1190,45 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 2D91B83D249053190005B197 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D0BB86C249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h in Headers */, + 2D91B842249053190005B197 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */, + 2D91B83E249053190005B197 /* OIDAuthorizationResponse.h in Headers */, + 2D91B83F249053190005B197 /* OIDScopes.h in Headers */, + 2D91B840249053190005B197 /* OIDExternalUserAgentRequest.h in Headers */, + 2D91B841249053190005B197 /* OIDAuthStateChangeDelegate.h in Headers */, + 2D91B845249053190005B197 /* OIDIDToken.h in Headers */, + 2D91B846249053190005B197 /* OIDResponseTypes.h in Headers */, + 2D91B847249053190005B197 /* OIDTokenRequest.h in Headers */, + 2D91B848249053190005B197 /* OIDScopeUtilities.h in Headers */, + 2D91B849249053190005B197 /* OIDTokenResponse.h in Headers */, + 2D91B84A249053190005B197 /* OIDEndSessionResponse.h in Headers */, + 2D91B84B249053190005B197 /* OIDServiceDiscovery.h in Headers */, + 2D91B84C249053190005B197 /* OIDGrantTypes.h in Headers */, + 2D91B84D249053190005B197 /* OIDURLSessionProvider.h in Headers */, + 2D91B84F249053190005B197 /* OIDRegistrationResponse.h in Headers */, + 2D91B850249053190005B197 /* OIDExternalUserAgent.h in Headers */, + 2D91B851249053190005B197 /* OIDExternalUserAgentSession.h in Headers */, + 2D91B852249053190005B197 /* OIDServiceConfiguration.h in Headers */, + 2D91B853249053190005B197 /* OIDAuthStateErrorDelegate.h in Headers */, + 2D91B854249053190005B197 /* OIDAuthorizationService.h in Headers */, + 2D91B855249053190005B197 /* OIDRegistrationRequest.h in Headers */, + 2D91B856249053190005B197 /* OIDEndSessionRequest.h in Headers */, + 2D91B857249053190005B197 /* OIDAuthState.h in Headers */, + 2D91B858249053190005B197 /* OIDErrorUtilities.h in Headers */, + 2D91B85A249053190005B197 /* OIDAuthorizationRequest.h in Headers */, + 2D91B85B249053190005B197 /* OIDTokenUtilities.h in Headers */, + 2D91B85C249053190005B197 /* OIDError.h in Headers */, + 2D91B859249053190005B197 /* OIDAuthorizationService+IOS.h in Headers */, + 2D91B84E249053190005B197 /* OIDAuthState+IOS.h in Headers */, + 2D91B85D249053190005B197 /* OIDExternalUserAgentIOS.h in Headers */, + 2D91B844249053190005B197 /* OIDExternalUserAgentCatalyst.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 340DAE4C1D58216A00EC285B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1144,7 +1280,6 @@ 343AAAF31E83499000F9D36E /* OIDScopes.h in Headers */, A6DEAB9F2018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAAE81E83499000F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, - 345AE749202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */, 343AAA6B1E83465500F9D36E /* AppAuth.h in Headers */, F9A7082E2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h in Headers */, 34A663291E871DD40060B664 /* OIDIDToken.h in Headers */, @@ -1285,6 +1420,24 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 2D91B81D249053190005B197 /* AppAuthEnterpriseUserAgent */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D91B85F249053190005B197 /* Build configuration list for PBXNativeTarget "AppAuthEnterpriseUserAgent" */; + buildPhases = ( + 2D91B81E249053190005B197 /* Sources */, + 2D91B83C249053190005B197 /* Frameworks */, + 2D91B83D249053190005B197 /* Headers */, + 2D91B85E249053190005B197 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AppAuthEnterpriseUserAgent; + productName = AppAuth_iOS; + productReference = 2D91B862249053190005B197 /* AppAuthEnterpriseUserAgent.framework */; + productType = "com.apple.product-type.framework"; + }; 340DAE4D1D58216A00EC285B /* AppAuth-macOS */ = { isa = PBXNativeTarget; buildConfigurationList = 340DAE541D58216A00EC285B /* Build configuration list for PBXNativeTarget "AppAuth-macOS" */; @@ -1663,11 +1816,19 @@ 343AAAC91E8348AA00F9D36E /* AppAuth_macOSTests */, 342F42842177B1FC00574F24 /* AppAuthCore */, 3489707D2177B3B000ABEED4 /* AppAuthCoreTests */, + 2D91B81D249053190005B197 /* AppAuthEnterpriseUserAgent */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 2D91B85E249053190005B197 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 341741EE1C5D8283000EF209 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1755,6 +1916,42 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 2D91B81E249053190005B197 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D91B82F249053190005B197 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */, + 2D91B821249053190005B197 /* OIDFieldMapping.m in Sources */, + 2D91B822249053190005B197 /* OIDIDToken.m in Sources */, + 2D91B823249053190005B197 /* OIDEndSessionResponse.m in Sources */, + 2D91B824249053190005B197 /* OIDAuthState.m in Sources */, + 2D91B826249053190005B197 /* OIDTokenResponse.m in Sources */, + 2D91B827249053190005B197 /* OIDErrorUtilities.m in Sources */, + 2D91B828249053190005B197 /* OIDURLQueryComponent.m in Sources */, + 2D91B829249053190005B197 /* OIDAuthorizationRequest.m in Sources */, + 2D91B82A249053190005B197 /* OIDAuthorizationService.m in Sources */, + 2D91B82B249053190005B197 /* OIDClientMetadataParameters.m in Sources */, + 2D91B82C249053190005B197 /* OIDTokenUtilities.m in Sources */, + 2D91B82D249053190005B197 /* OIDServiceDiscovery.m in Sources */, + 2D91B82E249053190005B197 /* OIDTokenRequest.m in Sources */, + 2D91B830249053190005B197 /* OIDEndSessionRequest.m in Sources */, + 2D91B832249053190005B197 /* OIDServiceConfiguration.m in Sources */, + 2D91B833249053190005B197 /* OIDRegistrationResponse.m in Sources */, + 2D91B834249053190005B197 /* OIDURLSessionProvider.m in Sources */, + 2D91B835249053190005B197 /* OIDScopes.m in Sources */, + 2D91B836249053190005B197 /* OIDScopeUtilities.m in Sources */, + 2D91B837249053190005B197 /* OIDGrantTypes.m in Sources */, + 2D91B838249053190005B197 /* OIDRegistrationRequest.m in Sources */, + 2D91B839249053190005B197 /* OIDResponseTypes.m in Sources */, + 2D91B83A249053190005B197 /* OIDAuthorizationResponse.m in Sources */, + 2D91B83B249053190005B197 /* OIDError.m in Sources */, + 2D91B825249053190005B197 /* OIDAuthState+IOS.m in Sources */, + 2D91B831249053190005B197 /* OIDAuthorizationService+IOS.m in Sources */, + 2D91B81F249053190005B197 /* OIDExternalUserAgentIOS.m in Sources */, + 2D91B820249053190005B197 /* OIDExternalUserAgentCatalyst.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 340DAE4A1D58216A00EC285B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1812,7 +2009,6 @@ 341741DE1C5D8243000EF209 /* OIDAuthState.m in Sources */, CF37C06E1F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */, 341741DD1C5D8243000EF209 /* OIDAuthorizationService.m in Sources */, - 345AE747202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */, 341741EB1C5D8243000EF209 /* OIDURLQueryComponent.m in Sources */, 341741E11C5D8243000EF209 /* OIDFieldMapping.m in Sources */, 039697461FA8258D003D1FB2 /* OIDURLSessionProvider.m in Sources */, @@ -1980,7 +2176,6 @@ 343AAA931E83478900F9D36E /* OIDTokenUtilities.m in Sources */, 343AAA901E83478900F9D36E /* OIDServiceDiscovery.m in Sources */, 343AAA911E83478900F9D36E /* OIDTokenRequest.m in Sources */, - 345AE748202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */, A6DEAB8A2017A7140022AC32 /* OIDEndSessionRequest.m in Sources */, 343AAA6F1E83467D00F9D36E /* OIDAuthorizationService+IOS.m in Sources */, 343AAA8F1E83478900F9D36E /* OIDServiceConfiguration.m in Sources */, @@ -2261,6 +2456,58 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 2D91B860249053190005B197 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Source/EnterpriseUserAgentFramework/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthEnterpriseUserAgent; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 2D91B861249053190005B197 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Source/EnterpriseUserAgentFramework/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthEnterpriseUserAgent; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; 340DAE551D58216A00EC285B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2943,6 +3190,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 2D91B85F249053190005B197 /* Build configuration list for PBXNativeTarget "AppAuthEnterpriseUserAgent" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D91B860249053190005B197 /* Debug */, + 2D91B861249053190005B197 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 340DAE541D58216A00EC285B /* Build configuration list for PBXNativeTarget "AppAuth-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthEnterpriseUserAgent.xcscheme b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthEnterpriseUserAgent.xcscheme new file mode 100644 index 000000000..3c18d8b46 --- /dev/null +++ b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthEnterpriseUserAgent.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_tvOS.xcscheme b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_tvOS.xcscheme index 47b0457ab..ade3db91f 100644 --- a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_tvOS.xcscheme +++ b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth_tvOS.xcscheme @@ -27,6 +27,15 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + @@ -39,17 +48,6 @@ - - - - - - - - `. This includes all the files included by AppAuth/AppAuthCore, so only this import is necessary. + +##### Swift Package Manager +Include the `AppAuthEnterpriseUserAgent` target alongside any targets you were already using. + +Make sure to import `AppAuthEnterpriseUserAgent.h` in addition to `AppAuth.h` if you are using the full `AppAuth` functionality. + Here's how to configure AppAuth to use a custom browser using the `OIDExternalUserAgentIOSCustomBrowser` user agent: @@ -526,6 +544,9 @@ This is required so that AppAuth can test for the browser and open the app store if it's not installed (the default behavior of this user-agent). You only need to include the URL scheme of the actual browser you intend to use. +Next, make sure to import the correct header file. +If using CocoaPods/Swift Package manager, make sure to import AppAuthEnterpriseUserAgent alongside AppAuth/AppAuthCore. + Objective-C ```objc // performs authentication request @@ -535,7 +556,7 @@ id userAgent = [OIDExternalUserAgentIOSCustomBrowser CustomBrowserChrome]; appDelegate.currentAuthorizationFlow = [OIDAuthState authStateByPresentingAuthorizationRequest:request - externalUserAgent:self + externalUserAgent:userAgent callback:^(OIDAuthState *_Nullable authState, NSError *_Nullable error) { if (authState) { @@ -549,6 +570,24 @@ appDelegate.currentAuthorizationFlow = }]; ``` +Swift +``` +guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { + self.logMessage("Error accessing AppDelegate") + return + } +let userAgent = OIDExternalUserAgentIOSCustomBrowser.customBrowserChrome() +appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, externalUserAgent: userAgent) { authState, error in + if let authState = authState { + self.setAuthState(authState) + self.logMessage("Got authorization tokens. Access token: \(authState.lastTokenResponse?.accessToken ?? "DEFAULT_TOKEN")") + } else { + self.logMessage("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + } +} +``` + That's it! With those two changes (which you can try on the included sample), AppAuth will use Chrome iOS for the authorization request (and open Chrome in the App Store if it's not installed). diff --git a/Source/AppAuth.h b/Source/AppAuth.h index 4f779df38..0b68463fb 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -49,7 +49,6 @@ #import "OIDAuthState+IOS.h" #import "OIDAuthorizationService+IOS.h" #import "OIDExternalUserAgentIOS.h" -#import "OIDExternalUserAgentIOSCustomBrowser.h" #import "OIDExternalUserAgentCatalyst.h" #elif TARGET_OS_MAC #import "OIDAuthState+Mac.h" diff --git a/Source/AppAuthEnterpriseUserAgent.h b/Source/AppAuthEnterpriseUserAgent.h new file mode 100644 index 000000000..3bc505418 --- /dev/null +++ b/Source/AppAuthEnterpriseUserAgent.h @@ -0,0 +1,21 @@ +/*! @file AppAuthEnterpriseUserAgent.h + @brief AppAuth iOS SDK + @copyright + Copyright 2020 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 "AppAuthCore.h" + +#import "OIDExternalUserAgentIOSCustomBrowser.h" diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h b/Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.h similarity index 100% rename from Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h rename to Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.h diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m b/Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.m similarity index 100% rename from Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m rename to Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.m diff --git a/Source/EnterpriseUserAgentFramework/AppAuthEnterpriseUserAgent.h b/Source/EnterpriseUserAgentFramework/AppAuthEnterpriseUserAgent.h new file mode 100644 index 000000000..70c152deb --- /dev/null +++ b/Source/EnterpriseUserAgentFramework/AppAuthEnterpriseUserAgent.h @@ -0,0 +1,59 @@ +/*! @file AppAuthEnterpriseUserAgentEnterpriseUserAgent.h + @brief AppAuthEnterpriseUserAgent iOS SDK + @copyright + Copyright 2020 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 + +//! Project version number for AppAuthEnterpriseUserAgentEnterpriseUserAgentFramework. +FOUNDATION_EXPORT double AppAuthEnterpriseUserAgentEnterpriseUserAgentVersionNumber; + +//! Project version string for AppAuthEnterpriseUserAgentEnterpriseUserAgentFramework. +FOUNDATION_EXPORT const unsigned char AppAuthEnterpriseUserAgentEnterpriseUserAgentVersionString[]; + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import "AppAuthEnterpriseUserAgent/OIDExternalUserAgentCatalyst.h" + +#import diff --git a/Source/EnterpriseUserAgentFramework/Info.plist b/Source/EnterpriseUserAgentFramework/Info.plist new file mode 100644 index 000000000..fdd42e8ac --- /dev/null +++ b/Source/EnterpriseUserAgentFramework/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2020 The AppAuth Authors + NSPrincipalClass + + + diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index 4c76aeb2c..d53ed1347 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -57,7 +57,6 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import -#import #import "AppAuth/OIDExternalUserAgentCatalyst.h" #elif TARGET_OS_MAC #import From 80ea9b5b7b670b8d77ff545d50ea894f6afe6c85 Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Fri, 10 Jul 2020 16:13:02 -0400 Subject: [PATCH 116/204] Replace itms-apps with https --- .../iOS/OIDExternalUserAgentIOSCustomBrowser.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.m b/Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.m index 51d2e56cf..be5dc820c 100644 --- a/Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.m +++ b/Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.m @@ -39,7 +39,7 @@ + (instancetype)CustomBrowserChrome { // Chrome iOS documentation: https://developer.chrome.com/multidevice/ios/links OIDCustomBrowserURLTransformation transform = [[self class] URLTransformationSchemeSubstitutionHTTPS:@"googlechromes" HTTP:@"googlechrome"]; NSURL *appStoreURL = - [NSURL URLWithString:@"itms-apps://itunes.apple.com/us/app/chrome/id535886823"]; + [NSURL URLWithString:@"https://itunes.apple.com/us/app/chrome/id535886823"]; return [[[self class] alloc] initWithURLTransformation:transform canOpenURLScheme:@"googlechromes" appStoreURL:appStoreURL]; @@ -50,7 +50,7 @@ + (instancetype)CustomBrowserFirefox { OIDCustomBrowserURLTransformation transform = [[self class] URLTransformationSchemeConcatPrefix:@"firefox://open-url?url="]; NSURL *appStoreURL = - [NSURL URLWithString:@"itms-apps://itunes.apple.com/us/app/firefox-web-browser/id989804926"]; + [NSURL URLWithString:@"https://itunes.apple.com/us/app/firefox-web-browser/id989804926"]; return [[[self class] alloc] initWithURLTransformation:transform canOpenURLScheme:@"firefox" appStoreURL:appStoreURL]; @@ -60,7 +60,7 @@ + (instancetype)CustomBrowserOpera { OIDCustomBrowserURLTransformation transform = [[self class] URLTransformationSchemeSubstitutionHTTPS:@"opera-https" HTTP:@"opera-http"]; NSURL *appStoreURL = - [NSURL URLWithString:@"itms-apps://itunes.apple.com/us/app/opera-mini-web-browser/id363729560"]; + [NSURL URLWithString:@"https://itunes.apple.com/us/app/opera-mini-web-browser/id363729560"]; return [[[self class] alloc] initWithURLTransformation:transform canOpenURLScheme:@"opera-https" appStoreURL:appStoreURL]; From 7232d6efc94f9160360f3345890ed906b873c68c Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Thu, 16 Jul 2020 23:42:11 -0700 Subject: [PATCH 117/204] Exclude unit test files from code coverage analysis. --- .codecov.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .codecov.yml diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 000000000..45de502e0 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,2 @@ +ignore: + - "UnitTests" From 9d04840f4cdb99005dc9f54d054d5526534d1354 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Tue, 2 Jun 2020 15:35:56 -0700 Subject: [PATCH 118/204] Import TV authorization flow from GTMAppAuth - Code is from tree https://github.com/google/GTMAppAuth/tree/10d138eb05ad299fd6260741e7b92b0090db0963 - I contributed this code originally to GTMAppAuth on Dec 6, 2016 https://github.com/google/GTMAppAuth/commit/2eb382fd62c727026e3816db51e6c6da4105ebb5 --- Source/AppAuthTV/GTMAppAuth.h | 21 ++ Source/AppAuthTV/GTMTVAuthorizationRequest.h | 57 ++++ Source/AppAuthTV/GTMTVAuthorizationRequest.m | 125 +++++++++ Source/AppAuthTV/GTMTVAuthorizationResponse.h | 96 +++++++ Source/AppAuthTV/GTMTVAuthorizationResponse.m | 164 +++++++++++ Source/AppAuthTV/GTMTVAuthorizationService.h | 82 ++++++ Source/AppAuthTV/GTMTVAuthorizationService.m | 259 ++++++++++++++++++ Source/AppAuthTV/GTMTVServiceConfiguration.h | 65 +++++ Source/AppAuthTV/GTMTVServiceConfiguration.m | 100 +++++++ 9 files changed, 969 insertions(+) create mode 100644 Source/AppAuthTV/GTMAppAuth.h create mode 100644 Source/AppAuthTV/GTMTVAuthorizationRequest.h create mode 100644 Source/AppAuthTV/GTMTVAuthorizationRequest.m create mode 100644 Source/AppAuthTV/GTMTVAuthorizationResponse.h create mode 100644 Source/AppAuthTV/GTMTVAuthorizationResponse.m create mode 100644 Source/AppAuthTV/GTMTVAuthorizationService.h create mode 100644 Source/AppAuthTV/GTMTVAuthorizationService.m create mode 100644 Source/AppAuthTV/GTMTVServiceConfiguration.h create mode 100644 Source/AppAuthTV/GTMTVServiceConfiguration.m diff --git a/Source/AppAuthTV/GTMAppAuth.h b/Source/AppAuthTV/GTMAppAuth.h new file mode 100644 index 000000000..b018d6fc4 --- /dev/null +++ b/Source/AppAuthTV/GTMAppAuth.h @@ -0,0 +1,21 @@ +/*! @file GTMAppAuth.h + @brief GTMAppAuth SDK + @copyright + Copyright 2016 Google Inc. + @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 "GTMTVAuthorizationRequest.h" +#import "GTMTVAuthorizationResponse.h" +#import "GTMTVAuthorizationService.h" diff --git a/Source/AppAuthTV/GTMTVAuthorizationRequest.h b/Source/AppAuthTV/GTMTVAuthorizationRequest.h new file mode 100644 index 000000000..84334f0a2 --- /dev/null +++ b/Source/AppAuthTV/GTMTVAuthorizationRequest.h @@ -0,0 +1,57 @@ +/*! @file GTMTVAuthorizationRequest.h + @brief GTMAppAuth SDK + @copyright + Copyright 2016 Google Inc. + @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 + +#ifndef GTMAPPAUTH_USER_IMPORTS +#import +#else // GTMAPPAUTH_USER_IMPORTS +#import "AppAuthCore.h" +#endif // GTMAPPAUTH_USER_IMPORTS + +@class GTMTVServiceConfiguration; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Represents a TV and limited input device authorization request. + @see https://developers.google.com/identity/protocols/OAuth2ForDevices + */ +@interface GTMTVAuthorizationRequest : OIDAuthorizationRequest + +/*! @brief Creates a TV authorization request with opinionated defaults + @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 additionalParameters The client's additional authorization parameters. + */ +- (instancetype) + initWithConfiguration:(GTMTVServiceConfiguration *)configuration + clientId:(NSString *)clientID + clientSecret:(NSString *)clientSecret + scopes:(nullable NSArray *)scopes + additionalParameters:(nullable NSDictionary *)additionalParameters; + +/*! @brief Constructs an @c NSURLRequest representing the TV authorization request. + @return An @c NSURLRequest representing the TV authorization request. + */ +- (NSURLRequest *)URLRequest; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthTV/GTMTVAuthorizationRequest.m b/Source/AppAuthTV/GTMTVAuthorizationRequest.m new file mode 100644 index 000000000..7b1e69791 --- /dev/null +++ b/Source/AppAuthTV/GTMTVAuthorizationRequest.m @@ -0,0 +1,125 @@ +/*! @file GTMTVAuthorizationRequest.m + @brief GTMAppAuth SDK + @copyright + Copyright 2016 Google Inc. + @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 "GTMTVAuthorizationRequest.h" + +#ifndef GTMAPPAUTH_USER_IMPORTS +#import +#import +#else // GTMAPPAUTH_USER_IMPORTS +#import "AppAuthCore.h" +#import "OIDURLQueryComponent.h" +#endif // GTMAPPAUTH_USER_IMPORTS +#import "GTMTVServiceConfiguration.h" + +@implementation GTMTVAuthorizationRequest + +- (instancetype) + initWithConfiguration:(GTMTVServiceConfiguration *)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 *)additionalParameters { + + if (![configuration isKindOfClass:[GTMTVServiceConfiguration class]]) { + NSAssert([configuration isKindOfClass:[GTMTVServiceConfiguration class]], + @"configuration parameter must be of type GTMTVServiceConfiguration, encountered %@", + NSStringFromClass([configuration class])); + return nil; + } + + return [super initWithConfiguration:configuration + clientId:clientID + clientSecret:clientSecret + scope:scope + redirectURL:redirectURL + responseType:responseType + state:state + nonce:nonce + codeVerifier:codeVerifier + codeChallenge:codeChallenge + codeChallengeMethod:codeChallengeMethod + additionalParameters:additionalParameters]; +} + +- (instancetype) + initWithConfiguration:(GTMTVServiceConfiguration *)configuration + clientId:(NSString *)clientID + clientSecret:(NSString *)clientSecret + scopes:(nullable NSArray *)scopes + additionalParameters:(nullable NSDictionary *)additionalParameters { + return [self initWithConfiguration:configuration + clientId:clientID + clientSecret:clientSecret + scopes:scopes + redirectURL:[[NSURL alloc] init] + responseType:OIDResponseTypeCode + additionalParameters:additionalParameters]; +} + +#pragma mark - NSObject overrides + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, request: %@>", + NSStringFromClass([self class]), + self, + self.authorizationRequestURL]; +} + +#pragma mark - + +- (NSURLRequest *)URLRequest { + OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init]; + + // Required parameters. + [query addParameter:@"client_id" value:self.clientID]; + + if (self.additionalParameters) { + // Add any additional parameters the client has specified. + [query addParameters:(NSDictionary *)self.additionalParameters]; + } + + if (self.scope) { + [query addParameter:@"scope" value:(NSString *)self.scope]; + } + + static NSString *const kHTTPPost = @"POST"; + static NSString *const kHTTPContentTypeHeaderKey = @"Content-Type"; + static NSString *const kHTTPContentTypeHeaderValue = + @"application/x-www-form-urlencoded; charset=UTF-8"; + + GTMTVServiceConfiguration *tvConfiguration = (GTMTVServiceConfiguration *)self.configuration; + + NSMutableURLRequest *URLRequest = + [[NSURLRequest requestWithURL:tvConfiguration.TVAuthorizationEndpoint] mutableCopy]; + URLRequest.HTTPMethod = kHTTPPost; + [URLRequest setValue:kHTTPContentTypeHeaderValue forHTTPHeaderField:kHTTPContentTypeHeaderKey]; + NSString *bodyString = [query URLEncodedParameters]; + NSData *body = [bodyString dataUsingEncoding:NSUTF8StringEncoding]; + URLRequest.HTTPBody = body; + return URLRequest; +} + +@end diff --git a/Source/AppAuthTV/GTMTVAuthorizationResponse.h b/Source/AppAuthTV/GTMTVAuthorizationResponse.h new file mode 100644 index 000000000..d5987f2a0 --- /dev/null +++ b/Source/AppAuthTV/GTMTVAuthorizationResponse.h @@ -0,0 +1,96 @@ +/*! @file GTMTVAuthorizationResponse.h + @brief GTMAppAuth SDK + @copyright + Copyright 2016 Google Inc. + @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 + +#ifndef GTMAPPAUTH_USER_IMPORTS +#import +#else // GTMAPPAUTH_USER_IMPORTS +#import "AppAuthCore.h" +#endif // GTMAPPAUTH_USER_IMPORTS + +@class GTMTVAuthorizationRequest; +@class OIDTokenRequest; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief The @c grant_type value for the the TV authorization flow. + @see https://developers.google.com/identity/protocols/OAuth2ForDevices + */ +extern NSString *const GTMTVDeviceTokenGrantType; + +/*! @brief Represents the response to a TV authorization request. + @see https://developers.google.com/identity/protocols/OAuth2ForDevices + */ +@interface GTMTVAuthorizationResponse : OIDAuthorizationResponse + +/*! @brief The verification URL that should be displayed to the user instructing them to visit the + URL and enter the code. + @remarks verification_url + */ +@property(nonatomic, readonly, nullable) NSString *verificationURL; + +/*! @brief The code that should be displayed to the user which they enter at the @c verificationURL. + @remarks user_code + */ +@property(nonatomic, readonly, nullable) NSString *userCode; + +/*! @brief The device code grant used to poll the token endpoint. Rather than using this directly, + use the provided @c tokenPollRequest method to create the token request. + @remarks device_code + */ +@property(nonatomic, readonly, nullable) NSString *deviceCode; + +/*! @brief The interval at which the token endpoint should be polled with the @c deviceCode. + @remarks interval + */ +@property(nonatomic, readonly, nullable) NSNumber *interval; + +/*! @brief The date at which the user can no longer authorize this request. + @remarks expires_in + */ +@property(nonatomic, readonly, nullable) NSDate *expirationDate; + +/*! @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:(GTMTVAuthorizationRequest *)request + parameters:(NSDictionary *> *)parameters + NS_DESIGNATED_INITIALIZER; + +/*! @brief Creates a token request suitable for polling the token endpoint with the @c deviceCode. + @return A @c OIDTokenRequest suitable for polling the token endpoint. + @see https://developers.google.com/identity/protocols/OAuth2ForDevices + */ +- (nullable OIDTokenRequest *)tokenPollRequest; + +/*! @brief Creates a token request suitable for polling the token endpoint with the @c deviceCode. + @param additionalParameters Additional parameters for the token request. + @return A @c OIDTokenRequest suitable for polling the token endpoint. + @see https://developers.google.com/identity/protocols/OAuth2ForDevices + */ +- (nullable OIDTokenRequest *)tokenPollRequestWithAdditionalParameters: + (nullable NSDictionary *)additionalParameters; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthTV/GTMTVAuthorizationResponse.m b/Source/AppAuthTV/GTMTVAuthorizationResponse.m new file mode 100644 index 000000000..556d3f282 --- /dev/null +++ b/Source/AppAuthTV/GTMTVAuthorizationResponse.m @@ -0,0 +1,164 @@ +/*! @file GTMTVAuthorizationResponse.m + @brief GTMAppAuth SDK + @copyright + Copyright 2016 Google Inc. + @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 "GTMTVAuthorizationResponse.h" + +#import "GTMTVAuthorizationRequest.h" +#ifndef GTMAPPAUTH_USER_IMPORTS +#import +#import +#import +#else // GTMAPPAUTH_USER_IMPORTS +#import "AppAuthCore.h" +#import "OIDDefines.h" +#import "OIDFieldMapping.h" +#endif // GTMAPPAUTH_USER_IMPORTS + + +NSString *const GTMTVDeviceTokenGrantType = @"http://oauth.net/grant_type/device/1.0"; + +/*! @brief The key for the @c verificationURL property in the incoming parameters and for + @c NSSecureCoding. + */ +static NSString *const kVerificationURLKey = @"verification_url"; + +/*! @brief The key for the @c userCode property in the incoming parameters and for + @c NSSecureCoding. + */ +static NSString *const kUserCodeKey = @"user_code"; + +/*! @brief The key for the @c deviceCode property in the incoming parameters and for + @c NSSecureCoding. + */ +static NSString *const kDeviceCodeKey = @"device_code"; + +/*! @brief The key for the @c expirationDate property in the incoming parameters and for + @c NSSecureCoding. + */ +static NSString *const kExpiresInKey = @"expires_in"; + +/*! @brief The key for the @c interval property in the incoming parameters and for + @c NSSecureCoding. + */ +static NSString *const kIntervalKey = @"interval"; + +/*! @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"; + +@implementation GTMTVAuthorizationResponse + +@synthesize verificationURL = _verificationURL; +@synthesize userCode = _userCode; +@synthesize deviceCode = _deviceCode; +@synthesize interval = _interval; +@synthesize expirationDate = _expirationDate; + +/*! @brief Returns a mapping of incoming parameters to instance variables. + @return A mapping of incoming parameters to instance variables. + */ ++ (NSDictionary *)fieldMap { + static NSMutableDictionary *fieldMap; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + fieldMap = [NSMutableDictionary dictionary]; + fieldMap[kVerificationURLKey] = + [[OIDFieldMapping alloc] initWithName:@"_verificationURL" type:[NSString class]]; + fieldMap[kUserCodeKey] = + [[OIDFieldMapping alloc] initWithName:@"_userCode" type:[NSString class]]; + fieldMap[kDeviceCodeKey] = + [[OIDFieldMapping alloc] initWithName:@"_deviceCode" type:[NSString class]]; + fieldMap[kExpiresInKey] = + [[OIDFieldMapping alloc] initWithName:@"_expirationDate" + 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[kIntervalKey] = + [[OIDFieldMapping alloc] initWithName:@"_interval" type:[NSNumber class]]; + }); + return fieldMap; +} + +#pragma mark - Initializers + +- (instancetype)initWithRequest:(GTMTVAuthorizationRequest *)request + parameters:(NSDictionary *> *)parameters { + self = [super initWithRequest:request parameters:parameters]; + 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 - NSObject overrides + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, verificationURL: %@, userCode: \"%@\", deviceCode: " + "\"%@\", interval: %@, expirationDate: %@, " + "additionalParameters: %@, " + "request: %@>", + NSStringFromClass([self class]), + self, + _verificationURL, + _userCode, + _deviceCode, + _interval, + _expirationDate, + self.additionalParameters, + self.request]; +} + +#pragma mark - + +- (OIDTokenRequest *)tokenPollRequest { + return [self tokenPollRequestWithAdditionalParameters:nil]; +} + +- (OIDTokenRequest *)tokenPollRequestWithAdditionalParameters: + (NSDictionary *)additionalParameters { + OIDTokenRequest *pollRequest = + [[OIDTokenRequest alloc] initWithConfiguration:self.request.configuration + grantType:GTMTVDeviceTokenGrantType + authorizationCode:_deviceCode + redirectURL:[[NSURL alloc] init] + clientID:self.request.clientID + clientSecret:self.request.clientSecret + scopes:nil + refreshToken:nil + codeVerifier:nil + additionalParameters:nil]; + return pollRequest; +} + +@end diff --git a/Source/AppAuthTV/GTMTVAuthorizationService.h b/Source/AppAuthTV/GTMTVAuthorizationService.h new file mode 100644 index 000000000..620372a12 --- /dev/null +++ b/Source/AppAuthTV/GTMTVAuthorizationService.h @@ -0,0 +1,82 @@ +/*! @file GTMTVAuthorizationService.h + @brief GTMAppAuth SDK + @copyright + Copyright 2016 Google Inc. + @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 + +NS_ASSUME_NONNULL_BEGIN + +@class GTMAppAuthFetcherAuthorization; +@class GTMTVAuthorizationRequest; +@class GTMTVAuthorizationResponse; +@class GTMTVServiceConfiguration; + +/*! @brief The block that is called when the TV authorization has initialized. + @param response The authorization response, or nil if there was an error. Display + @c GTMTVAuthorizationResponse.userCode and @c GTMTVAuthorizationResponse.verificationURL to + the user so they can action the request. + @param error The error if an error occurred. + */ +typedef void (^GTMTVAuthorizationInitialization)(GTMTVAuthorizationResponse *_Nullable response, + NSError *_Nullable error); + +/*! @brief The block that is called when the TV authorization has completed. + @param authorization The @c GTMAppAuthFetcherAuthorization which you can use to authorize + API calls, or nil if there was an error. + @param error The error if an error occurred. + */ +typedef void (^GTMTVAuthorizationCompletion) + (GTMAppAuthFetcherAuthorization *_Nullable authorization, + NSError *_Nullable error); + +/*! @brief Block returned when authorization is initialized to that will cancel the pending + authorization when executed. Has no effect if called twice or after the authorization + concluded. + */ +typedef void (^GTMTVAuthorizationCancelBlock)(void); + +/*! @brief Performs authorization flows designed for TVs and other limited input devices. + */ +@interface GTMTVAuthorizationService : NSObject + +#if !GTM_APPAUTH_SKIP_GOOGLE_SUPPORT +/*! @brief Convenience method to return the TV authorization URL for Google. + @return TV authorization URL for Google. + */ ++ (GTMTVServiceConfiguration *)TVConfigurationForGoogle; +#endif // !GTM_APPAUTH_SKIP_GOOGLE_SUPPORT + +/*! @brief Starts a TV authorization flow with the given request and polls for a response. + @param request The TV authorization request to initiate. + @param initialization Block that is called with the initial authorization response. Unlike other + OAuth authorization responses, the TV authorization response doesn't contain the + authorization as the user has yet to grant it. Rather, it contains the information that you + show to the user in order for them to authorize the request on another device. + @param completion Block that is called on the success or failure of the authorization. If the + user approves the request, you will get a @c GTMAppAuthFetherAuthorization that you can use + to authenticate API calls, otherwis eyou will get an error. + @return A block which you can execute if you need to cancel the ongoing authorization. Has no + effect if called twice, or called after the authorization concludes. + @see https://developers.google.com/identity/protocols/OAuth2ForDevices + */ ++ (GTMTVAuthorizationCancelBlock)authorizeTVRequest:(GTMTVAuthorizationRequest *)request + initializaiton:(GTMTVAuthorizationInitialization)initialization + completion:(GTMTVAuthorizationCompletion)completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthTV/GTMTVAuthorizationService.m b/Source/AppAuthTV/GTMTVAuthorizationService.m new file mode 100644 index 000000000..64957f578 --- /dev/null +++ b/Source/AppAuthTV/GTMTVAuthorizationService.m @@ -0,0 +1,259 @@ +/*! @file GTMAppAuthFetcherAuthorization.m + @brief GTMAppAuth SDK + @copyright + Copyright 2016 Google Inc. + @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 "GTMTVAuthorizationService.h" + +#ifndef GTMAPPAUTH_USER_IMPORTS +#import +#import +#import +#else // GTMAPPAUTH_USER_IMPORTS +#import "AppAuthCore.h" +#import "OIDDefines.h" +#import "OIDURLQueryComponent.h" +#endif // GTMAPPAUTH_USER_IMPORTS + +#import "GTMAppAuthFetcherAuthorization.h" +#import "GTMTVAuthorizationRequest.h" +#import "GTMTVAuthorizationResponse.h" +#import "GTMTVServiceConfiguration.h" + +/*! @brief Google's device authorization endpoint. + */ +NSString *const kGoogleDeviceAuthorizationEndpoint = + @"https://accounts.google.com/o/oauth2/device/code"; + +/*! @brief The authorization pending error code. + @see https://developers.google.com/identity/protocols/OAuth2ForDevices + */ +NSString *const kErrorCodeAuthorizationPending = @"authorization_pending"; + +/*! @brief The slow down error code. + @see https://developers.google.com/identity/protocols/OAuth2ForDevices + */ +NSString *const kErrorCodeSlowDown = @"slow_down"; + +@implementation GTMTVAuthorizationService + +#pragma mark - Initializers + +#if !GTM_APPAUTH_SKIP_GOOGLE_SUPPORT ++ (GTMTVServiceConfiguration *)TVConfigurationForGoogle { + NSURL *authorizationEndpoint = + [NSURL URLWithString:@"https://accounts.google.com/o/oauth2/v2/auth"]; + NSURL *tokenEndpoint = + [NSURL URLWithString:@"https://www.googleapis.com/oauth2/v4/token"]; + NSURL *TVAuthorizationEndpoint = + [NSURL URLWithString:kGoogleDeviceAuthorizationEndpoint]; + + GTMTVServiceConfiguration *configuration = + [[GTMTVServiceConfiguration alloc] initWithAuthorizationEndpoint:authorizationEndpoint + TVAuthorizationEndpoint:TVAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; + return configuration; +} +#endif // !GTM_APPAUTH_SKIP_GOOGLE_SUPPORT + ++ (GTMTVAuthorizationCancelBlock)authorizeTVRequest:(GTMTVAuthorizationRequest *)request + initializaiton:(GTMTVAuthorizationInitialization)initialization + completion:(GTMTVAuthorizationCompletion)completion { + // Block level variable that can be used to cancel the polling. + __block BOOL pollRunning = YES; + + // Block that will be returned allowign the caller to cancel the polling. + GTMTVAuthorizationCancelBlock cancelBlock = ^{ + if (pollRunning) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSError *cancelError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeProgramCanceledAuthorizationFlow + underlyingError:nil + description:@"Authorization cancelled"]; + completion(nil, cancelError); + }); + } + pollRunning = NO; + }; + + // Performs the initial authorization reqeust. + NSURLRequest *URLRequest = [request URLRequest]; + NSURLSession *session = [NSURLSession sharedSession]; + [[session dataTaskWithRequest:URLRequest + completionHandler:^(NSData *_Nullable data, + NSURLResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + // A network error or server error occurred. + NSError *returnedError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeNetworkError + underlyingError:error + description:nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + initialization(nil, returnedError); + }); + return; + } + + NSHTTPURLResponse *HTTPURLResponse = (NSHTTPURLResponse *)response; + + if (HTTPURLResponse.statusCode != 200) { + // A server error occurred. + NSError *serverError = + [OIDErrorUtilities HTTPErrorWithHTTPResponse:HTTPURLResponse data:data]; + + // HTTP 400 may indicate an RFC6749 Section 5.2 error response, checks for that + if (HTTPURLResponse.statusCode == 400) { + NSError *jsonDeserializationError; + NSDictionary *> *json = + [NSJSONSerialization JSONObjectWithData:(NSData *)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:OIDOAuthTokenErrorDomain + OAuthResponse:json + underlyingError:serverError]; + dispatch_async(dispatch_get_main_queue(), ^{ + initialization(nil, oauthError); + }); + return; + } + } + + // not an OAuth error, just a generic server error + NSError *returnedError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeServerError + underlyingError:serverError + description:nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + initialization(nil, returnedError); + }); + return; + } + + NSError *jsonDeserializationError; + NSDictionary *> *json = + [NSJSONSerialization JSONObjectWithData:(NSData *)data + options:0 + error:&jsonDeserializationError]; + if (jsonDeserializationError) { + // A problem occurred deserializing the response/JSON. + NSError *returnedError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeJSONDeserializationError + underlyingError:jsonDeserializationError + description:nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + initialization(nil, returnedError); + }); + return; + } + + // Parses the authorization response. + GTMTVAuthorizationResponse *TVAuthorizationResponse = + [[GTMTVAuthorizationResponse alloc] initWithRequest:request parameters:json]; + if (!TVAuthorizationResponse) { + // A problem occurred constructing the token response from the JSON. + NSError *returnedError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeTokenResponseConstructionError + underlyingError:jsonDeserializationError + description:nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + initialization(nil, returnedError); + }); + return; + } + + // Calls the initialization block to signal that we received a TV authorization response. + dispatch_async(dispatch_get_main_queue(), ^() { + initialization(TVAuthorizationResponse, nil); + }); + + // Creates the token request that will be used to poll the token endpoint. + OIDTokenRequest *pollRequest = [TVAuthorizationResponse tokenPollRequest]; + + // Starting polling interval (may be increased if a slow down message is received). + __block NSTimeInterval interval = [TVAuthorizationResponse.interval doubleValue]; + + // Polls the token endpoint until the authorization completes or expires. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + do { + // Sleeps for polling interval. + [NSThread sleepForTimeInterval:interval]; + + if (!pollRunning) { + break; + } + + // Polls token endpoint. + [OIDAuthorizationService performTokenRequest:pollRequest + callback:^(OIDTokenResponse *_Nullable tokenResponse, + NSError *_Nullable tokenError) { + if (!pollRunning) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^() { + if (tokenResponse) { + // Success response. + pollRunning = NO; + dispatch_async(dispatch_get_main_queue(), ^{ + OIDAuthState *authState = + [[OIDAuthState alloc] initWithAuthorizationResponse:TVAuthorizationResponse + tokenResponse:tokenResponse]; + GTMAppAuthFetcherAuthorization *authorization = + [[GTMAppAuthFetcherAuthorization alloc] initWithAuthState:authState]; + completion(authorization, nil); + }); + } else { + if (tokenError.domain == OIDOAuthTokenErrorDomain) { + // OAuth token errors inspected for device flow specific errors. + NSString *errorCode = + tokenError.userInfo[OIDOAuthErrorResponseErrorKey][OIDOAuthErrorFieldError]; + if ([errorCode isEqual:kErrorCodeAuthorizationPending]) { + // authorization_pending is an expected response. + return; + } else if ([errorCode isEqual:kErrorCodeSlowDown]) { + // Increase interval by 20%, enforce a lower bound of 5s. + interval *= 1.20; + interval = MAX(5.0, interval); + } else { + // Unhandled token error, considered fatal. + pollRunning = NO; + dispatch_async(dispatch_get_main_queue(), ^{ + completion(nil, tokenError); + }); + } + } else { + // All other errors considered fatal. + pollRunning = NO; + dispatch_async(dispatch_get_main_queue(), ^{ + completion(nil, tokenError); + }); + } + } + }); + }]; + } while ([TVAuthorizationResponse.expirationDate timeIntervalSinceNow] > 0 && pollRunning); + }); + }] resume]; + + return cancelBlock; +} + +@end diff --git a/Source/AppAuthTV/GTMTVServiceConfiguration.h b/Source/AppAuthTV/GTMTVServiceConfiguration.h new file mode 100644 index 000000000..70e748241 --- /dev/null +++ b/Source/AppAuthTV/GTMTVServiceConfiguration.h @@ -0,0 +1,65 @@ +/*! @file GTMTVServiceConfiguration.h + @brief GTMAppAuth SDK + @copyright + Copyright 2016 Google Inc. + @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. + */ + +#ifndef GTMAPPAUTH_USER_IMPORTS +#import +#else // GTMAPPAUTH_USER_IMPORTS +#import "AppAuthCore.h" +#endif // GTMAPPAUTH_USER_IMPORTS + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Configuration for authorizing the user with the @c GTMTVAuthorizationService. + */ +@interface GTMTVServiceConfiguration : OIDServiceConfiguration + +/*! @brief The TV authorization endpoint URI. + */ +@property(nonatomic, readonly) NSURL *TVAuthorizationEndpoint; + +/*! @internal + @brief Unavailable. Please use + @c initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint: + */ +- (instancetype)init NS_UNAVAILABLE; + +/*! @internal + @brief Unavailable. Please use + @c initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint: + */ +- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint NS_UNAVAILABLE; + +/*! @internal + @brief Unavailable. Please use + @c initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint: + */ +- (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *)discoveryDocument NS_UNAVAILABLE; + +/*! @brief Designated initializer. + @param authorizationEndpoint The authorization endpoint URI. + @param TVAuthorizationEndpoint The TV authorization endpoint URI. + @param tokenEndpoint The token exchange and refresh endpoint URI. + */ +- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint + TVAuthorizationEndpoint:(NSURL *)TVAuthorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthTV/GTMTVServiceConfiguration.m b/Source/AppAuthTV/GTMTVServiceConfiguration.m new file mode 100644 index 000000000..8d7fabdf4 --- /dev/null +++ b/Source/AppAuthTV/GTMTVServiceConfiguration.m @@ -0,0 +1,100 @@ +/*! @file GTMTVServiceConfiguration.m + @brief GTMAppAuth SDK + @copyright + Copyright 2016 Google Inc. + @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 "GTMTVServiceConfiguration.h" + +#ifndef GTMAPPAUTH_USER_IMPORTS +#import +#import +#else // GTMAPPAUTH_USER_IMPORTS +#import "AppAuthCore.h" +#import "OIDDefines.h" +#endif // GTMAPPAUTH_USER_IMPORTS + +/*! @brief The key for the @c TVAuthorizationEndpoint property. + */ +static NSString *const kTVAuthorizationEndpointKey = @"TVAuthorizationEndpoint"; + +NS_ASSUME_NONNULL_BEGIN + +@interface GTMTVServiceConfiguration () + +/*! @brief Designated initializer. + @param aDecoder NSCoder to unserialize the object from. + */ +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; + +@end + +@implementation GTMTVServiceConfiguration + +@synthesize TVAuthorizationEndpoint = _TVAuthorizationEndpoint; + +- (instancetype)init + OID_UNAVAILABLE_USE_INITIALIZER( + @selector(initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint:)); + +- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint + OID_UNAVAILABLE_USE_INITIALIZER( + @selector(initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint:)); + +- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint + TVAuthorizationEndpoint:(NSURL *)TVAuthorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint { + self = [super initWithAuthorizationEndpoint:authorizationEndpoint tokenEndpoint:tokenEndpoint]; + if (self) { + _TVAuthorizationEndpoint = [TVAuthorizationEndpoint copy]; + } + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + NSURL *TVAuthorizationEndpoint = [aDecoder decodeObjectOfClass:[NSURL class] + forKey:kTVAuthorizationEndpointKey]; + _TVAuthorizationEndpoint = TVAuthorizationEndpoint; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [super encodeWithCoder:aCoder]; + [aCoder encodeObject:_TVAuthorizationEndpoint forKey:kTVAuthorizationEndpointKey]; +} + +#pragma mark - description + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, TVAuthorizationEndpoint: %@ tokenEndpoint: %@>", + NSStringFromClass([self class]), + self, + _TVAuthorizationEndpoint, + self.tokenEndpoint]; +} + +@end + +NS_ASSUME_NONNULL_END From da0be7a354d13c7330aa28d7dafd00dd79a8ccee Mon Sep 17 00:00:00 2001 From: William Denniss Date: Tue, 2 Jun 2020 16:10:03 -0700 Subject: [PATCH 119/204] Modify TV authorization logic for AppAuth - Add beginnings of 'AppAuthTV' Pod subspec - Remove dependency on `GTMAppAuthFetcherAuthorization` - Remove GTMAPPAUTH_USER_IMPORTS code - Verified with `pod lib lint` --- AppAuth.podspec | 8 ++++++- Source/AppAuthTV/AppAuthTV.h | 22 +++++++++++++++++++ Source/AppAuthTV/GTMTVAuthorizationRequest.h | 4 ---- Source/AppAuthTV/GTMTVAuthorizationRequest.m | 5 ----- Source/AppAuthTV/GTMTVAuthorizationResponse.h | 4 ---- Source/AppAuthTV/GTMTVAuthorizationResponse.m | 6 ----- Source/AppAuthTV/GTMTVAuthorizationService.h | 6 ++--- Source/AppAuthTV/GTMTVAuthorizationService.m | 13 ++--------- Source/AppAuthTV/GTMTVServiceConfiguration.h | 4 ---- Source/AppAuthTV/GTMTVServiceConfiguration.m | 5 ----- 10 files changed, 34 insertions(+), 43 deletions(-) create mode 100644 Source/AppAuthTV/AppAuthTV.h diff --git a/AppAuth.podspec b/AppAuth.podspec index b34dbe3f1..c34fc79f3 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -67,6 +67,12 @@ It follows the OAuth 2.0 for Native Apps best current practice enterpriseUserAgent.ios.source_files = "Source/AppAuthEnterpriseUserAgent.h", "Source/AppAuthEnterpriseUserAgent/iOS/**/*.{h,m}" enterpriseUserAgent.ios.deployment_target = "7.0" end - + + # Subspec for the full AppAuth library, including platform-dependant external user agents. + s.subspec 'TV' do |tv| + tv.source_files = "Source/AppAuthTV/*.{h,m}" + tv.dependency 'AppAuth/Core' + end + s.default_subspecs = 'Core', 'ExternalUserAgent' end diff --git a/Source/AppAuthTV/AppAuthTV.h b/Source/AppAuthTV/AppAuthTV.h new file mode 100644 index 000000000..32a6d8581 --- /dev/null +++ b/Source/AppAuthTV/AppAuthTV.h @@ -0,0 +1,22 @@ +/*! @file AppAuthTV.h + @brief GTMAppAuth SDK + @copyright + Copyright 2016 Google Inc. + @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 "GTMTVAuthorizationRequest.h" +#import "GTMTVAuthorizationResponse.h" +#import "GTMTVAuthorizationService.h" +#import "GTMTVServiceConfiguration.h" \ No newline at end of file diff --git a/Source/AppAuthTV/GTMTVAuthorizationRequest.h b/Source/AppAuthTV/GTMTVAuthorizationRequest.h index 84334f0a2..032c05488 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationRequest.h +++ b/Source/AppAuthTV/GTMTVAuthorizationRequest.h @@ -18,11 +18,7 @@ #import -#ifndef GTMAPPAUTH_USER_IMPORTS -#import -#else // GTMAPPAUTH_USER_IMPORTS #import "AppAuthCore.h" -#endif // GTMAPPAUTH_USER_IMPORTS @class GTMTVServiceConfiguration; diff --git a/Source/AppAuthTV/GTMTVAuthorizationRequest.m b/Source/AppAuthTV/GTMTVAuthorizationRequest.m index 7b1e69791..f47d80aac 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationRequest.m +++ b/Source/AppAuthTV/GTMTVAuthorizationRequest.m @@ -18,13 +18,8 @@ #import "GTMTVAuthorizationRequest.h" -#ifndef GTMAPPAUTH_USER_IMPORTS -#import -#import -#else // GTMAPPAUTH_USER_IMPORTS #import "AppAuthCore.h" #import "OIDURLQueryComponent.h" -#endif // GTMAPPAUTH_USER_IMPORTS #import "GTMTVServiceConfiguration.h" @implementation GTMTVAuthorizationRequest diff --git a/Source/AppAuthTV/GTMTVAuthorizationResponse.h b/Source/AppAuthTV/GTMTVAuthorizationResponse.h index d5987f2a0..ce1365f4f 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationResponse.h +++ b/Source/AppAuthTV/GTMTVAuthorizationResponse.h @@ -18,11 +18,7 @@ #import -#ifndef GTMAPPAUTH_USER_IMPORTS -#import -#else // GTMAPPAUTH_USER_IMPORTS #import "AppAuthCore.h" -#endif // GTMAPPAUTH_USER_IMPORTS @class GTMTVAuthorizationRequest; @class OIDTokenRequest; diff --git a/Source/AppAuthTV/GTMTVAuthorizationResponse.m b/Source/AppAuthTV/GTMTVAuthorizationResponse.m index 556d3f282..bee285fc4 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationResponse.m +++ b/Source/AppAuthTV/GTMTVAuthorizationResponse.m @@ -19,15 +19,9 @@ #import "GTMTVAuthorizationResponse.h" #import "GTMTVAuthorizationRequest.h" -#ifndef GTMAPPAUTH_USER_IMPORTS -#import -#import -#import -#else // GTMAPPAUTH_USER_IMPORTS #import "AppAuthCore.h" #import "OIDDefines.h" #import "OIDFieldMapping.h" -#endif // GTMAPPAUTH_USER_IMPORTS NSString *const GTMTVDeviceTokenGrantType = @"http://oauth.net/grant_type/device/1.0"; diff --git a/Source/AppAuthTV/GTMTVAuthorizationService.h b/Source/AppAuthTV/GTMTVAuthorizationService.h index 620372a12..a37d4aa4e 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationService.h +++ b/Source/AppAuthTV/GTMTVAuthorizationService.h @@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN -@class GTMAppAuthFetcherAuthorization; +@class OIDAuthState; @class GTMTVAuthorizationRequest; @class GTMTVAuthorizationResponse; @class GTMTVServiceConfiguration; @@ -35,12 +35,12 @@ typedef void (^GTMTVAuthorizationInitialization)(GTMTVAuthorizationResponse *_Nu NSError *_Nullable error); /*! @brief The block that is called when the TV authorization has completed. - @param authorization The @c GTMAppAuthFetcherAuthorization which you can use to authorize + @param authorization The @c OIDAuthState which you can use to authorize API calls, or nil if there was an error. @param error The error if an error occurred. */ typedef void (^GTMTVAuthorizationCompletion) - (GTMAppAuthFetcherAuthorization *_Nullable authorization, + (OIDAuthState *_Nullable authorization, NSError *_Nullable error); /*! @brief Block returned when authorization is initialized to that will cancel the pending diff --git a/Source/AppAuthTV/GTMTVAuthorizationService.m b/Source/AppAuthTV/GTMTVAuthorizationService.m index 64957f578..5160d0305 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationService.m +++ b/Source/AppAuthTV/GTMTVAuthorizationService.m @@ -1,4 +1,4 @@ -/*! @file GTMAppAuthFetcherAuthorization.m +/*! @file GTMTVAuthorizationService.m @brief GTMAppAuth SDK @copyright Copyright 2016 Google Inc. @@ -18,17 +18,10 @@ #import "GTMTVAuthorizationService.h" -#ifndef GTMAPPAUTH_USER_IMPORTS -#import -#import -#import -#else // GTMAPPAUTH_USER_IMPORTS #import "AppAuthCore.h" #import "OIDDefines.h" #import "OIDURLQueryComponent.h" -#endif // GTMAPPAUTH_USER_IMPORTS -#import "GTMAppAuthFetcherAuthorization.h" #import "GTMTVAuthorizationRequest.h" #import "GTMTVAuthorizationResponse.h" #import "GTMTVServiceConfiguration.h" @@ -216,9 +209,7 @@ + (GTMTVAuthorizationCancelBlock)authorizeTVRequest:(GTMTVAuthorizationRequest * OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:TVAuthorizationResponse tokenResponse:tokenResponse]; - GTMAppAuthFetcherAuthorization *authorization = - [[GTMAppAuthFetcherAuthorization alloc] initWithAuthState:authState]; - completion(authorization, nil); + completion(authState, nil); }); } else { if (tokenError.domain == OIDOAuthTokenErrorDomain) { diff --git a/Source/AppAuthTV/GTMTVServiceConfiguration.h b/Source/AppAuthTV/GTMTVServiceConfiguration.h index 70e748241..7fb6babfd 100644 --- a/Source/AppAuthTV/GTMTVServiceConfiguration.h +++ b/Source/AppAuthTV/GTMTVServiceConfiguration.h @@ -16,11 +16,7 @@ limitations under the License. */ -#ifndef GTMAPPAUTH_USER_IMPORTS -#import -#else // GTMAPPAUTH_USER_IMPORTS #import "AppAuthCore.h" -#endif // GTMAPPAUTH_USER_IMPORTS NS_ASSUME_NONNULL_BEGIN diff --git a/Source/AppAuthTV/GTMTVServiceConfiguration.m b/Source/AppAuthTV/GTMTVServiceConfiguration.m index 8d7fabdf4..e6493292c 100644 --- a/Source/AppAuthTV/GTMTVServiceConfiguration.m +++ b/Source/AppAuthTV/GTMTVServiceConfiguration.m @@ -18,13 +18,8 @@ #import "GTMTVServiceConfiguration.h" -#ifndef GTMAPPAUTH_USER_IMPORTS -#import -#import -#else // GTMAPPAUTH_USER_IMPORTS #import "AppAuthCore.h" #import "OIDDefines.h" -#endif // GTMAPPAUTH_USER_IMPORTS /*! @brief The key for the @c TVAuthorizationEndpoint property. */ From 72d874bb3059e20960b6aca39727285620e6f76f Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Wed, 15 Jul 2020 19:48:41 -0400 Subject: [PATCH 120/204] Rename GTM to OID, remove extra headers * Rename OID to GTM, remove extra header * Remove TVConfigurationForGoogle, update copyright headers * Return OIDAuthState instead of GTMAppAuthFetcherAuthorization * Add AppAuthTV to Xcode targets, fix NSObject description method bug * Add files to AppAuthTV target * Remove property synthesize * Add AppAuthTVTests target, pointing to UnitTests/UnitTestsInfo.plist and included in AppAuthTV scheme. Reorder imports * Update min tvOS version and identifier for AppAuthTV target * Correct capitalization in AppAuthTV identifier * Update reference URLs, references to Google removed * Remove non-functioning AppAuthTVTests target (tests coming in future PR) * Fix indent spacing in OIDTVServiceConfiguration --- AppAuth.xcodeproj/project.pbxproj | 294 ++++++++++++++++++ .../xcshareddata/xcschemes/AppAuthTV.xcscheme | 81 +++++ Package.swift | 11 +- Source/{AppAuthTV => }/AppAuthTV.h | 12 +- Source/AppAuthTV/GTMAppAuth.h | 21 -- ...nRequest.h => OIDTVAuthorizationRequest.h} | 14 +- ...nRequest.m => OIDTVAuthorizationRequest.m} | 27 +- ...esponse.h => OIDTVAuthorizationResponse.h} | 22 +- ...esponse.m => OIDTVAuthorizationResponse.m} | 26 +- ...nService.h => OIDTVAuthorizationService.h} | 39 +-- ...nService.m => OIDTVAuthorizationService.m} | 56 ++-- ...guration.h => OIDTVServiceConfiguration.h} | 10 +- ...guration.m => OIDTVServiceConfiguration.m} | 21 +- Source/TVFramework/AppAuthTV.h | 57 ++++ Source/TVFramework/Info.plist | 22 ++ 15 files changed, 558 insertions(+), 155 deletions(-) create mode 100644 AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthTV.xcscheme rename Source/{AppAuthTV => }/AppAuthTV.h (75%) delete mode 100644 Source/AppAuthTV/GTMAppAuth.h rename Source/AppAuthTV/{GTMTVAuthorizationRequest.h => OIDTVAuthorizationRequest.h} (84%) rename Source/AppAuthTV/{GTMTVAuthorizationRequest.m => OIDTVAuthorizationRequest.m} (86%) rename Source/AppAuthTV/{GTMTVAuthorizationResponse.h => OIDTVAuthorizationResponse.h} (83%) rename Source/AppAuthTV/{GTMTVAuthorizationResponse.m => OIDTVAuthorizationResponse.m} (89%) rename Source/AppAuthTV/{GTMTVAuthorizationService.h => OIDTVAuthorizationService.h} (69%) rename Source/AppAuthTV/{GTMTVAuthorizationService.m => OIDTVAuthorizationService.m} (82%) rename Source/AppAuthTV/{GTMTVServiceConfiguration.h => OIDTVServiceConfiguration.h} (88%) rename Source/AppAuthTV/{GTMTVServiceConfiguration.m => OIDTVServiceConfiguration.m} (86%) create mode 100644 Source/TVFramework/AppAuthTV.h create mode 100644 Source/TVFramework/Info.plist diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 058bb712b..11b222444 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -14,6 +14,10 @@ 06C19E9C22B474A600C19CE1 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 06C19E9D22B474AD00C19CE1 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; 2D0BB86C249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D0BB86B249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D47AAE1249A87020059B5A4 /* OIDTVAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AAD8249A87010059B5A4 /* OIDTVAuthorizationResponse.m */; }; + 2D47AAE4249A87020059B5A4 /* OIDTVServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */; }; + 2D47AAE8249A87020059B5A4 /* OIDTVAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADD249A87010059B5A4 /* OIDTVAuthorizationRequest.m */; }; + 2D47AAEC249A87020059B5A4 /* OIDTVAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AAE0249A87020059B5A4 /* OIDTVAuthorizationService.m */; }; 2D91B81F249053190005B197 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; 2D91B820249053190005B197 /* OIDExternalUserAgentCatalyst.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A7082D2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m */; }; 2D91B821249053190005B197 /* OIDFieldMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C41C5D8243000EF209 /* OIDFieldMapping.m */; }; @@ -74,6 +78,65 @@ 2D91B85B249053190005B197 /* OIDTokenUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D51C5D8243000EF209 /* OIDTokenUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2D91B85C249053190005B197 /* OIDError.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BF1C5D8243000EF209 /* OIDError.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2D91B85D249053190005B197 /* OIDExternalUserAgentIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D9385DE24B3861E009A12D7 /* AppAuthTV.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D9385B924B37CAD009A12D7 /* AppAuthTV.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D9385DF24B38646009A12D7 /* OIDTVAuthorizationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D47AAD9249A87010059B5A4 /* OIDTVAuthorizationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D9385E024B38658009A12D7 /* OIDTVAuthorizationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D47AADC249A87010059B5A4 /* OIDTVAuthorizationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D9385E124B3865E009A12D7 /* OIDTVAuthorizationService.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D47AADF249A87020059B5A4 /* OIDTVAuthorizationService.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D9385E224B38669009A12D7 /* OIDTVServiceConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D47AADE249A87020059B5A4 /* OIDTVServiceConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93861924B38803009A12D7 /* OIDAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */; }; + 2D93861A24B3880B009A12D7 /* OIDAuthorizationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93861B24B38810009A12D7 /* OIDAuthorizationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93861C24B38812009A12D7 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; + 2D93861D24B38815009A12D7 /* OIDAuthorizationService.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B81C5D8243000EF209 /* OIDAuthorizationService.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93861E24B3881B009A12D7 /* OIDAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B91C5D8243000EF209 /* OIDAuthorizationService.m */; }; + 2D93861F24B3881B009A12D7 /* OIDAuthState.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BA1C5D8243000EF209 /* OIDAuthState.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93862024B3881B009A12D7 /* OIDAuthState.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741BB1C5D8243000EF209 /* OIDAuthState.m */; }; + 2D93862124B3881B009A12D7 /* OIDAuthStateChangeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BC1C5D8243000EF209 /* OIDAuthStateChangeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93862224B3881C009A12D7 /* OIDAuthStateErrorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BD1C5D8243000EF209 /* OIDAuthStateErrorDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93862424B3881C009A12D7 /* OIDClientMetadataParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F791DE4276800DA0DC3 /* OIDClientMetadataParameters.m */; }; + 2D93862624B3881C009A12D7 /* OIDError.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BF1C5D8243000EF209 /* OIDError.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93862724B3881C009A12D7 /* OIDError.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C01C5D8243000EF209 /* OIDError.m */; }; + 2D93862824B3881C009A12D7 /* OIDErrorUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C11C5D8243000EF209 /* OIDErrorUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93862924B3881C009A12D7 /* OIDErrorUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C21C5D8243000EF209 /* OIDErrorUtilities.m */; }; + 2D93862A24B3881C009A12D7 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93862B24B38825009A12D7 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93862C24B38826009A12D7 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93862E24B38826009A12D7 /* OIDFieldMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C41C5D8243000EF209 /* OIDFieldMapping.m */; }; + 2D93862F24B38826009A12D7 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93863024B38826009A12D7 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + 2D93863124B38826009A12D7 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93863224B38826009A12D7 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + 2D93863324B38826009A12D7 /* OIDRegistrationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 60140F7E1DE4335200DA0DC3 /* OIDRegistrationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93863424B38826009A12D7 /* OIDRegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */; }; + 2D93863524B38827009A12D7 /* OIDRegistrationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 60140F7D1DE42E3000DA0DC3 /* OIDRegistrationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93863624B38827009A12D7 /* OIDRegistrationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7B1DE42E1000DA0DC3 /* OIDRegistrationRequest.m */; }; + 2D93863724B38827009A12D7 /* OIDGrantTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C51C5D8243000EF209 /* OIDGrantTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93863824B38827009A12D7 /* OIDGrantTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C61C5D8243000EF209 /* OIDGrantTypes.m */; }; + 2D93863924B38827009A12D7 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93863A24B38827009A12D7 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 2D93863B24B38827009A12D7 /* OIDResponseTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C71C5D8243000EF209 /* OIDResponseTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93863C24B38827009A12D7 /* OIDResponseTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C81C5D8243000EF209 /* OIDResponseTypes.m */; }; + 2D93863D24B38827009A12D7 /* OIDScopes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C91C5D8243000EF209 /* OIDScopes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93863E24B38827009A12D7 /* OIDScopes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CA1C5D8243000EF209 /* OIDScopes.m */; }; + 2D93863F24B38828009A12D7 /* OIDScopeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CB1C5D8243000EF209 /* OIDScopeUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93864024B38828009A12D7 /* OIDScopeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CC1C5D8243000EF209 /* OIDScopeUtilities.m */; }; + 2D93864124B38828009A12D7 /* OIDServiceConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CD1C5D8243000EF209 /* OIDServiceConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93864224B38828009A12D7 /* OIDServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CE1C5D8243000EF209 /* OIDServiceConfiguration.m */; }; + 2D93864324B38828009A12D7 /* OIDServiceDiscovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CF1C5D8243000EF209 /* OIDServiceDiscovery.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93864424B38828009A12D7 /* OIDServiceDiscovery.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D01C5D8243000EF209 /* OIDServiceDiscovery.m */; }; + 2D93864524B38828009A12D7 /* OIDTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D11C5D8243000EF209 /* OIDTokenRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93864624B38828009A12D7 /* OIDTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D21C5D8243000EF209 /* OIDTokenRequest.m */; }; + 2D93864724B38828009A12D7 /* OIDTokenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D31C5D8243000EF209 /* OIDTokenResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93864824B38828009A12D7 /* OIDTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D41C5D8243000EF209 /* OIDTokenResponse.m */; }; + 2D93864924B38828009A12D7 /* OIDTokenUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D51C5D8243000EF209 /* OIDTokenUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93864A24B38829009A12D7 /* OIDTokenUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D61C5D8243000EF209 /* OIDTokenUtilities.m */; }; + 2D93864C24B38829009A12D7 /* OIDURLQueryComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D81C5D8243000EF209 /* OIDURLQueryComponent.m */; }; + 2D93864D24B38829009A12D7 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D93864E24B38829009A12D7 /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; + 2D93864F24B38840009A12D7 /* OIDTVAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADD249A87010059B5A4 /* OIDTVAuthorizationRequest.m */; }; + 2D93865024B38840009A12D7 /* OIDTVAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AAD8249A87010059B5A4 /* OIDTVAuthorizationResponse.m */; }; + 2D93865124B38840009A12D7 /* OIDTVAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AAE0249A87020059B5A4 /* OIDTVAuthorizationService.m */; }; + 2D93865224B38840009A12D7 /* OIDTVServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */; }; 340DAE571D5821A100EC285B /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; 340DAE581D5821A100EC285B /* OIDExternalUserAgentMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentMac.m */; }; 340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */; }; @@ -665,8 +728,20 @@ 0396974C1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDURLSessionProviderTests.m; sourceTree = ""; }; 2D0BB86A249D5B75005BA653 /* AppAuthEnterpriseUserAgent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppAuthEnterpriseUserAgent.h; sourceTree = ""; }; 2D0BB86B249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAuthEnterpriseUserAgent.h; sourceTree = ""; }; + 2D47AAD8249A87010059B5A4 /* OIDTVAuthorizationResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVAuthorizationResponse.m; sourceTree = ""; }; + 2D47AAD9249A87010059B5A4 /* OIDTVAuthorizationRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVAuthorizationRequest.h; sourceTree = ""; }; + 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVServiceConfiguration.m; sourceTree = ""; }; + 2D47AADB249A87010059B5A4 /* AppAuthTV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAuthTV.h; sourceTree = ""; }; + 2D47AADC249A87010059B5A4 /* OIDTVAuthorizationResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVAuthorizationResponse.h; sourceTree = ""; }; + 2D47AADD249A87010059B5A4 /* OIDTVAuthorizationRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVAuthorizationRequest.m; sourceTree = ""; }; + 2D47AADE249A87020059B5A4 /* OIDTVServiceConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVServiceConfiguration.h; sourceTree = ""; }; + 2D47AADF249A87020059B5A4 /* OIDTVAuthorizationService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVAuthorizationService.h; sourceTree = ""; }; + 2D47AAE0249A87020059B5A4 /* OIDTVAuthorizationService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVAuthorizationService.m; sourceTree = ""; }; 2D91B862249053190005B197 /* AppAuthEnterpriseUserAgent.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuthEnterpriseUserAgent.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2D91B868249177180005B197 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 2D9385B724B37CAD009A12D7 /* AppAuthTV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuthTV.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D9385B924B37CAD009A12D7 /* AppAuthTV.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppAuthTV.h; sourceTree = ""; }; + 2D9385BA24B37CAD009A12D7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 340DAE251D581FE700EC285B /* OIDAuthorizationService+Mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OIDAuthorizationService+Mac.h"; sourceTree = ""; }; 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OIDAuthorizationService+Mac.m"; sourceTree = ""; }; 340DAE271D581FE700EC285B /* OIDExternalUserAgentMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentMac.h; sourceTree = ""; }; @@ -806,6 +881,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2D9385B424B37CAD009A12D7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 340DAE4B1D58216A00EC285B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -948,6 +1030,21 @@ path = iOS; sourceTree = ""; }; + 2D47AAD7249A86E30059B5A4 /* AppAuthTV */ = { + isa = PBXGroup; + children = ( + 2D47AAD9249A87010059B5A4 /* OIDTVAuthorizationRequest.h */, + 2D47AADD249A87010059B5A4 /* OIDTVAuthorizationRequest.m */, + 2D47AADC249A87010059B5A4 /* OIDTVAuthorizationResponse.h */, + 2D47AAD8249A87010059B5A4 /* OIDTVAuthorizationResponse.m */, + 2D47AADF249A87020059B5A4 /* OIDTVAuthorizationService.h */, + 2D47AAE0249A87020059B5A4 /* OIDTVAuthorizationService.m */, + 2D47AADE249A87020059B5A4 /* OIDTVServiceConfiguration.h */, + 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */, + ); + path = AppAuthTV; + sourceTree = ""; + }; 2D91B867249177180005B197 /* EnterpriseUserAgentFramework */ = { isa = PBXGroup; children = ( @@ -957,6 +1054,15 @@ path = EnterpriseUserAgentFramework; sourceTree = ""; }; + 2D9385B824B37CAD009A12D7 /* TVFramework */ = { + isa = PBXGroup; + children = ( + 2D9385B924B37CAD009A12D7 /* AppAuthTV.h */, + 2D9385BA24B37CAD009A12D7 /* Info.plist */, + ); + path = TVFramework; + sourceTree = ""; + }; 340DAE241D581FE700EC285B /* macOS */ = { isa = PBXGroup; children = ( @@ -1004,6 +1110,7 @@ 343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */, 342F42C32177B1FC00574F24 /* AppAuthCore.framework */, 348970972177B3B000ABEED4 /* AppAuthCoreTests.xctest */, + 2D9385B724B37CAD009A12D7 /* AppAuthTV.framework */, 2D91B862249053190005B197 /* AppAuthEnterpriseUserAgent.framework */, ); name = Products; @@ -1016,10 +1123,13 @@ 2D91B867249177180005B197 /* EnterpriseUserAgentFramework */, 348970992178F40600ABEED4 /* CoreFramework */, 343AAA4C1E8345B600F9D36E /* Framework */, + 2D9385B824B37CAD009A12D7 /* TVFramework */, 8A9B9D5E24561EC40055353E /* AppAuthCore */, 8A9B9D632456227D0055353E /* AppAuth */, + 2D47AAD7249A86E30059B5A4 /* AppAuthTV */, 341741AF1C5D8243000EF209 /* AppAuth.h */, 3489709E21791B0C00ABEED4 /* AppAuthCore.h */, + 2D47AADB249A87010059B5A4 /* AppAuthTV.h */, 2D0BB86A249D5B75005BA653 /* AppAuthEnterpriseUserAgent.h */, ); path = Source; @@ -1229,6 +1339,44 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2D9385B224B37CAD009A12D7 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D93861B24B38810009A12D7 /* OIDAuthorizationResponse.h in Headers */, + 2D93861F24B3881B009A12D7 /* OIDAuthState.h in Headers */, + 2D93863724B38827009A12D7 /* OIDGrantTypes.h in Headers */, + 2D93862F24B38826009A12D7 /* OIDEndSessionResponse.h in Headers */, + 2D93863924B38827009A12D7 /* OIDIDToken.h in Headers */, + 2D93862824B3881C009A12D7 /* OIDErrorUtilities.h in Headers */, + 2D93863124B38826009A12D7 /* OIDEndSessionRequest.h in Headers */, + 2D93863D24B38827009A12D7 /* OIDScopes.h in Headers */, + 2D93864124B38828009A12D7 /* OIDServiceConfiguration.h in Headers */, + 2D93862124B3881B009A12D7 /* OIDAuthStateChangeDelegate.h in Headers */, + 2D93862A24B3881C009A12D7 /* OIDExternalUserAgentSession.h in Headers */, + 2D93863324B38826009A12D7 /* OIDRegistrationResponse.h in Headers */, + 2D93864324B38828009A12D7 /* OIDServiceDiscovery.h in Headers */, + 2D93862624B3881C009A12D7 /* OIDError.h in Headers */, + 2D93864524B38828009A12D7 /* OIDTokenRequest.h in Headers */, + 2D93864724B38828009A12D7 /* OIDTokenResponse.h in Headers */, + 2D93864924B38828009A12D7 /* OIDTokenUtilities.h in Headers */, + 2D93862C24B38826009A12D7 /* OIDExternalUserAgent.h in Headers */, + 2D93863F24B38828009A12D7 /* OIDScopeUtilities.h in Headers */, + 2D93863B24B38827009A12D7 /* OIDResponseTypes.h in Headers */, + 2D93862224B3881C009A12D7 /* OIDAuthStateErrorDelegate.h in Headers */, + 2D93864D24B38829009A12D7 /* OIDURLSessionProvider.h in Headers */, + 2D93863524B38827009A12D7 /* OIDRegistrationRequest.h in Headers */, + 2D93861D24B38815009A12D7 /* OIDAuthorizationService.h in Headers */, + 2D93862B24B38825009A12D7 /* OIDExternalUserAgentRequest.h in Headers */, + 2D93861A24B3880B009A12D7 /* OIDAuthorizationRequest.h in Headers */, + 2D9385DE24B3861E009A12D7 /* AppAuthTV.h in Headers */, + 2D9385DF24B38646009A12D7 /* OIDTVAuthorizationRequest.h in Headers */, + 2D9385E124B3865E009A12D7 /* OIDTVAuthorizationService.h in Headers */, + 2D9385E024B38658009A12D7 /* OIDTVAuthorizationResponse.h in Headers */, + 2D9385E224B38669009A12D7 /* OIDTVServiceConfiguration.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 340DAE4C1D58216A00EC285B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1438,6 +1586,24 @@ productReference = 2D91B862249053190005B197 /* AppAuthEnterpriseUserAgent.framework */; productType = "com.apple.product-type.framework"; }; + 2D9385B624B37CAD009A12D7 /* AppAuthTV */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D9385C824B37CAD009A12D7 /* Build configuration list for PBXNativeTarget "AppAuthTV" */; + buildPhases = ( + 2D9385B224B37CAD009A12D7 /* Headers */, + 2D9385B324B37CAD009A12D7 /* Sources */, + 2D9385B424B37CAD009A12D7 /* Frameworks */, + 2D9385B524B37CAD009A12D7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AppAuthTV; + productName = AppAuthTV; + productReference = 2D9385B724B37CAD009A12D7 /* AppAuthTV.framework */; + productType = "com.apple.product-type.framework"; + }; 340DAE4D1D58216A00EC285B /* AppAuth-macOS */ = { isa = PBXNativeTarget; buildConfigurationList = 340DAE541D58216A00EC285B /* Build configuration list for PBXNativeTarget "AppAuth-macOS" */; @@ -1731,6 +1897,11 @@ LastUpgradeCheck = 1000; ORGANIZATIONNAME = "OpenID Foundation"; TargetAttributes = { + 2D9385B624B37CAD009A12D7 = { + CreatedOnToolsVersion = 11.5; + DevelopmentTeam = AUX79W8H33; + ProvisioningStyle = Automatic; + }; 340DAE4D1D58216A00EC285B = { CreatedOnToolsVersion = 7.3.1; }; @@ -1816,6 +1987,7 @@ 343AAAC91E8348AA00F9D36E /* AppAuth_macOSTests */, 342F42842177B1FC00574F24 /* AppAuthCore */, 3489707D2177B3B000ABEED4 /* AppAuthCoreTests */, + 2D9385B624B37CAD009A12D7 /* AppAuthTV */, 2D91B81D249053190005B197 /* AppAuthEnterpriseUserAgent */, ); }; @@ -1829,6 +2001,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2D9385B524B37CAD009A12D7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 341741EE1C5D8283000EF209 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1952,6 +2131,41 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2D9385B324B37CAD009A12D7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D93862E24B38826009A12D7 /* OIDFieldMapping.m in Sources */, + 2D93864024B38828009A12D7 /* OIDScopeUtilities.m in Sources */, + 2D93862024B3881B009A12D7 /* OIDAuthState.m in Sources */, + 2D93861924B38803009A12D7 /* OIDAuthorizationRequest.m in Sources */, + 2D93863824B38827009A12D7 /* OIDGrantTypes.m in Sources */, + 2D93864824B38828009A12D7 /* OIDTokenResponse.m in Sources */, + 2D93863624B38827009A12D7 /* OIDRegistrationRequest.m in Sources */, + 2D93863224B38826009A12D7 /* OIDEndSessionRequest.m in Sources */, + 2D93864424B38828009A12D7 /* OIDServiceDiscovery.m in Sources */, + 2D93861C24B38812009A12D7 /* OIDAuthorizationResponse.m in Sources */, + 2D93863A24B38827009A12D7 /* OIDIDToken.m in Sources */, + 2D93864F24B38840009A12D7 /* OIDTVAuthorizationRequest.m in Sources */, + 2D93864624B38828009A12D7 /* OIDTokenRequest.m in Sources */, + 2D93865024B38840009A12D7 /* OIDTVAuthorizationResponse.m in Sources */, + 2D93864224B38828009A12D7 /* OIDServiceConfiguration.m in Sources */, + 2D93864A24B38829009A12D7 /* OIDTokenUtilities.m in Sources */, + 2D93862724B3881C009A12D7 /* OIDError.m in Sources */, + 2D93862424B3881C009A12D7 /* OIDClientMetadataParameters.m in Sources */, + 2D93864E24B38829009A12D7 /* OIDURLSessionProvider.m in Sources */, + 2D93864C24B38829009A12D7 /* OIDURLQueryComponent.m in Sources */, + 2D93863024B38826009A12D7 /* OIDEndSessionResponse.m in Sources */, + 2D93865224B38840009A12D7 /* OIDTVServiceConfiguration.m in Sources */, + 2D93861E24B3881B009A12D7 /* OIDAuthorizationService.m in Sources */, + 2D93863C24B38827009A12D7 /* OIDResponseTypes.m in Sources */, + 2D93863424B38826009A12D7 /* OIDRegistrationResponse.m in Sources */, + 2D93862924B3881C009A12D7 /* OIDErrorUtilities.m in Sources */, + 2D93865124B38840009A12D7 /* OIDTVAuthorizationService.m in Sources */, + 2D93863E24B38827009A12D7 /* OIDScopes.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 340DAE4A1D58216A00EC285B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2112,6 +2326,7 @@ 341310D31E6F944D00D5DEE5 /* OIDError.m in Sources */, 341310DE1E6F944D00D5DEE5 /* OIDTokenRequest.m in Sources */, 341310DF1E6F944D00D5DEE5 /* OIDTokenResponse.m in Sources */, + 2D47AAEC249A87020059B5A4 /* OIDTVAuthorizationService.m in Sources */, A6DEAB842017A7040022AC32 /* OIDEndSessionResponse.m in Sources */, 341310DC1E6F944D00D5DEE5 /* OIDServiceConfiguration.m in Sources */, 341310BF1E6F943C00D5DEE5 /* OIDClientMetadataParameters.m in Sources */, @@ -2120,8 +2335,11 @@ 341310D91E6F944D00D5DEE5 /* OIDResponseTypes.m in Sources */, 341310E11E6F944D00D5DEE5 /* OIDURLQueryComponent.m in Sources */, CF37C0701F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */, + 2D47AAE1249A87020059B5A4 /* OIDTVAuthorizationResponse.m in Sources */, 341310D41E6F944D00D5DEE5 /* OIDErrorUtilities.m in Sources */, 341310D81E6F944D00D5DEE5 /* OIDGrantTypes.m in Sources */, + 2D47AAE4249A87020059B5A4 /* OIDTVServiceConfiguration.m in Sources */, + 2D47AAE8249A87020059B5A4 /* OIDTVAuthorizationRequest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2508,6 +2726,73 @@ }; name = Release; }; + 2D9385C924B37CAD009A12D7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AUX79W8H33; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "$(SRCROOT)/Source/TVFramework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthTV; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 2D9385CA24B37CAD009A12D7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AUX79W8H33; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "$(SRCROOT)/Source/TVFramework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthTV; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; 340DAE551D58216A00EC285B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3199,6 +3484,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 2D9385C824B37CAD009A12D7 /* Build configuration list for PBXNativeTarget "AppAuthTV" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D9385C924B37CAD009A12D7 /* Debug */, + 2D9385CA24B37CAD009A12D7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 340DAE541D58216A00EC285B /* Build configuration list for PBXNativeTarget "AppAuth-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthTV.xcscheme b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthTV.xcscheme new file mode 100644 index 000000000..821dc33b5 --- /dev/null +++ b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthTV.xcscheme @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Package.swift b/Package.swift index e6a198173..da4c1da8b 100644 --- a/Package.swift +++ b/Package.swift @@ -37,7 +37,10 @@ let package = Package( targets: ["AppAuth"]), .library( name: "AppAuthEnterpriseUserAgent", - targets: ["AppAuthEnterpriseUserAgent"]) + targets: ["AppAuthEnterpriseUserAgent"]), + .library( + name: "AppAuthTV", + targets: ["AppAuthTV"]) ], dependencies: [], targets: [ @@ -68,6 +71,12 @@ let package = Package( .headerSearchPath("iOS"), ] ), + .target( + name: "AppAuthTV", + dependencies: ["AppAuthCore"], + path: "Source/AppAuthTV", + publicHeadersPath: "" + ), .testTarget( name: "AppAuthCoreTests", dependencies: ["AppAuthCore"], diff --git a/Source/AppAuthTV/AppAuthTV.h b/Source/AppAuthTV.h similarity index 75% rename from Source/AppAuthTV/AppAuthTV.h rename to Source/AppAuthTV.h index 32a6d8581..3768c6c14 100644 --- a/Source/AppAuthTV/AppAuthTV.h +++ b/Source/AppAuthTV.h @@ -1,7 +1,7 @@ /*! @file AppAuthTV.h - @brief GTMAppAuth SDK + @brief AppAuthTV SDK @copyright - Copyright 2016 Google Inc. + Copyright 2020 Google Inc. @copydetails Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ limitations under the License. */ -#import "GTMTVAuthorizationRequest.h" -#import "GTMTVAuthorizationResponse.h" -#import "GTMTVAuthorizationService.h" -#import "GTMTVServiceConfiguration.h" \ No newline at end of file +#import "OIDTVAuthorizationRequest.h" +#import "OIDTVAuthorizationResponse.h" +#import "OIDTVAuthorizationService.h" +#import "OIDTVServiceConfiguration.h" diff --git a/Source/AppAuthTV/GTMAppAuth.h b/Source/AppAuthTV/GTMAppAuth.h deleted file mode 100644 index b018d6fc4..000000000 --- a/Source/AppAuthTV/GTMAppAuth.h +++ /dev/null @@ -1,21 +0,0 @@ -/*! @file GTMAppAuth.h - @brief GTMAppAuth SDK - @copyright - Copyright 2016 Google Inc. - @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 "GTMTVAuthorizationRequest.h" -#import "GTMTVAuthorizationResponse.h" -#import "GTMTVAuthorizationService.h" diff --git a/Source/AppAuthTV/GTMTVAuthorizationRequest.h b/Source/AppAuthTV/OIDTVAuthorizationRequest.h similarity index 84% rename from Source/AppAuthTV/GTMTVAuthorizationRequest.h rename to Source/AppAuthTV/OIDTVAuthorizationRequest.h index 032c05488..2496948f1 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationRequest.h +++ b/Source/AppAuthTV/OIDTVAuthorizationRequest.h @@ -1,5 +1,5 @@ -/*! @file GTMTVAuthorizationRequest.h - @brief GTMAppAuth SDK +/*! @file OIDTVAuthorizationRequest.h + @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. @copydetails @@ -18,16 +18,16 @@ #import -#import "AppAuthCore.h" +#import "OIDAuthorizationRequest.h" -@class GTMTVServiceConfiguration; +@class OIDTVServiceConfiguration; NS_ASSUME_NONNULL_BEGIN /*! @brief Represents a TV and limited input device authorization request. - @see https://developers.google.com/identity/protocols/OAuth2ForDevices + @see https://tools.ietf.org/html/rfc8628#section-3.1 */ -@interface GTMTVAuthorizationRequest : OIDAuthorizationRequest +@interface OIDTVAuthorizationRequest : OIDAuthorizationRequest /*! @brief Creates a TV authorization request with opinionated defaults @param configuration The service's configuration. @@ -37,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN @param additionalParameters The client's additional authorization parameters. */ - (instancetype) - initWithConfiguration:(GTMTVServiceConfiguration *)configuration + initWithConfiguration:(OIDTVServiceConfiguration *)configuration clientId:(NSString *)clientID clientSecret:(NSString *)clientSecret scopes:(nullable NSArray *)scopes diff --git a/Source/AppAuthTV/GTMTVAuthorizationRequest.m b/Source/AppAuthTV/OIDTVAuthorizationRequest.m similarity index 86% rename from Source/AppAuthTV/GTMTVAuthorizationRequest.m rename to Source/AppAuthTV/OIDTVAuthorizationRequest.m index f47d80aac..4afae8cd8 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationRequest.m +++ b/Source/AppAuthTV/OIDTVAuthorizationRequest.m @@ -1,5 +1,5 @@ -/*! @file GTMTVAuthorizationRequest.m - @brief GTMAppAuth SDK +/*! @file OIDTVAuthorizationRequest.m + @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. @copydetails @@ -15,17 +15,14 @@ See the License for the specific language governing permissions and limitations under the License. */ - -#import "GTMTVAuthorizationRequest.h" - -#import "AppAuthCore.h" +#import "OIDTVAuthorizationRequest.h" +#import "OIDTVServiceConfiguration.h" #import "OIDURLQueryComponent.h" -#import "GTMTVServiceConfiguration.h" -@implementation GTMTVAuthorizationRequest +@implementation OIDTVAuthorizationRequest - (instancetype) - initWithConfiguration:(GTMTVServiceConfiguration *)configuration + initWithConfiguration:(OIDTVServiceConfiguration *)configuration clientId:(NSString *)clientID clientSecret:(nullable NSString *)clientSecret scope:(nullable NSString *)scope @@ -38,9 +35,9 @@ @implementation GTMTVAuthorizationRequest codeChallengeMethod:(nullable NSString *)codeChallengeMethod additionalParameters:(nullable NSDictionary *)additionalParameters { - if (![configuration isKindOfClass:[GTMTVServiceConfiguration class]]) { - NSAssert([configuration isKindOfClass:[GTMTVServiceConfiguration class]], - @"configuration parameter must be of type GTMTVServiceConfiguration, encountered %@", + if (![configuration isKindOfClass:[OIDTVServiceConfiguration class]]) { + NSAssert([configuration isKindOfClass:[OIDTVServiceConfiguration class]], + @"configuration parameter must be of type OIDTVServiceConfiguration, encountered %@", NSStringFromClass([configuration class])); return nil; } @@ -60,7 +57,7 @@ @implementation GTMTVAuthorizationRequest } - (instancetype) - initWithConfiguration:(GTMTVServiceConfiguration *)configuration + initWithConfiguration:(OIDTVServiceConfiguration *)configuration clientId:(NSString *)clientID clientSecret:(NSString *)clientSecret scopes:(nullable NSArray *)scopes @@ -79,7 +76,7 @@ @implementation GTMTVAuthorizationRequest - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p, request: %@>", NSStringFromClass([self class]), - self, + (void *)self, self.authorizationRequestURL]; } @@ -105,7 +102,7 @@ - (NSURLRequest *)URLRequest { static NSString *const kHTTPContentTypeHeaderValue = @"application/x-www-form-urlencoded; charset=UTF-8"; - GTMTVServiceConfiguration *tvConfiguration = (GTMTVServiceConfiguration *)self.configuration; + OIDTVServiceConfiguration *tvConfiguration = (OIDTVServiceConfiguration *)self.configuration; NSMutableURLRequest *URLRequest = [[NSURLRequest requestWithURL:tvConfiguration.TVAuthorizationEndpoint] mutableCopy]; diff --git a/Source/AppAuthTV/GTMTVAuthorizationResponse.h b/Source/AppAuthTV/OIDTVAuthorizationResponse.h similarity index 83% rename from Source/AppAuthTV/GTMTVAuthorizationResponse.h rename to Source/AppAuthTV/OIDTVAuthorizationResponse.h index ce1365f4f..cb4793c59 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationResponse.h +++ b/Source/AppAuthTV/OIDTVAuthorizationResponse.h @@ -1,5 +1,5 @@ -/*! @file GTMTVAuthorizationResponse.h - @brief GTMAppAuth SDK +/*! @file OIDTVAuthorizationResponse.h + @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. @copydetails @@ -18,22 +18,22 @@ #import -#import "AppAuthCore.h" +#import "OIDAuthorizationResponse.h" -@class GTMTVAuthorizationRequest; +@class OIDTVAuthorizationRequest; @class OIDTokenRequest; NS_ASSUME_NONNULL_BEGIN /*! @brief The @c grant_type value for the the TV authorization flow. - @see https://developers.google.com/identity/protocols/OAuth2ForDevices + @see https://tools.ietf.org/html/rfc8628#section-3.4 */ -extern NSString *const GTMTVDeviceTokenGrantType; +extern NSString *const OIDTVDeviceTokenGrantType; /*! @brief Represents the response to a TV authorization request. - @see https://developers.google.com/identity/protocols/OAuth2ForDevices + @see https://tools.ietf.org/html/rfc8628#section-3.5 */ -@interface GTMTVAuthorizationResponse : OIDAuthorizationResponse +@interface OIDTVAuthorizationResponse : OIDAuthorizationResponse /*! @brief The verification URL that should be displayed to the user instructing them to visit the URL and enter the code. @@ -69,20 +69,20 @@ extern NSString *const GTMTVDeviceTokenGrantType; properties are populated. Non-normative parameters are placed in the @c #additionalParameters dictionary. */ -- (instancetype)initWithRequest:(GTMTVAuthorizationRequest *)request +- (instancetype)initWithRequest:(OIDTVAuthorizationRequest *)request parameters:(NSDictionary *> *)parameters NS_DESIGNATED_INITIALIZER; /*! @brief Creates a token request suitable for polling the token endpoint with the @c deviceCode. @return A @c OIDTokenRequest suitable for polling the token endpoint. - @see https://developers.google.com/identity/protocols/OAuth2ForDevices + @see https://tools.ietf.org/html/rfc8628#section-3.4 */ - (nullable OIDTokenRequest *)tokenPollRequest; /*! @brief Creates a token request suitable for polling the token endpoint with the @c deviceCode. @param additionalParameters Additional parameters for the token request. @return A @c OIDTokenRequest suitable for polling the token endpoint. - @see https://developers.google.com/identity/protocols/OAuth2ForDevices + @see https://tools.ietf.org/html/rfc8628#section-3.4 */ - (nullable OIDTokenRequest *)tokenPollRequestWithAdditionalParameters: (nullable NSDictionary *)additionalParameters; diff --git a/Source/AppAuthTV/GTMTVAuthorizationResponse.m b/Source/AppAuthTV/OIDTVAuthorizationResponse.m similarity index 89% rename from Source/AppAuthTV/GTMTVAuthorizationResponse.m rename to Source/AppAuthTV/OIDTVAuthorizationResponse.m index bee285fc4..6a0110baa 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationResponse.m +++ b/Source/AppAuthTV/OIDTVAuthorizationResponse.m @@ -1,5 +1,5 @@ -/*! @file GTMTVAuthorizationResponse.m - @brief GTMAppAuth SDK +/*! @file OIDTVAuthorizationResponse.m + @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. @copydetails @@ -16,15 +16,15 @@ limitations under the License. */ -#import "GTMTVAuthorizationResponse.h" +#import "OIDTVAuthorizationResponse.h" -#import "GTMTVAuthorizationRequest.h" -#import "AppAuthCore.h" #import "OIDDefines.h" #import "OIDFieldMapping.h" +#import "OIDTokenRequest.h" +#import "OIDTVAuthorizationRequest.h" -NSString *const GTMTVDeviceTokenGrantType = @"http://oauth.net/grant_type/device/1.0"; +NSString *const OIDTVDeviceTokenGrantType = @"http://oauth.net/grant_type/device/1.0"; /*! @brief The key for the @c verificationURL property in the incoming parameters and for @c NSSecureCoding. @@ -59,13 +59,7 @@ */ static NSString *const kRequestKey = @"request"; -@implementation GTMTVAuthorizationResponse - -@synthesize verificationURL = _verificationURL; -@synthesize userCode = _userCode; -@synthesize deviceCode = _deviceCode; -@synthesize interval = _interval; -@synthesize expirationDate = _expirationDate; +@implementation OIDTVAuthorizationResponse /*! @brief Returns a mapping of incoming parameters to instance variables. @return A mapping of incoming parameters to instance variables. @@ -99,7 +93,7 @@ @implementation GTMTVAuthorizationResponse #pragma mark - Initializers -- (instancetype)initWithRequest:(GTMTVAuthorizationRequest *)request +- (instancetype)initWithRequest:(OIDTVAuthorizationRequest *)request parameters:(NSDictionary *> *)parameters { self = [super initWithRequest:request parameters:parameters]; return self; @@ -123,7 +117,7 @@ - (NSString *)description { "additionalParameters: %@, " "request: %@>", NSStringFromClass([self class]), - self, + (void *)self, _verificationURL, _userCode, _deviceCode, @@ -143,7 +137,7 @@ - (OIDTokenRequest *)tokenPollRequestWithAdditionalParameters: (NSDictionary *)additionalParameters { OIDTokenRequest *pollRequest = [[OIDTokenRequest alloc] initWithConfiguration:self.request.configuration - grantType:GTMTVDeviceTokenGrantType + grantType:OIDTVDeviceTokenGrantType authorizationCode:_deviceCode redirectURL:[[NSURL alloc] init] clientID:self.request.clientID diff --git a/Source/AppAuthTV/GTMTVAuthorizationService.h b/Source/AppAuthTV/OIDTVAuthorizationService.h similarity index 69% rename from Source/AppAuthTV/GTMTVAuthorizationService.h rename to Source/AppAuthTV/OIDTVAuthorizationService.h index a37d4aa4e..14027bc09 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationService.h +++ b/Source/AppAuthTV/OIDTVAuthorizationService.h @@ -1,5 +1,5 @@ -/*! @file GTMTVAuthorizationService.h - @brief GTMAppAuth SDK +/*! @file OIDTVAuthorizationService.h + @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. @copydetails @@ -21,17 +21,17 @@ NS_ASSUME_NONNULL_BEGIN @class OIDAuthState; -@class GTMTVAuthorizationRequest; -@class GTMTVAuthorizationResponse; -@class GTMTVServiceConfiguration; +@class OIDTVAuthorizationRequest; +@class OIDTVAuthorizationResponse; +@class OIDTVServiceConfiguration; /*! @brief The block that is called when the TV authorization has initialized. @param response The authorization response, or nil if there was an error. Display - @c GTMTVAuthorizationResponse.userCode and @c GTMTVAuthorizationResponse.verificationURL to + @c OIDTVAuthorizationResponse.userCode and @c OIDTVAuthorizationResponse.verificationURL to the user so they can action the request. @param error The error if an error occurred. */ -typedef void (^GTMTVAuthorizationInitialization)(GTMTVAuthorizationResponse *_Nullable response, +typedef void (^OIDTVAuthorizationInitialization)(OIDTVAuthorizationResponse *_Nullable response, NSError *_Nullable error); /*! @brief The block that is called when the TV authorization has completed. @@ -39,26 +39,19 @@ typedef void (^GTMTVAuthorizationInitialization)(GTMTVAuthorizationResponse *_Nu API calls, or nil if there was an error. @param error The error if an error occurred. */ -typedef void (^GTMTVAuthorizationCompletion) +typedef void (^OIDTVAuthorizationCompletion) (OIDAuthState *_Nullable authorization, NSError *_Nullable error); -/*! @brief Block returned when authorization is initialized to that will cancel the pending +/*! @brief Block returned when authorization is initialized that will cancel the pending authorization when executed. Has no effect if called twice or after the authorization concluded. */ -typedef void (^GTMTVAuthorizationCancelBlock)(void); +typedef void (^OIDTVAuthorizationCancelBlock)(void); /*! @brief Performs authorization flows designed for TVs and other limited input devices. */ -@interface GTMTVAuthorizationService : NSObject - -#if !GTM_APPAUTH_SKIP_GOOGLE_SUPPORT -/*! @brief Convenience method to return the TV authorization URL for Google. - @return TV authorization URL for Google. - */ -+ (GTMTVServiceConfiguration *)TVConfigurationForGoogle; -#endif // !GTM_APPAUTH_SKIP_GOOGLE_SUPPORT +@interface OIDTVAuthorizationService : NSObject /*! @brief Starts a TV authorization flow with the given request and polls for a response. @param request The TV authorization request to initiate. @@ -67,15 +60,15 @@ typedef void (^GTMTVAuthorizationCancelBlock)(void); authorization as the user has yet to grant it. Rather, it contains the information that you show to the user in order for them to authorize the request on another device. @param completion Block that is called on the success or failure of the authorization. If the - user approves the request, you will get a @c GTMAppAuthFetherAuthorization that you can use + user approves the request, you will get a @c OIDAuthState that you can use to authenticate API calls, otherwis eyou will get an error. @return A block which you can execute if you need to cancel the ongoing authorization. Has no effect if called twice, or called after the authorization concludes. - @see https://developers.google.com/identity/protocols/OAuth2ForDevices + @see https://tools.ietf.org/html/rfc8628 */ -+ (GTMTVAuthorizationCancelBlock)authorizeTVRequest:(GTMTVAuthorizationRequest *)request - initializaiton:(GTMTVAuthorizationInitialization)initialization - completion:(GTMTVAuthorizationCompletion)completion; ++ (OIDTVAuthorizationCancelBlock)authorizeTVRequest:(OIDTVAuthorizationRequest *)request + initialization:(OIDTVAuthorizationInitialization)initialization + completion:(OIDTVAuthorizationCompletion)completion; @end diff --git a/Source/AppAuthTV/GTMTVAuthorizationService.m b/Source/AppAuthTV/OIDTVAuthorizationService.m similarity index 82% rename from Source/AppAuthTV/GTMTVAuthorizationService.m rename to Source/AppAuthTV/OIDTVAuthorizationService.m index 5160d0305..fed16a938 100644 --- a/Source/AppAuthTV/GTMTVAuthorizationService.m +++ b/Source/AppAuthTV/OIDTVAuthorizationService.m @@ -1,5 +1,5 @@ -/*! @file GTMTVAuthorizationService.m - @brief GTMAppAuth SDK +/*! @file OIDTVAuthorizationService.m + @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. @copydetails @@ -16,60 +16,40 @@ limitations under the License. */ -#import "GTMTVAuthorizationService.h" +#import "OIDTVAuthorizationService.h" -#import "AppAuthCore.h" +#import "OIDAuthorizationService.h" +#import "OIDAuthState.h" #import "OIDDefines.h" +#import "OIDErrorUtilities.h" #import "OIDURLQueryComponent.h" -#import "GTMTVAuthorizationRequest.h" -#import "GTMTVAuthorizationResponse.h" -#import "GTMTVServiceConfiguration.h" - -/*! @brief Google's device authorization endpoint. - */ -NSString *const kGoogleDeviceAuthorizationEndpoint = - @"https://accounts.google.com/o/oauth2/device/code"; +#import "OIDTVAuthorizationRequest.h" +#import "OIDTVAuthorizationResponse.h" +#import "OIDTVServiceConfiguration.h" /*! @brief The authorization pending error code. - @see https://developers.google.com/identity/protocols/OAuth2ForDevices + @see https://tools.ietf.org/html/rfc8628#section-3.5 */ NSString *const kErrorCodeAuthorizationPending = @"authorization_pending"; /*! @brief The slow down error code. - @see https://developers.google.com/identity/protocols/OAuth2ForDevices + @see https://tools.ietf.org/html/rfc8628#section-3.5 */ NSString *const kErrorCodeSlowDown = @"slow_down"; -@implementation GTMTVAuthorizationService +@implementation OIDTVAuthorizationService #pragma mark - Initializers -#if !GTM_APPAUTH_SKIP_GOOGLE_SUPPORT -+ (GTMTVServiceConfiguration *)TVConfigurationForGoogle { - NSURL *authorizationEndpoint = - [NSURL URLWithString:@"https://accounts.google.com/o/oauth2/v2/auth"]; - NSURL *tokenEndpoint = - [NSURL URLWithString:@"https://www.googleapis.com/oauth2/v4/token"]; - NSURL *TVAuthorizationEndpoint = - [NSURL URLWithString:kGoogleDeviceAuthorizationEndpoint]; - - GTMTVServiceConfiguration *configuration = - [[GTMTVServiceConfiguration alloc] initWithAuthorizationEndpoint:authorizationEndpoint - TVAuthorizationEndpoint:TVAuthorizationEndpoint - tokenEndpoint:tokenEndpoint]; - return configuration; -} -#endif // !GTM_APPAUTH_SKIP_GOOGLE_SUPPORT - -+ (GTMTVAuthorizationCancelBlock)authorizeTVRequest:(GTMTVAuthorizationRequest *)request - initializaiton:(GTMTVAuthorizationInitialization)initialization - completion:(GTMTVAuthorizationCompletion)completion { ++ (OIDTVAuthorizationCancelBlock)authorizeTVRequest:(OIDTVAuthorizationRequest *)request + initialization:(OIDTVAuthorizationInitialization)initialization + completion:(OIDTVAuthorizationCompletion)completion { // Block level variable that can be used to cancel the polling. __block BOOL pollRunning = YES; // Block that will be returned allowign the caller to cancel the polling. - GTMTVAuthorizationCancelBlock cancelBlock = ^{ + OIDTVAuthorizationCancelBlock cancelBlock = ^{ if (pollRunning) { dispatch_async(dispatch_get_main_queue(), ^{ NSError *cancelError = @@ -159,8 +139,8 @@ + (GTMTVAuthorizationCancelBlock)authorizeTVRequest:(GTMTVAuthorizationRequest * } // Parses the authorization response. - GTMTVAuthorizationResponse *TVAuthorizationResponse = - [[GTMTVAuthorizationResponse alloc] initWithRequest:request parameters:json]; + OIDTVAuthorizationResponse *TVAuthorizationResponse = + [[OIDTVAuthorizationResponse alloc] initWithRequest:request parameters:json]; if (!TVAuthorizationResponse) { // A problem occurred constructing the token response from the JSON. NSError *returnedError = diff --git a/Source/AppAuthTV/GTMTVServiceConfiguration.h b/Source/AppAuthTV/OIDTVServiceConfiguration.h similarity index 88% rename from Source/AppAuthTV/GTMTVServiceConfiguration.h rename to Source/AppAuthTV/OIDTVServiceConfiguration.h index 7fb6babfd..2b6b29093 100644 --- a/Source/AppAuthTV/GTMTVServiceConfiguration.h +++ b/Source/AppAuthTV/OIDTVServiceConfiguration.h @@ -1,5 +1,5 @@ -/*! @file GTMTVServiceConfiguration.h - @brief GTMAppAuth SDK +/*! @file OIDTVServiceConfiguration.h + @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. @copydetails @@ -16,13 +16,13 @@ limitations under the License. */ -#import "AppAuthCore.h" +#import "OIDServiceConfiguration.h" NS_ASSUME_NONNULL_BEGIN -/*! @brief Configuration for authorizing the user with the @c GTMTVAuthorizationService. +/*! @brief Configuration for authorizing the user with the @c OIDTVAuthorizationService. */ -@interface GTMTVServiceConfiguration : OIDServiceConfiguration +@interface OIDTVServiceConfiguration : OIDServiceConfiguration /*! @brief The TV authorization endpoint URI. */ diff --git a/Source/AppAuthTV/GTMTVServiceConfiguration.m b/Source/AppAuthTV/OIDTVServiceConfiguration.m similarity index 86% rename from Source/AppAuthTV/GTMTVServiceConfiguration.m rename to Source/AppAuthTV/OIDTVServiceConfiguration.m index e6493292c..edd53962c 100644 --- a/Source/AppAuthTV/GTMTVServiceConfiguration.m +++ b/Source/AppAuthTV/OIDTVServiceConfiguration.m @@ -1,5 +1,5 @@ -/*! @file GTMTVServiceConfiguration.m - @brief GTMAppAuth SDK +/*! @file OIDTVServiceConfiguration.m + @brief AppAuth iOS SDK @copyright Copyright 2016 Google Inc. @copydetails @@ -16,9 +16,8 @@ limitations under the License. */ -#import "GTMTVServiceConfiguration.h" +#import "OIDTVServiceConfiguration.h" -#import "AppAuthCore.h" #import "OIDDefines.h" /*! @brief The key for the @c TVAuthorizationEndpoint property. @@ -27,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface GTMTVServiceConfiguration () +@interface OIDTVServiceConfiguration () /*! @brief Designated initializer. @param aDecoder NSCoder to unserialize the object from. @@ -36,18 +35,16 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIAL @end -@implementation GTMTVServiceConfiguration - -@synthesize TVAuthorizationEndpoint = _TVAuthorizationEndpoint; +@implementation OIDTVServiceConfiguration - (instancetype)init OID_UNAVAILABLE_USE_INITIALIZER( - @selector(initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint:)); + @selector(initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint:)) - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint - tokenEndpoint:(NSURL *)tokenEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint OID_UNAVAILABLE_USE_INITIALIZER( - @selector(initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint:)); + @selector(initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint:)) - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint TVAuthorizationEndpoint:(NSURL *)TVAuthorizationEndpoint @@ -85,7 +82,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p, TVAuthorizationEndpoint: %@ tokenEndpoint: %@>", NSStringFromClass([self class]), - self, + (void *)self, _TVAuthorizationEndpoint, self.tokenEndpoint]; } diff --git a/Source/TVFramework/AppAuthTV.h b/Source/TVFramework/AppAuthTV.h new file mode 100644 index 000000000..ab3c1e604 --- /dev/null +++ b/Source/TVFramework/AppAuthTV.h @@ -0,0 +1,57 @@ +/*! @file AppAuthTV.h + @brief AppAuthTV SDK + @copyright + Copyright 2020 Google Inc. + @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 + +//! Project version number for AppAuthTV. +FOUNDATION_EXPORT double AppAuthTVVersionNumber; + +//! Project version string for AppAuthTV. +FOUNDATION_EXPORT const unsigned char AppAuthTVVersionString[]; + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import diff --git a/Source/TVFramework/Info.plist b/Source/TVFramework/Info.plist new file mode 100644 index 000000000..9bcb24442 --- /dev/null +++ b/Source/TVFramework/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + From 8248ccb557efdde674306f5aa238a36f2a15054c Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Wed, 22 Jul 2020 15:36:53 -0400 Subject: [PATCH 121/204] Add AppAuthTV tests * OIDTVAuthorizationRequest tests * Add tests to AppAuthTVScheme * add AppAuthTV to .travis.yml --- .travis.yml | 1 + AppAuth.xcodeproj/project.pbxproj | 144 +++++++++ .../xcshareddata/xcschemes/AppAuthTV.xcscheme | 12 +- Package.swift | 9 +- .../OIDTVAuthorizationRequestTests.h | 60 ++++ .../OIDTVAuthorizationRequestTests.m | 290 ++++++++++++++++++ 6 files changed, 513 insertions(+), 3 deletions(-) create mode 100644 UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.h create mode 100644 UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m diff --git a/.travis.yml b/.travis.yml index 288293628..4adfa0042 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,7 @@ jobs: - SCHEME=AppAuth_macOS DESTINATION="'platform=macOS,arch=x86_64'" SDK=macosx10.15 - SCHEME=AppAuth-tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=13.0'" SDK=appletvsimulator13.0 - SCHEME=AppAuth_tvOS DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=13.0'" SDK=appletvsimulator13.0 + - SCHEME=AppAuthTV DESTINATION="'platform=tvOS Simulator,name=Apple TV,OS=13.0'" SDK=appletvsimulator13.0 before_script: - sudo gem install xcpretty script: diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 11b222444..ca5c21628 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -18,6 +18,24 @@ 2D47AAE4249A87020059B5A4 /* OIDTVServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */; }; 2D47AAE8249A87020059B5A4 /* OIDTVAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADD249A87010059B5A4 /* OIDTVAuthorizationRequest.m */; }; 2D47AAEC249A87020059B5A4 /* OIDTVAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AAE0249A87020059B5A4 /* OIDTVAuthorizationService.m */; }; + 2D8111FA24C0FD4C00984DA7 /* AppAuthTV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D9385B724B37CAD009A12D7 /* AppAuthTV.framework */; }; + 2D81120424C1036700984DA7 /* OIDTVAuthorizationRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D81120024C1036700984DA7 /* OIDTVAuthorizationRequestTests.m */; }; + 2D81120624C103C800984DA7 /* OIDAuthorizationRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742011C5D82D3000EF209 /* OIDAuthorizationRequestTests.m */; }; + 2D81120724C103CC00984DA7 /* OIDAuthorizationResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742031C5D82D3000EF209 /* OIDAuthorizationResponseTests.m */; }; + 2D81120824C103F200984DA7 /* OIDAuthStateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742051C5D82D3000EF209 /* OIDAuthStateTests.m */; }; + 2D81120924C103F200984DA7 /* OIDGrantTypesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742061C5D82D3000EF209 /* OIDGrantTypesTests.m */; }; + 2D81120A24C103F200984DA7 /* OIDResponseTypesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742071C5D82D3000EF209 /* OIDResponseTypesTests.m */; }; + 2D81120C24C103F300984DA7 /* OIDScopesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742081C5D82D3000EF209 /* OIDScopesTests.m */; }; + 2D81120D24C103F300984DA7 /* OIDServiceConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3417420A1C5D82D3000EF209 /* OIDServiceConfigurationTests.m */; }; + 2D81120E24C103F300984DA7 /* OIDServiceDiscoveryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3417420C1C5D82D3000EF209 /* OIDServiceDiscoveryTests.m */; }; + 2D81120F24C103F300984DA7 /* OIDTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3417420E1C5D82D3000EF209 /* OIDTokenRequestTests.m */; }; + 2D81121024C103F300984DA7 /* OIDTokenResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742101C5D82D3000EF209 /* OIDTokenResponseTests.m */; }; + 2D81121124C103F300984DA7 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; + 2D81121224C103F300984DA7 /* OIDURLQueryComponentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742121C5D82D3000EF209 /* OIDURLQueryComponentTests.m */; }; + 2D81121324C103F300984DA7 /* OIDURLQueryComponentTestsIOS7.m in Sources */ = {isa = PBXBuildFile; fileRef = 341742131C5D82D3000EF209 /* OIDURLQueryComponentTestsIOS7.m */; }; + 2D81121424C103F300984DA7 /* OIDRegistrationRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F821DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m */; }; + 2D81121524C103F300984DA7 /* OIDRegistrationResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */; }; + 2D81121624C103F300984DA7 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; 2D91B81F249053190005B197 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; 2D91B820249053190005B197 /* OIDExternalUserAgentCatalyst.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A7082D2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m */; }; 2D91B821249053190005B197 /* OIDFieldMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C41C5D8243000EF209 /* OIDFieldMapping.m */; }; @@ -641,6 +659,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 2D8111FB24C0FD4C00984DA7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 340E73741C5D819B0076B1F6 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2D9385B624B37CAD009A12D7; + remoteInfo = AppAuthTV; + }; 341741F61C5D8283000EF209 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 340E73741C5D819B0076B1F6 /* Project object */; @@ -737,6 +762,9 @@ 2D47AADE249A87020059B5A4 /* OIDTVServiceConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVServiceConfiguration.h; sourceTree = ""; }; 2D47AADF249A87020059B5A4 /* OIDTVAuthorizationService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVAuthorizationService.h; sourceTree = ""; }; 2D47AAE0249A87020059B5A4 /* OIDTVAuthorizationService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVAuthorizationService.m; sourceTree = ""; }; + 2D8111F524C0FD4C00984DA7 /* AppAuthTVTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuthTVTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D81120024C1036700984DA7 /* OIDTVAuthorizationRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVAuthorizationRequestTests.m; sourceTree = ""; }; + 2D81120324C1036700984DA7 /* OIDTVAuthorizationRequestTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVAuthorizationRequestTests.h; sourceTree = ""; }; 2D91B862249053190005B197 /* AppAuthEnterpriseUserAgent.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuthEnterpriseUserAgent.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2D91B868249177180005B197 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2D9385B724B37CAD009A12D7 /* AppAuthTV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuthTV.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -874,6 +902,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 2D8111F224C0FD4C00984DA7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D8111FA24C0FD4C00984DA7 /* AppAuthTV.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2D91B83C249053190005B197 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1045,6 +1081,15 @@ path = AppAuthTV; sourceTree = ""; }; + 2D52A08D24C24C260022E402 /* AppAuthTV */ = { + isa = PBXGroup; + children = ( + 2D81120324C1036700984DA7 /* OIDTVAuthorizationRequestTests.h */, + 2D81120024C1036700984DA7 /* OIDTVAuthorizationRequestTests.m */, + ); + path = AppAuthTV; + sourceTree = ""; + }; 2D91B867249177180005B197 /* EnterpriseUserAgentFramework */ = { isa = PBXGroup; children = ( @@ -1112,6 +1157,7 @@ 348970972177B3B000ABEED4 /* AppAuthCoreTests.xctest */, 2D9385B724B37CAD009A12D7 /* AppAuthTV.framework */, 2D91B862249053190005B197 /* AppAuthEnterpriseUserAgent.framework */, + 2D8111F524C0FD4C00984DA7 /* AppAuthTVTests.xctest */, ); name = Products; sourceTree = ""; @@ -1138,6 +1184,7 @@ 341741FB1C5D82D3000EF209 /* UnitTests */ = { isa = PBXGroup; children = ( + 2D52A08D24C24C260022E402 /* AppAuthTV */, 341742231C5D8317000EF209 /* UnitTestsInfo.plist */, 341742001C5D82D3000EF209 /* OIDAuthorizationRequestTests.h */, 341742011C5D82D3000EF209 /* OIDAuthorizationRequestTests.m */, @@ -1568,6 +1615,24 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 2D8111F424C0FD4C00984DA7 /* AppAuthTVTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D8111FF24C0FD4D00984DA7 /* Build configuration list for PBXNativeTarget "AppAuthTVTests" */; + buildPhases = ( + 2D8111F124C0FD4C00984DA7 /* Sources */, + 2D8111F224C0FD4C00984DA7 /* Frameworks */, + 2D8111F324C0FD4C00984DA7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 2D8111FC24C0FD4C00984DA7 /* PBXTargetDependency */, + ); + name = AppAuthTVTests; + productName = AppAuthTVTests; + productReference = 2D8111F524C0FD4C00984DA7 /* AppAuthTVTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 2D91B81D249053190005B197 /* AppAuthEnterpriseUserAgent */ = { isa = PBXNativeTarget; buildConfigurationList = 2D91B85F249053190005B197 /* Build configuration list for PBXNativeTarget "AppAuthEnterpriseUserAgent" */; @@ -1897,6 +1962,9 @@ LastUpgradeCheck = 1000; ORGANIZATIONNAME = "OpenID Foundation"; TargetAttributes = { + 2D8111F424C0FD4C00984DA7 = { + CreatedOnToolsVersion = 11.6; + }; 2D9385B624B37CAD009A12D7 = { CreatedOnToolsVersion = 11.5; DevelopmentTeam = AUX79W8H33; @@ -1989,11 +2057,19 @@ 3489707D2177B3B000ABEED4 /* AppAuthCoreTests */, 2D9385B624B37CAD009A12D7 /* AppAuthTV */, 2D91B81D249053190005B197 /* AppAuthEnterpriseUserAgent */, + 2D8111F424C0FD4C00984DA7 /* AppAuthTVTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 2D8111F324C0FD4C00984DA7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2D91B85E249053190005B197 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -2095,6 +2171,30 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 2D8111F124C0FD4C00984DA7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D81120C24C103F300984DA7 /* OIDScopesTests.m in Sources */, + 2D81121224C103F300984DA7 /* OIDURLQueryComponentTests.m in Sources */, + 2D81120D24C103F300984DA7 /* OIDServiceConfigurationTests.m in Sources */, + 2D81121324C103F300984DA7 /* OIDURLQueryComponentTestsIOS7.m in Sources */, + 2D81121524C103F300984DA7 /* OIDRegistrationResponseTests.m in Sources */, + 2D81120824C103F200984DA7 /* OIDAuthStateTests.m in Sources */, + 2D81121024C103F300984DA7 /* OIDTokenResponseTests.m in Sources */, + 2D81120F24C103F300984DA7 /* OIDTokenRequestTests.m in Sources */, + 2D81120424C1036700984DA7 /* OIDTVAuthorizationRequestTests.m in Sources */, + 2D81120E24C103F300984DA7 /* OIDServiceDiscoveryTests.m in Sources */, + 2D81120A24C103F200984DA7 /* OIDResponseTypesTests.m in Sources */, + 2D81120724C103CC00984DA7 /* OIDAuthorizationResponseTests.m in Sources */, + 2D81121424C103F300984DA7 /* OIDRegistrationRequestTests.m in Sources */, + 2D81120924C103F200984DA7 /* OIDGrantTypesTests.m in Sources */, + 2D81121624C103F300984DA7 /* OIDRPProfileCode.m in Sources */, + 2D81120624C103C800984DA7 /* OIDAuthorizationRequestTests.m in Sources */, + 2D81121124C103F300984DA7 /* OIDTokenUtilitiesTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2D91B81E249053190005B197 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2636,6 +2736,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 2D8111FC24C0FD4C00984DA7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2D9385B624B37CAD009A12D7 /* AppAuthTV */; + targetProxy = 2D8111FB24C0FD4C00984DA7 /* PBXContainerItemProxy */; + }; 341741F71C5D8283000EF209 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 340E737B1C5D819B0076B1F6 /* AppAuth-iOS */; @@ -2674,6 +2779,36 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 2D8111FD24C0FD4C00984DA7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + HEADER_SEARCH_PATHS = .; + INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthTVTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TVOS_DEPLOYMENT_TARGET = 10.1; + }; + name = Debug; + }; + 2D8111FE24C0FD4C00984DA7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + HEADER_SEARCH_PATHS = .; + INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthTVTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TVOS_DEPLOYMENT_TARGET = 10.1; + }; + name = Release; + }; 2D91B860249053190005B197 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3475,6 +3610,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 2D8111FF24C0FD4D00984DA7 /* Build configuration list for PBXNativeTarget "AppAuthTVTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D8111FD24C0FD4C00984DA7 /* Debug */, + 2D8111FE24C0FD4C00984DA7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 2D91B85F249053190005B197 /* Build configuration list for PBXNativeTarget "AppAuthEnterpriseUserAgent" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthTV.xcscheme b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthTV.xcscheme index 821dc33b5..fd3849bd6 100644 --- a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthTV.xcscheme +++ b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthTV.xcscheme @@ -28,7 +28,7 @@ buildForAnalyzing = "NO"> @@ -42,6 +42,16 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + +@class OIDTVServiceConfiguration; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Unit tests for @c OIDTVAuthorizationRequest. + */ +@interface OIDTVAuthorizationRequestTests : XCTestCase +- (OIDTVServiceConfiguration *)testServiceConfiguration; +- (NSDictionary *)bodyParametersFromURLRequest:(NSURLRequest *)urlRequest; + +/*! @brief Tests the initializer + */ +- (void)testInitializer; + +/*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying + * process and checking to make sure the source and destination both contain the + * @c TVAuthorizationEndpoint + */ +- (void)testCopying; + +/*! @brief Tests the @c NSSecureCoding implementation by round-tripping an instance through the + * coding process and checking to make sure the source and destination both contain the + * @c TVAuthorizationEndpoint + */ +- (void)testSecureCoding; + +/*! @brief Tests the @c URLRequest method on a request with no scopes or additional parameters + */ +- (void)testURLRequestBasicClientAuth; + +/*! @brief Tests the @c URLRequest method on a request with two scopes and no additional parameters + */ +- (void)testURLRequestScopes; + +/*! @brief Tests the @c URLRequest method on a request with two scopes and one additional parameter + */ +- (void)testURLRequestAdditionalParams; +@end + +NS_ASSUME_NONNULL_END diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m new file mode 100644 index 000000000..a533483f2 --- /dev/null +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m @@ -0,0 +1,290 @@ +/*! @file OIDTVAuthorizationRequestTests.m + @brief AppAuth iOS SDK + @copyright + Copyright 2020 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 "OIDTVAuthorizationRequestTests.h" +#import "OIDTVAuthorizationRequest.h" +#import "OIDTVServiceConfiguration.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDScopeUtilities.h" +#import "Source/AppAuthCore/OIDURLQueryComponent.h" +#endif + +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + +/*! @brief Test value for the @c TVAuthorizationEndpoint property. + */ +static NSString *const kTestTVAuthorizationEndpoint = @"https://www.example.com/device/code"; + +/*! @brief Test value for the @c tokenEndpoint property. + */ +static NSString *const kTestTokenEndpoint = @"https://www.example.com/token"; + +/*! @brief Test key for the @c additionalParameters property. + */ +static NSString *const kTestAdditionalParameterKey = @"A"; + +/*! @brief Test value for the @c additionalParameters property. + */ +static NSString *const kTestAdditionalParameterValue = @"1"; + +/*! @brief Test key for the @c clientID parameter in the HTTP request. + */ +static NSString *const kTestClientIDKey = @"client_id"; + +/*! @brief Test value for the @c clientID property. + */ +static NSString *const kTestClientID = @"ClientID"; + +/*! @brief Test value for the @c clientSecret property. + */ +static NSString *const kTestClientSecret = @"ClientSecret"; + +/*! @brief Test key for the @c scope parameter in the HTTP request. + */ +static NSString *const kTestScopeKey = @"scope"; + +/*! @brief Test value for the @c scope property. + */ +static NSString *const kTestScope = @"Scope"; + +/*! @brief Test value for the @c scope property. + */ +static NSString *const kTestScopeA = @"ScopeA"; + +/*! @brief Expected HTTP Method for the authorization @c URLRequest + */ +static NSString *const kHTTPPost = @"POST"; + +/*! @brief Expected @c ContentType header key for the authorization @c URLRequest + */ +static NSString *const kHTTPContentTypeHeaderKey = @"Content-Type"; + +/*! @brief Expected @c ContentType header value for the authorization @c URLRequest + */ +static NSString *const kHTTPContentTypeHeaderValue = + @"application/x-www-form-urlencoded; charset=UTF-8"; + +@implementation OIDTVAuthorizationRequestTests + +- (OIDTVServiceConfiguration *)testServiceConfiguration { + NSURL *tokenEndpoint = [NSURL URLWithString:kTestTokenEndpoint]; + NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTestTVAuthorizationEndpoint]; + + // Pass in an empty authorizationEndpoint since only the TVAuthorizationEndpoint and tokenEndpoint + // are used for the TV authentication flow. + OIDTVServiceConfiguration *configuration = [[OIDTVServiceConfiguration alloc] + initWithAuthorizationEndpoint:[[NSURL alloc] initWithString:@""] + TVAuthorizationEndpoint:TVAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; + return configuration; +} + +- (NSDictionary *)bodyParametersFromURLRequest:(NSURLRequest *)URLRequest { + NSString *bodyString = [[NSString alloc] initWithData:URLRequest.HTTPBody + encoding:NSUTF8StringEncoding]; + NSArray *bodyParameterStrings = [bodyString componentsSeparatedByString:@"&"]; + + NSMutableDictionary *bodyParameters = [[NSMutableDictionary alloc] init]; + + for (NSString *paramString in bodyParameterStrings) { + NSArray *components = [paramString componentsSeparatedByString:@"="]; + + if (components.count == 2) { + bodyParameters[components[0]] = components[1]; + } + } + + return bodyParameters; +} + +/*! @brief Tests the initializer + */ +- (void)testInitializer { + OIDTVServiceConfiguration *serviceConfiguration = [self testServiceConfiguration]; + NSArray *testScopes = @[ kTestScope, kTestScopeA ]; + NSString *testScopeString = [OIDScopeUtilities scopesWithArray:testScopes]; + NSDictionary *testAdditionalParameters = + @{kTestAdditionalParameterKey : kTestAdditionalParameterValue}; + + OIDTVAuthorizationRequest *authRequest = + [[OIDTVAuthorizationRequest alloc] initWithConfiguration:serviceConfiguration + clientId:kTestClientID + clientSecret:kTestClientSecret + scopes:testScopes + additionalParameters:testAdditionalParameters]; + + NSURL *authRequestTVAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)authRequest.configuration).TVAuthorizationEndpoint; + + XCTAssertEqualObjects(authRequest.clientID, kTestClientID); + XCTAssertEqualObjects(authRequest.clientSecret, kTestClientSecret); + XCTAssertEqualObjects(authRequest.scope, testScopeString); + XCTAssertEqualObjects(authRequest.additionalParameters, testAdditionalParameters); + XCTAssertEqualObjects(authRequest.responseType, OIDResponseTypeCode); + XCTAssertEqualObjects(authRequest.redirectURL, [[NSURL alloc] initWithString:@""]); + XCTAssertEqualObjects(authRequestTVAuthorizationEndpoint, + serviceConfiguration.TVAuthorizationEndpoint); +} + +/*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying + * process and checking to make sure the source and destination both contain the + * @c TVAuthorizationEndpoint + */ +- (void)testCopying { + OIDTVServiceConfiguration *serviceConfiguration = [self testServiceConfiguration]; + + OIDTVAuthorizationRequest *authRequest = + [[OIDTVAuthorizationRequest alloc] initWithConfiguration:serviceConfiguration + clientId:kTestClientID + clientSecret:kTestClientSecret + scopes:nil + additionalParameters:nil]; + + OIDTVAuthorizationRequest *authRequestCopy = [authRequest copy]; + NSURL *authRequestCopyTVAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)authRequestCopy.configuration).TVAuthorizationEndpoint; + + XCTAssertEqualObjects(authRequestCopyTVAuthorizationEndpoint, + serviceConfiguration.TVAuthorizationEndpoint); +} + +/*! @brief Tests the @c NSSecureCoding implementation by round-tripping an instance through the + * coding process and checking to make sure the source and destination both contain the + * @c TVAuthorizationEndpoint + */ +- (void)testSecureCoding { + OIDTVServiceConfiguration *serviceConfiguration = [self testServiceConfiguration]; + + OIDTVAuthorizationRequest *authRequest = + [[OIDTVAuthorizationRequest alloc] initWithConfiguration:serviceConfiguration + clientId:kTestClientID + clientSecret:kTestClientSecret + scopes:nil + additionalParameters:nil]; + + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:authRequest]; + OIDTVAuthorizationRequest *authRequestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + + NSURL *authRequestCopyTVAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)authRequestCopy.configuration).TVAuthorizationEndpoint; + + XCTAssertEqualObjects(authRequestCopyTVAuthorizationEndpoint, + serviceConfiguration.TVAuthorizationEndpoint); +} + +/*! @brief Tests the @c URLRequest method on a request with no scopes or additional parameters + */ +- (void)testURLRequestBasicClientAuth { + OIDTVServiceConfiguration *serviceConfiguration = [self testServiceConfiguration]; + + OIDTVAuthorizationRequest *authRequest = + [[OIDTVAuthorizationRequest alloc] initWithConfiguration:serviceConfiguration + clientId:kTestClientID + clientSecret:kTestClientSecret + scopes:nil + additionalParameters:nil]; + + NSURLRequest *URLRequest = [authRequest URLRequest]; + + XCTAssertEqualObjects(URLRequest.HTTPMethod, kHTTPPost); + XCTAssertEqualObjects([URLRequest valueForHTTPHeaderField:kHTTPContentTypeHeaderKey], + kHTTPContentTypeHeaderValue); + XCTAssertEqualObjects(URLRequest.URL, serviceConfiguration.TVAuthorizationEndpoint); + + NSDictionary *bodyParameters = + [self bodyParametersFromURLRequest:URLRequest]; + NSDictionary *expectedParameters = @{@"client_id" : kTestClientID}; + + XCTAssertEqualObjects(bodyParameters, expectedParameters); +} + +/*! @brief Tests the @c URLRequest method on a request with two scopes and no additional parameters + */ +- (void)testURLRequestScopes { + OIDTVServiceConfiguration *serviceConfiguration = [self testServiceConfiguration]; + NSArray *testScopes = @[ kTestScope, kTestScopeA ]; + NSString *testScopeString = [OIDScopeUtilities scopesWithArray:testScopes]; + NSString *testScopeStringPercentEncoded = [testScopeString + stringByAddingPercentEncodingWithAllowedCharacters:[OIDURLQueryComponent + URLParamValueAllowedCharacters]]; + + OIDTVAuthorizationRequest *authRequest = + [[OIDTVAuthorizationRequest alloc] initWithConfiguration:serviceConfiguration + clientId:kTestClientID + clientSecret:kTestClientSecret + scopes:@[ kTestScope, kTestScopeA ] + additionalParameters:nil]; + + NSURLRequest *URLRequest = [authRequest URLRequest]; + + XCTAssertEqualObjects([URLRequest HTTPMethod], kHTTPPost); + XCTAssertEqualObjects([URLRequest valueForHTTPHeaderField:kHTTPContentTypeHeaderKey], + kHTTPContentTypeHeaderValue); + XCTAssertEqualObjects(URLRequest.URL, serviceConfiguration.TVAuthorizationEndpoint); + + NSDictionary *bodyParameters = + [self bodyParametersFromURLRequest:URLRequest]; + NSDictionary *expectedParameters = + @{kTestClientIDKey : kTestClientID, kTestScopeKey : testScopeStringPercentEncoded}; + + XCTAssertEqualObjects(bodyParameters, expectedParameters); +} + +/*! @brief Tests the @c URLRequest method on a request with two scopes and one additional parameter + */ +- (void)testURLRequestAdditionalParams { + OIDTVServiceConfiguration *serviceConfiguration = [self testServiceConfiguration]; + NSArray *testScopes = @[ kTestScope, kTestScopeA ]; + NSString *testScopeString = [OIDScopeUtilities scopesWithArray:testScopes]; + NSString *testScopeStringPercentEncoded = [testScopeString + stringByAddingPercentEncodingWithAllowedCharacters:[OIDURLQueryComponent + URLParamValueAllowedCharacters]]; + OIDTVAuthorizationRequest *authRequest = [[OIDTVAuthorizationRequest alloc] + initWithConfiguration:serviceConfiguration + clientId:kTestClientID + clientSecret:kTestClientSecret + scopes:@[ kTestScope, kTestScopeA ] + additionalParameters:@{kTestAdditionalParameterKey : kTestAdditionalParameterValue}]; + + NSURLRequest *URLRequest = [authRequest URLRequest]; + + XCTAssertEqualObjects([URLRequest HTTPMethod], kHTTPPost); + XCTAssertEqualObjects([URLRequest valueForHTTPHeaderField:kHTTPContentTypeHeaderKey], + kHTTPContentTypeHeaderValue); + XCTAssertEqualObjects(URLRequest.URL, serviceConfiguration.TVAuthorizationEndpoint); + + NSDictionary *bodyParameters = + [self bodyParametersFromURLRequest:URLRequest]; + NSDictionary *expectedParameters = @{ + kTestClientIDKey : kTestClientID, + kTestScopeKey : testScopeStringPercentEncoded, + kTestAdditionalParameterKey : kTestAdditionalParameterValue + }; + + XCTAssertEqualObjects(bodyParameters, expectedParameters); +} + +@end + +#pragma GCC diagnostic pop From d08724f2b4ed71f2dfb69a7b8671d68282567e0b Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Wed, 29 Jul 2020 15:51:14 -0400 Subject: [PATCH 122/204] Add OIDTVTokenRequest class Adds OIDTVTokenRequest class, which is a subclass of OIDTokenRequest, to better adhere to the [RFC for TV authentication](https://tools.ietf.org/html/rfc8628#section-3.4). This adds the required `device_code` property which is sent in the request body, which differs from the `code` property for the authorization code in the parent class. Additionally, the grant type for TV authorization is hard-coded in, and several unused fields are dropped from the initializer and body request code. --- AppAuth.xcodeproj/project.pbxproj | 8 ++ Source/AppAuthCore/OIDTokenRequest.h | 5 + Source/AppAuthCore/OIDTokenRequest.m | 25 +++-- Source/AppAuthTV/OIDTVTokenRequest.h | 101 ++++++++++++++++++ Source/AppAuthTV/OIDTVTokenRequest.m | 152 +++++++++++++++++++++++++++ 5 files changed, 281 insertions(+), 10 deletions(-) create mode 100644 Source/AppAuthTV/OIDTVTokenRequest.h create mode 100644 Source/AppAuthTV/OIDTVTokenRequest.m diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index ca5c21628..2b793d994 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -155,6 +155,8 @@ 2D93865024B38840009A12D7 /* OIDTVAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AAD8249A87010059B5A4 /* OIDTVAuthorizationResponse.m */; }; 2D93865124B38840009A12D7 /* OIDTVAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AAE0249A87020059B5A4 /* OIDTVAuthorizationService.m */; }; 2D93865224B38840009A12D7 /* OIDTVServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */; }; + 2DEB065624CA1D9300DF47E7 /* OIDTVTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DEB065424CA1D9300DF47E7 /* OIDTVTokenRequest.h */; }; + 2DEB065724CA1D9300DF47E7 /* OIDTVTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB065524CA1D9300DF47E7 /* OIDTVTokenRequest.m */; }; 340DAE571D5821A100EC285B /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; 340DAE581D5821A100EC285B /* OIDExternalUserAgentMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentMac.m */; }; 340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */; }; @@ -770,6 +772,8 @@ 2D9385B724B37CAD009A12D7 /* AppAuthTV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuthTV.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2D9385B924B37CAD009A12D7 /* AppAuthTV.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppAuthTV.h; sourceTree = ""; }; 2D9385BA24B37CAD009A12D7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 2DEB065424CA1D9300DF47E7 /* OIDTVTokenRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDTVTokenRequest.h; sourceTree = ""; }; + 2DEB065524CA1D9300DF47E7 /* OIDTVTokenRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDTVTokenRequest.m; sourceTree = ""; }; 340DAE251D581FE700EC285B /* OIDAuthorizationService+Mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OIDAuthorizationService+Mac.h"; sourceTree = ""; }; 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OIDAuthorizationService+Mac.m"; sourceTree = ""; }; 340DAE271D581FE700EC285B /* OIDExternalUserAgentMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentMac.h; sourceTree = ""; }; @@ -1077,6 +1081,8 @@ 2D47AAE0249A87020059B5A4 /* OIDTVAuthorizationService.m */, 2D47AADE249A87020059B5A4 /* OIDTVServiceConfiguration.h */, 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */, + 2DEB065424CA1D9300DF47E7 /* OIDTVTokenRequest.h */, + 2DEB065524CA1D9300DF47E7 /* OIDTVTokenRequest.m */, ); path = AppAuthTV; sourceTree = ""; @@ -1415,6 +1421,7 @@ 2D93863524B38827009A12D7 /* OIDRegistrationRequest.h in Headers */, 2D93861D24B38815009A12D7 /* OIDAuthorizationService.h in Headers */, 2D93862B24B38825009A12D7 /* OIDExternalUserAgentRequest.h in Headers */, + 2DEB065624CA1D9300DF47E7 /* OIDTVTokenRequest.h in Headers */, 2D93861A24B3880B009A12D7 /* OIDAuthorizationRequest.h in Headers */, 2D9385DE24B3861E009A12D7 /* AppAuthTV.h in Headers */, 2D9385DF24B38646009A12D7 /* OIDTVAuthorizationRequest.h in Headers */, @@ -2244,6 +2251,7 @@ 2D93863624B38827009A12D7 /* OIDRegistrationRequest.m in Sources */, 2D93863224B38826009A12D7 /* OIDEndSessionRequest.m in Sources */, 2D93864424B38828009A12D7 /* OIDServiceDiscovery.m in Sources */, + 2DEB065724CA1D9300DF47E7 /* OIDTVTokenRequest.m in Sources */, 2D93861C24B38812009A12D7 /* OIDAuthorizationResponse.m in Sources */, 2D93863A24B38827009A12D7 /* OIDIDToken.m in Sources */, 2D93864F24B38840009A12D7 /* OIDTVAuthorizationRequest.m in Sources */, diff --git a/Source/AppAuthCore/OIDTokenRequest.h b/Source/AppAuthCore/OIDTokenRequest.h index 00e0c6e20..399294e8c 100644 --- a/Source/AppAuthCore/OIDTokenRequest.h +++ b/Source/AppAuthCore/OIDTokenRequest.h @@ -152,6 +152,11 @@ NS_ASSUME_NONNULL_BEGIN additionalParameters:(nullable NSDictionary *)additionalParameters NS_DESIGNATED_INITIALIZER; +/*! @brief Designated initializer for NSSecureCoding. + @param aDecoder Unarchiver object to decode + */ +- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; + /*! @brief Constructs an @c NSURLRequest representing the token request. @return An @c NSURLRequest representing the token request. */ diff --git a/Source/AppAuthCore/OIDTokenRequest.m b/Source/AppAuthCore/OIDTokenRequest.m index bd27dd480..5ed8a17ef 100644 --- a/Source/AppAuthCore/OIDTokenRequest.m +++ b/Source/AppAuthCore/OIDTokenRequest.m @@ -177,16 +177,21 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { 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]; + + 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]; + } return self; } diff --git a/Source/AppAuthTV/OIDTVTokenRequest.h b/Source/AppAuthTV/OIDTVTokenRequest.h new file mode 100644 index 000000000..f62911786 --- /dev/null +++ b/Source/AppAuthTV/OIDTVTokenRequest.h @@ -0,0 +1,101 @@ +/*! @file OIDTVTokenRequest.h + @brief AppAuth iOS SDK + @copyright + Copyright 2020 Google Inc. + @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 + +@class OIDServiceConfiguration; +@class OIDTVServiceConfiguration; + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDTVTokenRequest : OIDTokenRequest + +/*! @brief The device code received from the authorization server. + @remarks device_code + @see https://tools.ietf.org/html/rfc8628#section-3.4 + */ +@property(nonatomic, readonly) NSString *deviceCode; + +/*! @internal + @brief Unavailable. Please use + @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters: + or @c initWithCoder:. +*/ +- (instancetype)init NS_UNAVAILABLE; + +/*! @internal + @brief Unavailable. Please use + @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters: + or @c initWithCoder:. +*/ +- (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 *)scopes + refreshToken:(nullable NSString *)refreshToken + codeVerifier:(nullable NSString *)codeVerifier + additionalParameters: + (nullable NSDictionary *) + additionalParameters NS_UNAVAILABLE; + +/*! @internal + @brief Unavailable. Please use + @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters: + or @c initWithCoder:. +*/ +- (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 *) + additionalParameters NS_UNAVAILABLE; + +/*! @brief Designated initializer. + @param configuration The service's configuration. + @param deviceCode The device verification code received from the authorization server. + @param clientID The client identifier. + @param clientSecret The client secret (nullable). + @param additionalParameters The client's additional token request parameters. +*/ +- (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration + deviceCode:(NSString *)deviceCode + clientID:(NSString *)clientID + clientSecret:(nullable NSString *)clientSecret + additionalParameters:(nullable NSDictionary *) + additionalParameters + NS_DESIGNATED_INITIALIZER; + +/*! @brief Designated initializer for NSSecureCoding. + @param aDecoder Unarchiver object to decode + */ +- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthTV/OIDTVTokenRequest.m b/Source/AppAuthTV/OIDTVTokenRequest.m new file mode 100644 index 000000000..88874a817 --- /dev/null +++ b/Source/AppAuthTV/OIDTVTokenRequest.m @@ -0,0 +1,152 @@ +/*! @file OIDTVTokenRequest.m + @brief AppAuth iOS SDK + @copyright + Copyright 2020 Google Inc. + @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 "OIDTVTokenRequest.h" + +#import "OIDDefines.h" +#import "OIDTVServiceConfiguration.h" +#import "OIDURLQueryComponent.h" + +/*! @brief The key for the @c deviceCode property for @c NSSecureCoding and request body. + */ +static NSString *const kDeviceCodeKey = @"device_code"; + +/*! @brief Key used to encode the @c grantType property for @c NSSecureCoding and request body. + */ +static NSString *const kGrantTypeKey = @"grant_type"; + +/*! @brief Value for @c grant_type key in the request body + @see https://tools.ietf.org/html/rfc8628#section-3.4 + */ +static NSString *const kOIDTVDeviceTokenGrantType = @"urn:ietf:params:oauth:grant-type:device_code"; + +@implementation OIDTVTokenRequest + +- (instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector + (initWithConfiguration: + deviceCode: + clientID: + clientSecret: + 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 *)scopes + refreshToken:(nullable NSString *)refreshToken + codeVerifier:(nullable NSString *)codeVerifier + additionalParameters: + (nullable NSDictionary *)additionalParameters + OID_UNAVAILABLE_USE_INITIALIZER(@selector + (initWithConfiguration: + deviceCode: + clientID: + clientSecret: + 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 *)additionalParameters + OID_UNAVAILABLE_USE_INITIALIZER(@selector + (initWithConfiguration: + deviceCode: + clientID: + clientSecret: + additionalParameters: + )) + +- (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration + deviceCode:(NSString *)deviceCode + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret + additionalParameters:(NSDictionary *)additionalParameters { + self = [super initWithConfiguration:configuration + grantType:kOIDTVDeviceTokenGrantType + authorizationCode:nil + redirectURL:[[NSURL alloc] initWithString:@""] + clientID:clientID + clientSecret:clientSecret + scope:nil + refreshToken:nil + codeVerifier:nil + additionalParameters:additionalParameters]; + + if (self) { + _deviceCode = [deviceCode copy]; + } + return self; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(nullable NSZone *)zone { + // The documentation for NSCopying specifically advises us to return a reference to the original + // instance in the case where instances are immutable (as ours is): + // "Implement NSCopying by retaining the original instead of creating a new copy when the class + // and its contents are immutable." + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + NSString *deviceCode = [aDecoder decodeObjectOfClass:[NSString class] forKey:kDeviceCodeKey]; + _deviceCode = deviceCode; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [super encodeWithCoder:aCoder]; + [aCoder encodeObject:_deviceCode forKey:kDeviceCodeKey]; +} + +- (OIDURLQueryComponent *)tokenRequestBody { + OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init]; + + if (self.grantType) { + [query addParameter:kGrantTypeKey value:self.grantType]; + } + + [query addParameter:kDeviceCodeKey value:self.deviceCode]; + + [query addParameters:self.additionalParameters]; + + return query; +} + +@end From da5642aad73ab6461ecb1dfd02cc7301879ca308 Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Fri, 31 Jul 2020 14:58:23 -0400 Subject: [PATCH 123/204] Add OIDTVTokenRequest tests --- AppAuth.xcodeproj/project.pbxproj | 8 + UnitTests/AppAuthTV/OIDTVTokenRequestTests.h | 51 +++++ UnitTests/AppAuthTV/OIDTVTokenRequestTests.m | 194 +++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 UnitTests/AppAuthTV/OIDTVTokenRequestTests.h create mode 100644 UnitTests/AppAuthTV/OIDTVTokenRequestTests.m diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 2b793d994..555dc647c 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -157,6 +157,8 @@ 2D93865224B38840009A12D7 /* OIDTVServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */; }; 2DEB065624CA1D9300DF47E7 /* OIDTVTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DEB065424CA1D9300DF47E7 /* OIDTVTokenRequest.h */; }; 2DEB065724CA1D9300DF47E7 /* OIDTVTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB065524CA1D9300DF47E7 /* OIDTVTokenRequest.m */; }; + 2DEB066124CF5CE000DF47E7 /* OIDTVTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */; }; + 2DEB066224CF5CFB00DF47E7 /* OIDTVTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */; }; 340DAE571D5821A100EC285B /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; 340DAE581D5821A100EC285B /* OIDExternalUserAgentMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentMac.m */; }; 340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */; }; @@ -774,6 +776,8 @@ 2D9385BA24B37CAD009A12D7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2DEB065424CA1D9300DF47E7 /* OIDTVTokenRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDTVTokenRequest.h; sourceTree = ""; }; 2DEB065524CA1D9300DF47E7 /* OIDTVTokenRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDTVTokenRequest.m; sourceTree = ""; }; + 2DEB065F24CF5CDF00DF47E7 /* OIDTVTokenRequestTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVTokenRequestTests.h; sourceTree = ""; }; + 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVTokenRequestTests.m; sourceTree = ""; }; 340DAE251D581FE700EC285B /* OIDAuthorizationService+Mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OIDAuthorizationService+Mac.h"; sourceTree = ""; }; 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OIDAuthorizationService+Mac.m"; sourceTree = ""; }; 340DAE271D581FE700EC285B /* OIDExternalUserAgentMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentMac.h; sourceTree = ""; }; @@ -1092,6 +1096,8 @@ children = ( 2D81120324C1036700984DA7 /* OIDTVAuthorizationRequestTests.h */, 2D81120024C1036700984DA7 /* OIDTVAuthorizationRequestTests.m */, + 2DEB065F24CF5CDF00DF47E7 /* OIDTVTokenRequestTests.h */, + 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */, ); path = AppAuthTV; sourceTree = ""; @@ -2184,6 +2190,7 @@ files = ( 2D81120C24C103F300984DA7 /* OIDScopesTests.m in Sources */, 2D81121224C103F300984DA7 /* OIDURLQueryComponentTests.m in Sources */, + 2DEB066224CF5CFB00DF47E7 /* OIDTVTokenRequestTests.m in Sources */, 2D81120D24C103F300984DA7 /* OIDServiceConfigurationTests.m in Sources */, 2D81121324C103F300984DA7 /* OIDURLQueryComponentTestsIOS7.m in Sources */, 2D81121524C103F300984DA7 /* OIDRegistrationResponseTests.m in Sources */, @@ -2340,6 +2347,7 @@ 340DAECB1D582DE100EC285B /* OIDAuthorizationService+IOS.m in Sources */, 341741E31C5D8243000EF209 /* OIDResponseTypes.m in Sources */, 341741E41C5D8243000EF209 /* OIDScopes.m in Sources */, + 2DEB066124CF5CE000DF47E7 /* OIDTVTokenRequestTests.m in Sources */, 341741E71C5D8243000EF209 /* OIDServiceDiscovery.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.h b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.h new file mode 100644 index 000000000..1f6eecbdc --- /dev/null +++ b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.h @@ -0,0 +1,51 @@ +/*! @file OIDTVTokenRequestTests.h + @brief AppAuth iOS SDK + @copyright + Copyright 2020 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 + +@class OIDTVTokenRequest; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Unit tests for @c OIDTVTokenRequest. + */ +@interface OIDTVTokenRequestTests : XCTestCase + +/*! @brief Tests the initializer +*/ +- (void)testInitializer; + +/*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying + * process and checking to make sure the source and destination both contain the @c deviceCode. + */ +- (void)testCopying; + +/*! @brief Tests the @c NSSecureCoding implementation by round-tripping an instance through the + * coding process and checking to make sure the source and destination both contain the + * @c deviceCode + */ +- (void)testSecureCoding; + +/*! @brief Tests the @c URLRequest method to verify that the body parameters include the correct + * grant type, device code and additional parameters. + */ +- (void)testURLRequest; + +@end + +NS_ASSUME_NONNULL_END diff --git a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m new file mode 100644 index 000000000..2ccb524c8 --- /dev/null +++ b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m @@ -0,0 +1,194 @@ +/*! @file OIDTVTokenRequestTests.m + @brief AppAuth iOS SDK + @copyright + Copyright 2020 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 "OIDTVTokenRequestTests.h" + +#if SWIFT_PACKAGE +@import AppAuthTV; +#else +#import "Source/AppAuthCore/OIDScopeUtilities.h" +#import "Source/AppAuthTV/OIDTVAuthorizationRequest.h" +#import "Source/AppAuthTV/OIDTVAuthorizationResponse.h" +#import "Source/AppAuthTV/OIDTVServiceConfiguration.h" +#import "Source/AppAuthTV/OIDTVTokenRequest.h" +#endif + +// Ignore warnings about "Use of GNU statement expression extension" which is +// raised by our use of the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + +/*! @brief Test value for the @c TVAuthorizationEndpoint property. + */ +static NSString *const kTestTVAuthorizationEndpoint = + @"https://www.example.com/device/code"; + +/*! @brief Test value for the @c tokenEndpoint property. + */ +static NSString *const kTestTokenEndpoint = @"https://www.example.com/token"; + +/*! @brief Test key for the @c additionalParameters property. + */ +static NSString *const kTestAdditionalParameterKey = @"A"; + +/*! @brief Test value for the @c additionalParameters property. + */ +static NSString *const kTestAdditionalParameterValue = @"1"; + +/*! @brief Test key for the @c clientID parameter in the HTTP request. + */ +static NSString *const kTestClientIDKey = @"client_id"; + +/*! @brief Test value for the @c clientID property. + */ +static NSString *const kTestClientID = @"ClientID"; + +/*! @brief Test value for the @c clientSecret property. + */ +static NSString *const kTestClientSecret = @"ClientSecret"; + +/*! @brief Key for the @c deviceCode property for @c NSSecureCoding and the HTTP request body. + */ +static NSString *const kDeviceCodeKey = @"device_code"; + +/*! @brief Value for the @c deviceCode key in the HTTP request body. + */ +static NSString *const kDeviceCodeValue = @"DeviceCode"; + +/*! @brief Key for the @c grantType property for @c NSSecureCoding and the HTTP request body. + */ +static NSString *const kGrantTypeKey = @"grant_type"; + +/*! @brief Value for the @c grant_type key in the HTTP request body + * @see https://tools.ietf.org/html/rfc8628#section-3.4 + */ +static NSString *const kOIDTVDeviceTokenGrantType = + @"urn:ietf:params:oauth:grant-type:device_code"; + +@implementation OIDTVTokenRequestTests + +- (NSDictionary *)bodyParametersFromURLRequest: + (NSURLRequest *)URLRequest { + NSString *bodyString = [[NSString alloc] initWithData:URLRequest.HTTPBody + encoding:NSUTF8StringEncoding]; + NSArray *bodyParameterStrings = + [bodyString componentsSeparatedByString:@"&"]; + + NSMutableDictionary *bodyParameters = + [[NSMutableDictionary alloc] init]; + + for (NSString *paramString in bodyParameterStrings) { + NSArray *components = + [paramString componentsSeparatedByString:@"="]; + + if (components.count == 2) { + bodyParameters[components[0]] = components[1]; + } + } + + return bodyParameters; +} + +- (OIDTVServiceConfiguration *)testServiceConfiguration { + NSURL *tokenEndpoint = [NSURL URLWithString:kTestTokenEndpoint]; + NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTestTVAuthorizationEndpoint]; + + // Pass in an empty authorizationEndpoint since only the + // TVAuthorizationEndpoint and tokenEndpoint are used for the TV + // authentication flow. + OIDTVServiceConfiguration *configuration = [[OIDTVServiceConfiguration alloc] + initWithAuthorizationEndpoint:[[NSURL alloc] initWithString:@""] + TVAuthorizationEndpoint:TVAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; + return configuration; +} + +- (OIDTVTokenRequest *)testTokenRequest { + OIDTVServiceConfiguration *service = [self testServiceConfiguration]; + return [[OIDTVTokenRequest alloc] + initWithConfiguration:service + deviceCode:kDeviceCodeValue + clientID:kTestClientID + clientSecret:kTestClientSecret + additionalParameters:@{kTestAdditionalParameterKey : kTestAdditionalParameterValue}]; +} + +/*! @brief Tests the initializer +*/ +- (void)testInitializer { + OIDTVTokenRequest *request = [self testTokenRequest]; + NSURL *requestTVAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)request.configuration).TVAuthorizationEndpoint; + + XCTAssertEqualObjects(requestTVAuthorizationEndpoint, + [self testServiceConfiguration].TVAuthorizationEndpoint); + XCTAssertEqualObjects(request.deviceCode, kDeviceCodeValue); + XCTAssertEqualObjects(request.grantType, kOIDTVDeviceTokenGrantType); + XCTAssertEqualObjects(request.clientID, kTestClientID); + XCTAssertEqualObjects(request.clientSecret, kTestClientSecret); + XCTAssertEqualObjects(request.additionalParameters, + @{kTestAdditionalParameterKey:kTestAdditionalParameterValue}); +} + +/*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying + * process and checking to make sure the source and destination both contain the @c deviceCode. + */ +- (void)testCopying { + OIDTVTokenRequest *request = [self testTokenRequest]; + OIDTVTokenRequest *requestCopy = [request copy]; + + XCTAssertEqualObjects(requestCopy.deviceCode, request.deviceCode); +} + +/*! @brief Tests the @c NSSecureCoding implementation by round-tripping an instance through the + * coding process and checking to make sure the source and destination both contain the + * @c deviceCode + */ +- (void)testSecureCoding { + OIDTVTokenRequest *request = [self testTokenRequest]; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; + OIDTVTokenRequest *requestDecoded = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + XCTAssertEqualObjects(requestDecoded.deviceCode, request.deviceCode); +} + +/*! @brief Tests the @c URLRequest method to verify that the body parameters include the correct + * grant type, device code and additional parameters. + */ +- (void)testURLRequest { + OIDTVTokenRequest *request = [self testTokenRequest]; + + NSURLRequest *URLRequest = [request URLRequest]; + + NSDictionary *bodyParameters = + [self bodyParametersFromURLRequest:URLRequest]; + + // Since clientSecret is present, we will not need to check for client_id + // as that will be passed in using HTTP Basic Authentication + + NSDictionary *expectedParameters = @{ + kGrantTypeKey : kOIDTVDeviceTokenGrantType, + kDeviceCodeKey : kDeviceCodeValue, + kTestAdditionalParameterKey : kTestAdditionalParameterValue + }; + + XCTAssertEqualObjects(bodyParameters, expectedParameters); +} + +@end + +#pragma GCC diagnostic pop From 85cb334859a687b7aa2f672e211a4064d0b1f5c6 Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Wed, 5 Aug 2020 14:44:03 -0400 Subject: [PATCH 124/204] Update for RFC compliance, use new OIDTVTokenRequest class --- AppAuth.podspec | 2 +- Source/AppAuthTV/OIDTVAuthorizationRequest.m | 4 +- Source/AppAuthTV/OIDTVAuthorizationResponse.h | 32 +++++----- Source/AppAuthTV/OIDTVAuthorizationResponse.m | 59 +++++++++++-------- Source/AppAuthTV/OIDTVAuthorizationService.h | 2 +- Source/AppAuthTV/OIDTVAuthorizationService.m | 11 +++- Source/AppAuthTV/OIDTVServiceConfiguration.h | 6 +- Source/AppAuthTV/OIDTVServiceConfiguration.m | 14 ++--- Source/AppAuthTV/OIDTVTokenRequest.h | 12 ++-- .../OIDTVAuthorizationRequestTests.m | 9 +-- UnitTests/AppAuthTV/OIDTVTokenRequestTests.m | 10 +--- 11 files changed, 86 insertions(+), 75 deletions(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index c34fc79f3..d4191ad79 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -70,7 +70,7 @@ It follows the OAuth 2.0 for Native Apps best current practice # Subspec for the full AppAuth library, including platform-dependant external user agents. s.subspec 'TV' do |tv| - tv.source_files = "Source/AppAuthTV/*.{h,m}" + tv.source_files = "Source/AppAuthTV.h", "Source/AppAuthTV/*.{h,m}" tv.dependency 'AppAuth/Core' end diff --git a/Source/AppAuthTV/OIDTVAuthorizationRequest.m b/Source/AppAuthTV/OIDTVAuthorizationRequest.m index 4afae8cd8..5d085beb2 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationRequest.m +++ b/Source/AppAuthTV/OIDTVAuthorizationRequest.m @@ -66,9 +66,9 @@ @implementation OIDTVAuthorizationRequest clientId:clientID clientSecret:clientSecret scopes:scopes - redirectURL:[[NSURL alloc] init] + redirectURL:[[NSURL alloc] initWithString:@""] responseType:OIDResponseTypeCode - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters]; } #pragma mark - NSObject overrides diff --git a/Source/AppAuthTV/OIDTVAuthorizationResponse.h b/Source/AppAuthTV/OIDTVAuthorizationResponse.h index cb4793c59..d3bed1e97 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationResponse.h +++ b/Source/AppAuthTV/OIDTVAuthorizationResponse.h @@ -21,27 +21,27 @@ #import "OIDAuthorizationResponse.h" @class OIDTVAuthorizationRequest; -@class OIDTokenRequest; +@class OIDTVTokenRequest; NS_ASSUME_NONNULL_BEGIN -/*! @brief The @c grant_type value for the the TV authorization flow. - @see https://tools.ietf.org/html/rfc8628#section-3.4 - */ -extern NSString *const OIDTVDeviceTokenGrantType; - /*! @brief Represents the response to a TV authorization request. @see https://tools.ietf.org/html/rfc8628#section-3.5 */ @interface OIDTVAuthorizationResponse : OIDAuthorizationResponse -/*! @brief The verification URL that should be displayed to the user instructing them to visit the - URL and enter the code. - @remarks verification_url +/*! @brief The verification URI that should be displayed to the user instructing them to visit the + URI and enter the code. + @remarks verification_uri + */ +@property(nonatomic, readonly, nullable) NSString *verificationURI; + +/*! @brief A complete verification URI to allow for verification without entering the user code. + @remarks verification_uri */ -@property(nonatomic, readonly, nullable) NSString *verificationURL; +@property(nonatomic, readonly, nullable) NSString *verificationURIComplete; -/*! @brief The code that should be displayed to the user which they enter at the @c verificationURL. +/*! @brief The code that should be displayed to the user which they enter at the @c verificationURI. @remarks user_code */ @property(nonatomic, readonly, nullable) NSString *userCode; @@ -70,21 +70,21 @@ extern NSString *const OIDTVDeviceTokenGrantType; @c #additionalParameters dictionary. */ - (instancetype)initWithRequest:(OIDTVAuthorizationRequest *)request - parameters:(NSDictionary *> *)parameters + parameters:(NSDictionary *> *)parameters NS_DESIGNATED_INITIALIZER; /*! @brief Creates a token request suitable for polling the token endpoint with the @c deviceCode. - @return A @c OIDTokenRequest suitable for polling the token endpoint. + @return A @c OIDTVTokenRequest suitable for polling the token endpoint. @see https://tools.ietf.org/html/rfc8628#section-3.4 */ -- (nullable OIDTokenRequest *)tokenPollRequest; +- (nullable OIDTVTokenRequest *)tokenPollRequest; /*! @brief Creates a token request suitable for polling the token endpoint with the @c deviceCode. @param additionalParameters Additional parameters for the token request. - @return A @c OIDTokenRequest suitable for polling the token endpoint. + @return A @c OIDTVTokenRequest suitable for polling the token endpoint. @see https://tools.ietf.org/html/rfc8628#section-3.4 */ -- (nullable OIDTokenRequest *)tokenPollRequestWithAdditionalParameters: +- (nullable OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: (nullable NSDictionary *)additionalParameters; @end diff --git a/Source/AppAuthTV/OIDTVAuthorizationResponse.m b/Source/AppAuthTV/OIDTVAuthorizationResponse.m index 6a0110baa..71b9e8f04 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationResponse.m +++ b/Source/AppAuthTV/OIDTVAuthorizationResponse.m @@ -20,16 +20,26 @@ #import "OIDDefines.h" #import "OIDFieldMapping.h" -#import "OIDTokenRequest.h" +#import "OIDTVTokenRequest.h" #import "OIDTVAuthorizationRequest.h" -NSString *const OIDTVDeviceTokenGrantType = @"http://oauth.net/grant_type/device/1.0"; +/*! @brief The key for the @c verificationURI property in the incoming parameters and for + @c NSSecureCoding. + */ +static NSString *const kVerificationURIKey = @"verification_uri"; -/*! @brief The key for the @c verificationURL property in the incoming parameters and for +/*! @brief An alternative key for the @c verificationURI property in the incoming parameters and for + @c NSSecureCoding. If "verification_uri" is not found in the response, a "verification_url" + key is considered equivalent. This is included for compatibility with legacy implementations + and should ideally be removed in the future. + */ +static NSString *const kVerificationURIAlternativeKey = @"verification_url"; + +/*! @brief The key for the @c verificationURIComplete property in the incoming parameters and for @c NSSecureCoding. */ -static NSString *const kVerificationURLKey = @"verification_url"; +static NSString *const kVerificationURICompleteKey = @"verification_uri_complete"; /*! @brief The key for the @c userCode property in the incoming parameters and for @c NSSecureCoding. @@ -69,8 +79,10 @@ @implementation OIDTVAuthorizationResponse static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ fieldMap = [NSMutableDictionary dictionary]; - fieldMap[kVerificationURLKey] = - [[OIDFieldMapping alloc] initWithName:@"_verificationURL" type:[NSString class]]; + fieldMap[kVerificationURIKey] = + [[OIDFieldMapping alloc] initWithName:@"_verificationURI" type:[NSString class]]; + fieldMap[kVerificationURICompleteKey] = + [[OIDFieldMapping alloc] initWithName:@"_verificationURIComplete" type:[NSString class]]; fieldMap[kUserCodeKey] = [[OIDFieldMapping alloc] initWithName:@"_userCode" type:[NSString class]]; fieldMap[kDeviceCodeKey] = @@ -87,6 +99,11 @@ @implementation OIDTVAuthorizationResponse }]; fieldMap[kIntervalKey] = [[OIDFieldMapping alloc] initWithName:@"_interval" type:[NSNumber class]]; + + // Map the alternative verification URI key to "_verificationURI" to support legacy + // implementations using the alternative key + fieldMap[kVerificationURIAlternativeKey] = + [[OIDFieldMapping alloc] initWithName:@"_verificationURI" type:[NSString class]]; }); return fieldMap; } @@ -94,7 +111,7 @@ @implementation OIDTVAuthorizationResponse #pragma mark - Initializers - (instancetype)initWithRequest:(OIDTVAuthorizationRequest *)request - parameters:(NSDictionary *> *)parameters { + parameters:(NSDictionary *> *)parameters { self = [super initWithRequest:request parameters:parameters]; return self; } @@ -112,13 +129,15 @@ - (instancetype)copyWithZone:(nullable NSZone *)zone { #pragma mark - NSObject overrides - (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p, verificationURL: %@, userCode: \"%@\", deviceCode: " + return [NSString stringWithFormat:@"<%@: %p, verificationURI: %@, verificationURIComplete: %@, " + "userCode: \"%@\", deviceCode: " "\"%@\", interval: %@, expirationDate: %@, " "additionalParameters: %@, " "request: %@>", NSStringFromClass([self class]), (void *)self, - _verificationURL, + _verificationURI, + _verificationURIComplete, _userCode, _deviceCode, _interval, @@ -129,24 +148,18 @@ - (NSString *)description { #pragma mark - -- (OIDTokenRequest *)tokenPollRequest { +- (OIDTVTokenRequest *)tokenPollRequest { return [self tokenPollRequestWithAdditionalParameters:nil]; } -- (OIDTokenRequest *)tokenPollRequestWithAdditionalParameters: +- (OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: (NSDictionary *)additionalParameters { - OIDTokenRequest *pollRequest = - [[OIDTokenRequest alloc] initWithConfiguration:self.request.configuration - grantType:OIDTVDeviceTokenGrantType - authorizationCode:_deviceCode - redirectURL:[[NSURL alloc] init] - clientID:self.request.clientID - clientSecret:self.request.clientSecret - scopes:nil - refreshToken:nil - codeVerifier:nil - additionalParameters:nil]; - return pollRequest; + return [[OIDTVTokenRequest alloc] + initWithConfiguration:(OIDTVServiceConfiguration *)self.request.configuration + deviceCode:_deviceCode + clientID:self.request.clientID + clientSecret:self.request.clientSecret + additionalParameters:additionalParameters]; } @end diff --git a/Source/AppAuthTV/OIDTVAuthorizationService.h b/Source/AppAuthTV/OIDTVAuthorizationService.h index 14027bc09..2a9bfe571 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationService.h +++ b/Source/AppAuthTV/OIDTVAuthorizationService.h @@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN /*! @brief The block that is called when the TV authorization has initialized. @param response The authorization response, or nil if there was an error. Display - @c OIDTVAuthorizationResponse.userCode and @c OIDTVAuthorizationResponse.verificationURL to + @c OIDTVAuthorizationResponse.userCode and @c OIDTVAuthorizationResponse.verificationURI to the user so they can action the request. @param error The error if an error occurred. */ diff --git a/Source/AppAuthTV/OIDTVAuthorizationService.m b/Source/AppAuthTV/OIDTVAuthorizationService.m index fed16a938..944d722df 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationService.m +++ b/Source/AppAuthTV/OIDTVAuthorizationService.m @@ -27,6 +27,7 @@ #import "OIDTVAuthorizationRequest.h" #import "OIDTVAuthorizationResponse.h" #import "OIDTVServiceConfiguration.h" +#import "OIDTVTokenRequest.h" /*! @brief The authorization pending error code. @see https://tools.ietf.org/html/rfc8628#section-3.5 @@ -159,11 +160,19 @@ + (OIDTVAuthorizationCancelBlock)authorizeTVRequest:(OIDTVAuthorizationRequest * }); // Creates the token request that will be used to poll the token endpoint. - OIDTokenRequest *pollRequest = [TVAuthorizationResponse tokenPollRequest]; + OIDTVTokenRequest *pollRequest = [TVAuthorizationResponse tokenPollRequest]; // Starting polling interval (may be increased if a slow down message is received). __block NSTimeInterval interval = [TVAuthorizationResponse.interval doubleValue]; + // If no interval is set, use default value of 5 as per RFC. + // If interval is set to 0, use value of 1 to prevent infinite polling. + if (TVAuthorizationResponse.interval == nil) { + interval = 5.0; + } else if (interval == 0.0) { + interval = 1.0; + } + // Polls the token endpoint until the authorization completes or expires. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ do { diff --git a/Source/AppAuthTV/OIDTVServiceConfiguration.h b/Source/AppAuthTV/OIDTVServiceConfiguration.h index 2b6b29093..b3d60e748 100644 --- a/Source/AppAuthTV/OIDTVServiceConfiguration.h +++ b/Source/AppAuthTV/OIDTVServiceConfiguration.h @@ -48,13 +48,11 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *)discoveryDocument NS_UNAVAILABLE; /*! @brief Designated initializer. - @param authorizationEndpoint The authorization endpoint URI. @param TVAuthorizationEndpoint The TV authorization endpoint URI. @param tokenEndpoint The token exchange and refresh endpoint URI. */ -- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint - TVAuthorizationEndpoint:(NSURL *)TVAuthorizationEndpoint - tokenEndpoint:(NSURL *)tokenEndpoint NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithTVAuthorizationEndpoint:(NSURL *)TVAuthorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint NS_DESIGNATED_INITIALIZER; @end diff --git a/Source/AppAuthTV/OIDTVServiceConfiguration.m b/Source/AppAuthTV/OIDTVServiceConfiguration.m index edd53962c..0198475c3 100644 --- a/Source/AppAuthTV/OIDTVServiceConfiguration.m +++ b/Source/AppAuthTV/OIDTVServiceConfiguration.m @@ -38,18 +38,16 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIAL @implementation OIDTVServiceConfiguration - (instancetype)init - OID_UNAVAILABLE_USE_INITIALIZER( - @selector(initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint:)) + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithTVAuthorizationEndpoint:tokenEndpoint:)) - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint - OID_UNAVAILABLE_USE_INITIALIZER( - @selector(initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint:)) + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithTVAuthorizationEndpoint:tokenEndpoint:)) -- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint - TVAuthorizationEndpoint:(NSURL *)TVAuthorizationEndpoint - tokenEndpoint:(NSURL *)tokenEndpoint { - self = [super initWithAuthorizationEndpoint:authorizationEndpoint tokenEndpoint:tokenEndpoint]; +- (instancetype)initWithTVAuthorizationEndpoint:(NSURL *)TVAuthorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint { + self = [super initWithAuthorizationEndpoint:[[NSURL alloc] initWithString:@""] + tokenEndpoint:tokenEndpoint]; if (self) { _TVAuthorizationEndpoint = [TVAuthorizationEndpoint copy]; } diff --git a/Source/AppAuthTV/OIDTVTokenRequest.h b/Source/AppAuthTV/OIDTVTokenRequest.h index f62911786..5a81c7434 100644 --- a/Source/AppAuthTV/OIDTVTokenRequest.h +++ b/Source/AppAuthTV/OIDTVTokenRequest.h @@ -55,8 +55,8 @@ NS_ASSUME_NONNULL_BEGIN refreshToken:(nullable NSString *)refreshToken codeVerifier:(nullable NSString *)codeVerifier additionalParameters: - (nullable NSDictionary *) - additionalParameters NS_UNAVAILABLE; + (nullable NSDictionary *)additionalParameters + NS_UNAVAILABLE; /*! @internal @brief Unavailable. Please use @@ -73,8 +73,8 @@ NS_ASSUME_NONNULL_BEGIN refreshToken:(nullable NSString *)refreshToken codeVerifier:(nullable NSString *)codeVerifier additionalParameters: - (nullable NSDictionary *) - additionalParameters NS_UNAVAILABLE; + (nullable NSDictionary *)additionalParameters + NS_UNAVAILABLE; /*! @brief Designated initializer. @param configuration The service's configuration. @@ -87,8 +87,8 @@ NS_ASSUME_NONNULL_BEGIN deviceCode:(NSString *)deviceCode clientID:(NSString *)clientID clientSecret:(nullable NSString *)clientSecret - additionalParameters:(nullable NSDictionary *) - additionalParameters + additionalParameters: + (nullable NSDictionary *)additionalParameters NS_DESIGNATED_INITIALIZER; /*! @brief Designated initializer for NSSecureCoding. diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m index a533483f2..db9655506 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m @@ -91,12 +91,9 @@ - (OIDTVServiceConfiguration *)testServiceConfiguration { NSURL *tokenEndpoint = [NSURL URLWithString:kTestTokenEndpoint]; NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTestTVAuthorizationEndpoint]; - // Pass in an empty authorizationEndpoint since only the TVAuthorizationEndpoint and tokenEndpoint - // are used for the TV authentication flow. - OIDTVServiceConfiguration *configuration = [[OIDTVServiceConfiguration alloc] - initWithAuthorizationEndpoint:[[NSURL alloc] initWithString:@""] - TVAuthorizationEndpoint:TVAuthorizationEndpoint - tokenEndpoint:tokenEndpoint]; + OIDTVServiceConfiguration *configuration = + [[OIDTVServiceConfiguration alloc] initWithTVAuthorizationEndpoint:TVAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; return configuration; } diff --git a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m index 2ccb524c8..eecb48d35 100644 --- a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m +++ b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m @@ -108,13 +108,9 @@ - (OIDTVServiceConfiguration *)testServiceConfiguration { NSURL *tokenEndpoint = [NSURL URLWithString:kTestTokenEndpoint]; NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTestTVAuthorizationEndpoint]; - // Pass in an empty authorizationEndpoint since only the - // TVAuthorizationEndpoint and tokenEndpoint are used for the TV - // authentication flow. - OIDTVServiceConfiguration *configuration = [[OIDTVServiceConfiguration alloc] - initWithAuthorizationEndpoint:[[NSURL alloc] initWithString:@""] - TVAuthorizationEndpoint:TVAuthorizationEndpoint - tokenEndpoint:tokenEndpoint]; + OIDTVServiceConfiguration *configuration = + [[OIDTVServiceConfiguration alloc] initWithTVAuthorizationEndpoint:TVAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; return configuration; } From 6e89dbabcc071b4ca4a52d22ac253bca0270f975 Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Fri, 7 Aug 2020 13:45:54 -0400 Subject: [PATCH 125/204] Add OIDTVAuthorizationResponse tests --- AppAuth.xcodeproj/project.pbxproj | 6 + .../OIDTVAuthorizationRequestTests.m | 6 +- .../OIDTVAuthorizationResponseTests.h | 59 ++++ .../OIDTVAuthorizationResponseTests.m | 285 ++++++++++++++++++ 4 files changed, 353 insertions(+), 3 deletions(-) create mode 100644 UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h create mode 100644 UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 555dc647c..27449610b 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -155,6 +155,7 @@ 2D93865024B38840009A12D7 /* OIDTVAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AAD8249A87010059B5A4 /* OIDTVAuthorizationResponse.m */; }; 2D93865124B38840009A12D7 /* OIDTVAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AAE0249A87020059B5A4 /* OIDTVAuthorizationService.m */; }; 2D93865224B38840009A12D7 /* OIDTVServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */; }; + 2DA8D82624C6190400FDFB34 /* OIDTVAuthorizationResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DA8D82424C6190300FDFB34 /* OIDTVAuthorizationResponseTests.m */; }; 2DEB065624CA1D9300DF47E7 /* OIDTVTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DEB065424CA1D9300DF47E7 /* OIDTVTokenRequest.h */; }; 2DEB065724CA1D9300DF47E7 /* OIDTVTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB065524CA1D9300DF47E7 /* OIDTVTokenRequest.m */; }; 2DEB066124CF5CE000DF47E7 /* OIDTVTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */; }; @@ -774,6 +775,8 @@ 2D9385B724B37CAD009A12D7 /* AppAuthTV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuthTV.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2D9385B924B37CAD009A12D7 /* AppAuthTV.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppAuthTV.h; sourceTree = ""; }; 2D9385BA24B37CAD009A12D7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 2DA8D82424C6190300FDFB34 /* OIDTVAuthorizationResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVAuthorizationResponseTests.m; sourceTree = ""; }; + 2DA8D82524C6190400FDFB34 /* OIDTVAuthorizationResponseTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVAuthorizationResponseTests.h; sourceTree = ""; }; 2DEB065424CA1D9300DF47E7 /* OIDTVTokenRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDTVTokenRequest.h; sourceTree = ""; }; 2DEB065524CA1D9300DF47E7 /* OIDTVTokenRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDTVTokenRequest.m; sourceTree = ""; }; 2DEB065F24CF5CDF00DF47E7 /* OIDTVTokenRequestTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVTokenRequestTests.h; sourceTree = ""; }; @@ -1096,6 +1099,8 @@ children = ( 2D81120324C1036700984DA7 /* OIDTVAuthorizationRequestTests.h */, 2D81120024C1036700984DA7 /* OIDTVAuthorizationRequestTests.m */, + 2DA8D82524C6190400FDFB34 /* OIDTVAuthorizationResponseTests.h */, + 2DA8D82424C6190300FDFB34 /* OIDTVAuthorizationResponseTests.m */, 2DEB065F24CF5CDF00DF47E7 /* OIDTVTokenRequestTests.h */, 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */, ); @@ -2189,6 +2194,7 @@ buildActionMask = 2147483647; files = ( 2D81120C24C103F300984DA7 /* OIDScopesTests.m in Sources */, + 2DA8D82624C6190400FDFB34 /* OIDTVAuthorizationResponseTests.m in Sources */, 2D81121224C103F300984DA7 /* OIDURLQueryComponentTests.m in Sources */, 2DEB066224CF5CFB00DF47E7 /* OIDTVTokenRequestTests.m in Sources */, 2D81120D24C103F300984DA7 /* OIDServiceConfigurationTests.m in Sources */, diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m index db9655506..b86869c34 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m @@ -17,14 +17,14 @@ */ #import "OIDTVAuthorizationRequestTests.h" -#import "OIDTVAuthorizationRequest.h" -#import "OIDTVServiceConfiguration.h" #if SWIFT_PACKAGE -@import AppAuthCore; +@import AppAuthTV; #else #import "Source/AppAuthCore/OIDScopeUtilities.h" #import "Source/AppAuthCore/OIDURLQueryComponent.h" +#import "Source/AppAuthTV/OIDTVAuthorizationRequest.h" +#import "Source/AppAuthTV/OIDTVServiceConfiguration.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h new file mode 100644 index 000000000..32497c854 --- /dev/null +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h @@ -0,0 +1,59 @@ +/*! @file OIDTVAuthorizationResponseTests.h + @brief AppAuth iOS SDK + @copyright + Copyright 2020 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 + +@class OIDTVAuthorizationResponse; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Unit tests for @c OIDTVAuthorizationResponse. + */ +@interface OIDTVAuthorizationResponseTests : XCTestCase + +/*! @brief Tests the initializer using the standard key for @c verificationURI. + */ +- (void)testInitializer; + +/*! @brief Tests the initializer using the alternative key for @c verificationURI. + */ +- (void)testInitializerAlternativeKey; + +/*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying + * process and checking to make sure the source and destination are equivalent. + */ +- (void)testCopying; + +/*! @brief Tests the @c NSSecureCoding implementation by round-tripping an instance through the + * coding process and checking to make sure the source and destination are equivalent. + */ +- (void)testSecureCoding; + +/*! @brief Tests the @c tokenPollRequest method that takes no additional parameters. + */ +- (void)testTokenPollRequest; + +/*! @brief Tests the @c tokenPollRequestWithAdditionalParameters method with one additional + parameter. + */ +- (void)testTokenPollRequestWithAdditionalParameters; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m new file mode 100644 index 000000000..00612fd8f --- /dev/null +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m @@ -0,0 +1,285 @@ +/*! @file OIDTVAuthorizationResponseTests.m + @brief AppAuth iOS SDK + @copyright + Copyright 2020 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 "OIDTVAuthorizationResponseTests.h" + +#if SWIFT_PACKAGE +@import AppAuthTV; +#else +#import "Source/AppAuthCore/OIDScopeUtilities.h" +#import "Source/AppAuthCore/OIDURLQueryComponent.h" +#import "Source/AppAuthTV/OIDTVAuthorizationRequest.h" +#import "Source/AppAuthTV/OIDTVAuthorizationResponse.h" +#import "Source/AppAuthTV/OIDTVServiceConfiguration.h" +#import "Source/AppAuthTV/OIDTVTokenRequest.h" +#endif + +/*! @brief Test value for the @c TVAuthorizationEndpoint property. + */ +static NSString *const kTestTVAuthorizationEndpoint = @"https://www.example.com/device/code"; + +/*! @brief Test value for the @c tokenEndpoint property. + */ +static NSString *const kTestTokenEndpoint = @"https://www.example.com/token"; + +/*! @brief Test key for the @c additionalParameters property. + */ +static NSString *const kTestAdditionalParameterKey = @"A"; + +/*! @brief Test value for the @c additionalParameters property. + */ +static NSString *const kTestAdditionalParameterValue = @"1"; + +/*! @brief Test value for the @c clientID property. + */ +static NSString *const kTestClientID = @"ClientID"; + +/*! @brief Test value for the @c clientSecret property. + */ +static NSString *const kTestClientSecret = @"ClientSecret"; + +/*! @brief Key for the @c verificationURI property. + */ +static NSString *const kVerificationURIKey = @"verification_uri"; + +/*! @brief Alternative key for the @c verificationURI property. If "verification_uri" is not found + in the response, a "verification_url" key is considered equivalent. + */ +static NSString *const kVerificationURIAlternativeKey = @"verification_url"; + +/*! @brief Test value for the @c verificationURI property. + */ +static NSString *const kTestVerificationURI = @"https://www.example.com/device"; + +/*! @brief Key for the @c verificationURIComplete property. + */ +static NSString *const kVerificationURICompleteKey = @"verification_uri_complete"; + +/*! @brief Test value for the @c verificationURIComplete property. + */ +static NSString *const kTestVerificationURIComplete = @"https://www.example.com/device/UserCode"; + +/*! @brief Key for the @c userCode property. + */ +static NSString *const kUserCodeKey = @"user_code"; + +/*! @brief Test value for the @c userCode property. + */ +static NSString *const kTestUserCode = @"UserCode"; + +/*! @brief Key for the @c deviceCode property. + */ +static NSString *const kDeviceCodeKey = @"device_code"; + +/*! @brief Test value for the @c deviceCode property. + */ +static NSString *const kTestDeviceCode = @"DeviceCode"; + +/*! @brief Key for the @c expirationDate property. + */ +static NSString *const kExpiresInKey = @"expires_in"; + +/*! @brief Test lifetime value used for the @c expirationDate property. + */ +static long long const kTestExpiresIn = 1800; + +/*! @brief Key for the @c interval property. + */ +static NSString *const kIntervalKey = @"interval"; + +/*! @brief Test value for the @c interval property. + */ +static int const kTestInterval = 5; + +@implementation OIDTVAuthorizationResponseTests + +- (OIDTVServiceConfiguration *)testServiceConfiguration { + NSURL *tokenEndpoint = [NSURL URLWithString:kTestTokenEndpoint]; + NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTestTVAuthorizationEndpoint]; + + OIDTVServiceConfiguration *configuration = + [[OIDTVServiceConfiguration alloc] initWithTVAuthorizationEndpoint:TVAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; + return configuration; +} + +- (OIDTVAuthorizationRequest *)testAuthorizationRequest { + OIDTVAuthorizationRequest *request = + [[OIDTVAuthorizationRequest alloc] initWithConfiguration:[self testServiceConfiguration] + clientId:kTestClientID + clientSecret:kTestClientSecret + scopes:nil + additionalParameters:nil]; + return request; +} + +/*! @brief Returns an @c OIDTVAuthorizationResponse instance using the standard key for + @c verificationURI, with a @c verificationURIComplete value and additional parameter. + @returns an @c OIDTVAuthorizationResponse instance +*/ +- (OIDTVAuthorizationResponse *)testAuthorizationResponse { + OIDTVAuthorizationResponse *response = [[OIDTVAuthorizationResponse alloc] + initWithRequest:[self testAuthorizationRequest] + parameters:@{ + kVerificationURIKey : kTestVerificationURI, + kVerificationURICompleteKey : kTestVerificationURIComplete, + kUserCodeKey : kTestUserCode, + kDeviceCodeKey : kTestDeviceCode, + kExpiresInKey : @(kTestExpiresIn), + kIntervalKey : @(kTestInterval), + kTestAdditionalParameterKey : kTestAdditionalParameterValue + }]; + return response; +} + +/*! @brief Tests the initializer using the standard key for @c verificationURI. + */ +- (void)testInitializer { + OIDTVAuthorizationResponse *response = [self testAuthorizationResponse]; + + NSDictionary *testAdditionalParameters = + @{kTestAdditionalParameterKey : kTestAdditionalParameterValue}; + + XCTAssertEqualObjects(response.verificationURI, kTestVerificationURI); + XCTAssertEqualObjects(response.verificationURIComplete, kTestVerificationURIComplete); + XCTAssertEqualObjects(response.userCode, kTestUserCode); + XCTAssertEqualObjects(response.deviceCode, kTestDeviceCode); + XCTAssertEqualObjects(response.interval, @(kTestInterval)); + XCTAssertEqualObjects(response.additionalParameters, testAdditionalParameters); + + // Should be ~ kExpiresInValue seconds. Avoiding swizzling NSDate here for certainty + // to keep dependencies down, and simply making an assumption that this check will be executed + // relatively quickly after the initialization above (less than 5 seconds.) + NSTimeInterval expiration = [response.expirationDate timeIntervalSinceNow]; + XCTAssert(expiration > kTestExpiresIn - 5 && expiration <= kTestExpiresIn); +} + +/*! @brief Tests the initializer using the alternative key for @c verificationURI. + */ +- (void)testInitializerAlternativeKey { + OIDTVAuthorizationResponse *response = [[OIDTVAuthorizationResponse alloc] + initWithRequest:[self testAuthorizationRequest] + parameters:@{ + kVerificationURIAlternativeKey : kTestVerificationURI, + kVerificationURICompleteKey : kTestVerificationURIComplete, + kUserCodeKey : kTestUserCode, + kDeviceCodeKey : kTestDeviceCode, + kExpiresInKey : @(kTestExpiresIn), + kIntervalKey : @(kTestInterval), + kTestAdditionalParameterKey : kTestAdditionalParameterValue + }]; + + NSDictionary *testAdditionalParameters = + @{kTestAdditionalParameterKey : kTestAdditionalParameterValue}; + + // Tests that the alternative key used above maps to the verificationURI property, so + // subsequent tests can simply test using [self testAuthorizationResponse] which uses + // the standard key. + XCTAssertEqualObjects(response.verificationURI, kTestVerificationURI); + + XCTAssertEqualObjects(response.verificationURIComplete, kTestVerificationURIComplete); + XCTAssertEqualObjects(response.userCode, kTestUserCode); + XCTAssertEqualObjects(response.deviceCode, kTestDeviceCode); + XCTAssertEqualObjects(response.interval, @(kTestInterval)); + XCTAssertEqualObjects(response.additionalParameters, testAdditionalParameters); + + // Should be ~ kExpiresInValue seconds. Avoiding swizzling NSDate here for certainty + // to keep dependencies down, and simply making an assumption that this check will be executed + // relatively quickly after the initialization above (less than 5 seconds.) + NSTimeInterval expiration = [response.expirationDate timeIntervalSinceNow]; + XCTAssert(expiration > kTestExpiresIn - 5 && expiration <= kTestExpiresIn); +} + +/*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying + * process and checking to make sure the source and destination are equivalent. + */ +- (void)testCopying { + OIDTVAuthorizationResponse *response = [self testAuthorizationResponse]; + OIDTVAuthorizationResponse *responseCopy = [response copy]; + + NSDictionary *testAdditionalParameters = + @{kTestAdditionalParameterKey : kTestAdditionalParameterValue}; + + XCTAssertEqualObjects(responseCopy.request, response.request); + XCTAssertEqualObjects(responseCopy.deviceCode, kTestDeviceCode); + XCTAssertEqualObjects(responseCopy.interval, @(kTestInterval)); + XCTAssertEqualObjects(responseCopy.userCode, kTestUserCode); + XCTAssertEqualObjects(responseCopy.verificationURIComplete, kTestVerificationURIComplete); + XCTAssertEqualObjects(responseCopy.verificationURI, kTestVerificationURI); + XCTAssertEqualObjects(responseCopy.additionalParameters, testAdditionalParameters); +} + +/*! @brief Tests the @c NSSecureCoding implementation by round-tripping an instance through the + * coding process and checking to make sure the source and destination are equivalent. + */ +- (void)testSecureCoding { + OIDTVAuthorizationResponse *response = [self testAuthorizationResponse]; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:response]; + OIDTVAuthorizationResponse *responseCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + + NSDictionary *testAdditionalParameters = + @{kTestAdditionalParameterKey : kTestAdditionalParameterValue}; + + // Not a full test of the request deserialization, but should be sufficient as a smoke test + // to make sure the request IS actually getting serialized and deserialized in the + // NSSecureCoding implementation. We'll leave it up to the OIDTVAuthorizationRequest tests to make + // sure the NSSecureCoding implementation of that class is correct. + XCTAssertNotNil(responseCopy.request); + + XCTAssertEqualObjects(responseCopy.deviceCode, kTestDeviceCode); + XCTAssertEqualObjects(responseCopy.interval, @(kTestInterval)); + XCTAssertEqualObjects(responseCopy.userCode, kTestUserCode); + XCTAssertEqualObjects(responseCopy.verificationURIComplete, kTestVerificationURIComplete); + XCTAssertEqualObjects(responseCopy.verificationURI, kTestVerificationURI); + XCTAssertEqualObjects(responseCopy.additionalParameters, testAdditionalParameters); +} + +/*! @brief Tests the @c tokenPollRequest method that takes no additional parameters. + */ +- (void)testTokenPollRequest { + OIDTVAuthorizationResponse *testResponse = [self testAuthorizationResponse]; + + OIDTVTokenRequest *pollRequest = [testResponse tokenPollRequest]; + + XCTAssertEqualObjects(pollRequest.deviceCode, kTestDeviceCode); + XCTAssertEqualObjects(pollRequest.clientID, kTestClientID); + XCTAssertEqualObjects(pollRequest.clientSecret, kTestClientSecret); + XCTAssertEqualObjects(pollRequest.additionalParameters, @{}); +} + +/*! @brief Tests the @c tokenPollRequestWithAdditionalParameters method with one additional + parameter. + */ +- (void)testTokenPollRequestWithAdditionalParameters { + OIDTVAuthorizationResponse *testResponse = [self testAuthorizationResponse]; + + NSDictionary *testAdditionalParameters = + @{kTestAdditionalParameterKey : kTestAdditionalParameterValue}; + + OIDTVTokenRequest *pollRequest = + [testResponse tokenPollRequestWithAdditionalParameters:testAdditionalParameters]; + + XCTAssertEqualObjects(pollRequest.deviceCode, kTestDeviceCode); + XCTAssertEqualObjects(pollRequest.clientID, kTestClientID); + XCTAssertEqualObjects(pollRequest.clientSecret, kTestClientSecret); + XCTAssertEqualObjects(pollRequest.additionalParameters, testAdditionalParameters); +} + +@end + +#pragma GCC diagnostic pop From 61229cb6b990048931d0b6fe3ae9e1f3f75ef459 Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Tue, 11 Aug 2020 19:46:40 -0400 Subject: [PATCH 126/204] Add updated tvOS example project --- .../Example-tvOS.xcodeproj/project.pbxproj | 382 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AppAuthTVExampleViewController.h | 63 +++ .../AppAuthTVExampleViewController.m | 358 ++++++++++++++++ .../Example-tvOS/Example-tvOS/AppDelegate.h | 27 ++ .../Example-tvOS/Example-tvOS/AppDelegate.m | 20 + .../Content.imageset/Contents.json | 11 + .../Back.imagestacklayer/Contents.json | 6 + .../Contents.json | 17 + .../Content.imageset/Contents.json | 11 + .../Front.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 11 + .../Middle.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 16 + .../Back.imagestacklayer/Contents.json | 6 + .../App Icon.imagestack/Contents.json | 17 + .../Content.imageset/Contents.json | 16 + .../Front.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 16 + .../Middle.imagestacklayer/Contents.json | 6 + .../Contents.json | 32 ++ .../Contents.json | 24 ++ .../Top Shelf Image.imageset/Contents.json | 24 ++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 24 ++ Examples/Example-tvOS/Example-tvOS/Info.plist | 32 ++ .../Example-tvOS/Example-tvOS/Main.storyboard | 133 ++++++ Examples/Example-tvOS/Example-tvOS/main.m | 24 ++ Examples/Example-tvOS/Podfile | 7 + 30 files changed, 1322 insertions(+) create mode 100644 Examples/Example-tvOS/Example-tvOS.xcodeproj/project.pbxproj create mode 100644 Examples/Example-tvOS/Example-tvOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Examples/Example-tvOS/Example-tvOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.h create mode 100644 Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m create mode 100644 Examples/Example-tvOS/Example-tvOS/AppDelegate.h create mode 100644 Examples/Example-tvOS/Example-tvOS/AppDelegate.m create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Assets.xcassets/Contents.json create mode 100644 Examples/Example-tvOS/Example-tvOS/Base.lproj/LaunchScreen.storyboard create mode 100644 Examples/Example-tvOS/Example-tvOS/Info.plist create mode 100644 Examples/Example-tvOS/Example-tvOS/Main.storyboard create mode 100644 Examples/Example-tvOS/Example-tvOS/main.m create mode 100644 Examples/Example-tvOS/Podfile diff --git a/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.pbxproj b/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.pbxproj new file mode 100644 index 000000000..c70129f75 --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.pbxproj @@ -0,0 +1,382 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 2D8B83982497C7B800CD51D7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2D8B83972497C7B800CD51D7 /* Main.storyboard */; }; + 2D91B7A8248EA17C0005B197 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D91B7A7248EA17C0005B197 /* AppDelegate.m */; }; + 2D91B7AB248EA17C0005B197 /* AppAuthTVExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D91B7AA248EA17C0005B197 /* AppAuthTVExampleViewController.m */; }; + 2D91B7B0248EA17D0005B197 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2D91B7AF248EA17D0005B197 /* Assets.xcassets */; }; + 2D91B7B3248EA17D0005B197 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2D91B7B1248EA17D0005B197 /* LaunchScreen.storyboard */; }; + 2D91B7B6248EA17E0005B197 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D91B7B5248EA17E0005B197 /* main.m */; }; + 424A8D8BA4EB0A6C9FF4326A /* libPods-Example-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EEBF9489D390F9D0913AE178 /* libPods-Example-tvOS.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0822FAAB579E01CE65770A61 /* Pods-Example-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-tvOS.release.xcconfig"; path = "Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS.release.xcconfig"; sourceTree = ""; }; + 2D8B83972497C7B800CD51D7 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + 2D91B7A3248EA17C0005B197 /* AppAuth tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AppAuth tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D91B7A6248EA17C0005B197 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 2D91B7A7248EA17C0005B197 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 2D91B7A9248EA17C0005B197 /* AppAuthTVExampleViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppAuthTVExampleViewController.h; sourceTree = ""; }; + 2D91B7AA248EA17C0005B197 /* AppAuthTVExampleViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppAuthTVExampleViewController.m; sourceTree = ""; }; + 2D91B7AF248EA17D0005B197 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 2D91B7B2248EA17D0005B197 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 2D91B7B4248EA17D0005B197 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 2D91B7B5248EA17E0005B197 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + D33A601E3AA7C8647C857C08 /* Pods-Example-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS.debug.xcconfig"; sourceTree = ""; }; + EEBF9489D390F9D0913AE178 /* libPods-Example-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Example-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2D91B7A0248EA17C0005B197 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 424A8D8BA4EB0A6C9FF4326A /* libPods-Example-tvOS.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2D91B79A248EA17C0005B197 = { + isa = PBXGroup; + children = ( + 2D91B7A5248EA17C0005B197 /* Example-tvOS */, + 2D91B7A4248EA17C0005B197 /* Products */, + D5A14A70EF1AB61275EF2A3E /* Pods */, + 9CCA3BB0D6EA112455518E2C /* Frameworks */, + ); + sourceTree = ""; + }; + 2D91B7A4248EA17C0005B197 /* Products */ = { + isa = PBXGroup; + children = ( + 2D91B7A3248EA17C0005B197 /* AppAuth tvOS.app */, + ); + name = Products; + sourceTree = ""; + }; + 2D91B7A5248EA17C0005B197 /* Example-tvOS */ = { + isa = PBXGroup; + children = ( + 2D91B7A6248EA17C0005B197 /* AppDelegate.h */, + 2D91B7A7248EA17C0005B197 /* AppDelegate.m */, + 2D91B7A9248EA17C0005B197 /* AppAuthTVExampleViewController.h */, + 2D91B7AA248EA17C0005B197 /* AppAuthTVExampleViewController.m */, + 2D8B83972497C7B800CD51D7 /* Main.storyboard */, + 2D91B7AF248EA17D0005B197 /* Assets.xcassets */, + 2D91B7B1248EA17D0005B197 /* LaunchScreen.storyboard */, + 2D91B7B4248EA17D0005B197 /* Info.plist */, + 2D91B7B5248EA17E0005B197 /* main.m */, + ); + path = "Example-tvOS"; + sourceTree = ""; + }; + 9CCA3BB0D6EA112455518E2C /* Frameworks */ = { + isa = PBXGroup; + children = ( + EEBF9489D390F9D0913AE178 /* libPods-Example-tvOS.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + D5A14A70EF1AB61275EF2A3E /* Pods */ = { + isa = PBXGroup; + children = ( + D33A601E3AA7C8647C857C08 /* Pods-Example-tvOS.debug.xcconfig */, + 0822FAAB579E01CE65770A61 /* Pods-Example-tvOS.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 2D91B7A2248EA17C0005B197 /* Example-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D91B7B9248EA17E0005B197 /* Build configuration list for PBXNativeTarget "Example-tvOS" */; + buildPhases = ( + BE78BAE9067856409CDDD945 /* [CP] Check Pods Manifest.lock */, + 2D91B79F248EA17C0005B197 /* Sources */, + 2D91B7A0248EA17C0005B197 /* Frameworks */, + 2D91B7A1248EA17C0005B197 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Example-tvOS"; + productName = "Example-tvOS"; + productReference = 2D91B7A3248EA17C0005B197 /* AppAuth tvOS.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 2D91B79B248EA17C0005B197 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1150; + ORGANIZATIONNAME = "Souleiman Benhida"; + TargetAttributes = { + 2D91B7A2248EA17C0005B197 = { + CreatedOnToolsVersion = 11.5; + }; + }; + }; + buildConfigurationList = 2D91B79E248EA17C0005B197 /* Build configuration list for PBXProject "Example-tvOS" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 2D91B79A248EA17C0005B197; + productRefGroup = 2D91B7A4248EA17C0005B197 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 2D91B7A2248EA17C0005B197 /* Example-tvOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 2D91B7A1248EA17C0005B197 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D8B83982497C7B800CD51D7 /* Main.storyboard in Resources */, + 2D91B7B3248EA17D0005B197 /* LaunchScreen.storyboard in Resources */, + 2D91B7B0248EA17D0005B197 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + BE78BAE9067856409CDDD945 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Example-tvOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2D91B79F248EA17C0005B197 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D91B7AB248EA17C0005B197 /* AppAuthTVExampleViewController.m in Sources */, + 2D91B7B6248EA17E0005B197 /* main.m in Sources */, + 2D91B7A8248EA17C0005B197 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 2D91B7B1248EA17D0005B197 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 2D91B7B2248EA17D0005B197 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 2D91B7B7248EA17E0005B197 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = appletvos; + TVOS_DEPLOYMENT_TARGET = 9.0; + }; + name = Debug; + }; + 2D91B7B8248EA17E0005B197 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = appletvos; + TVOS_DEPLOYMENT_TARGET = 9.0; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 2D91B7BA248EA17E0005B197 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D33A601E3AA7C8647C857C08 /* Pods-Example-tvOS.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Example-tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauthtv.Example; + PRODUCT_NAME = "AppAuth tvOS"; + TARGETED_DEVICE_FAMILY = 3; + }; + name = Debug; + }; + 2D91B7BB248EA17E0005B197 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0822FAAB579E01CE65770A61 /* Pods-Example-tvOS.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Example-tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauthtv.Example; + PRODUCT_NAME = "AppAuth tvOS"; + TARGETED_DEVICE_FAMILY = 3; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2D91B79E248EA17C0005B197 /* Build configuration list for PBXProject "Example-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D91B7B7248EA17E0005B197 /* Debug */, + 2D91B7B8248EA17E0005B197 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D91B7B9248EA17E0005B197 /* Build configuration list for PBXNativeTarget "Example-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D91B7BA248EA17E0005B197 /* Debug */, + 2D91B7BB248EA17E0005B197 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 2D91B79B248EA17C0005B197 /* Project object */; +} diff --git a/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..eb768ef2b --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.h b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.h new file mode 100644 index 000000000..aeccd98b5 --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.h @@ -0,0 +1,63 @@ +/*! @file AppAuthTVExampleViewController.h + @brief AppAuth tvOS SDK Example + @copyright + Copyright 2016 Google Inc. + @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 + +@class OIDAuthState; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief The example application's view controller. + */ +@interface AppAuthTVExampleViewController : UIViewController + +@property(nullable) IBOutlet UIView *signInView; +@property(nullable) IBOutlet UILabel *verificationURLLabel; +@property(nullable) IBOutlet UILabel *userCodeLabel; +@property(nullable) IBOutlet UIView *signInButtons; +@property(nullable) IBOutlet UIButton *cancelSignInButton; +@property(nullable) IBOutlet UIView *signedInButtons; +@property(nullable) IBOutlet UITextView *logTextView; + +/*! @brief The authorization state. + */ +@property(nonatomic, nullable) OIDAuthState *authState; + +/*! @brief Initiates the sign-in. + @param sender IBAction sender. + */ +- (IBAction)signin:(nullable id)sender; + +/*! @brief Cancels the active sign-in (if any), has no effect if a sign-in isn't in progress. + @param sender IBAction sender. + */ +- (IBAction)cancelSignIn:(nullable id)sender; + +/*! @brief Forgets the authentication state, used to sign-out the user. + @param sender IBAction sender. + */ +- (IBAction)clearAuthState:(nullable id)sender; + +/*! @brief Performs an authenticated API call. + @param sender IBAction sender. + */ +- (IBAction)userinfo:(nullable id)sender; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m new file mode 100644 index 000000000..bb40c3426 --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m @@ -0,0 +1,358 @@ +/*! @file AppAuthTVExampleViewController.m + @brief AppAuth tvOS SDK Example + @copyright + Copyright 2016 Google Inc. + @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 "AppAuthTVExampleViewController.h" + +#import +#import + +/*! @brief OAuth client ID. + */ +static NSString *const kClientID = @"YOUR_CLIENT_ID"; + +/*! @brief OAuth client secret. + */ +static NSString *const kClientSecret = @"YOUR_CLIENT_SECRET"; + +/*! @brief Device authorization endpoint. + */ +static NSString *const kTVAuthorizationEndpoint = @"https://oauth.example.com/device"; + +/*! @brief Token endpoint. + */ +static NSString *const kTokenEndpoint = @"https://oauth.example.com/token"; + +/*! @brief User info endpoint. + */ +static NSString *const kUserInfoEndpoint = @"https://oauth.example.com/userinfo"; + +/*! @brief NSCoding key for the authorization property. + */ +static NSString *const kExampleAuthorizerKey = @"authorization"; + +/*! @brief NSCoding key for the authState property. + */ +static NSString *const kExampleAuthStateKey = @"authState"; + +@interface AppAuthTVExampleViewController () +@end + +@implementation AppAuthTVExampleViewController { + OIDTVAuthorizationCancelBlock _cancelBlock; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + _logTextView.text = @""; + _signInView.hidden = YES; + _cancelSignInButton.hidden = YES; + _logTextView.selectable = YES; + _logTextView.panGestureRecognizer.allowedTouchTypes = @[ @(UITouchTypeIndirect) ]; + + [self verifyConfig]; + + [self loadState]; + [self updateUI]; +} + +- (void)verifyConfig { +#if !defined(NS_BLOCK_ASSERTIONS) + // The example needs to be configured with your own client details. + // See: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md + + NSAssert(![kClientID isEqualToString:@"YOUR_CLIENT_ID"], + @"Update kClientID with your own client ID. " + "Instructions: " + "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); + + NSAssert(![kClientSecret isEqualToString:@"YOUR_CLIENT_SECRET"], + @"Update kClientSecret with your own client secret. " + "Instructions: " + "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); + + NSAssert(![kTVAuthorizationEndpoint isEqualToString:@"https://oauth.example.com/device"], + @"Update kTVAuthorizationEndpoint with your own TV authorization endpoint. " + "Instructions: " + "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); + + NSAssert(![kTokenEndpoint isEqualToString:@"https://oauth.example.com/token"], + @"Update kTokenEndpoint with your own token endpoint. " + "Instructions: " + "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); + + NSAssert(![kUserInfoEndpoint isEqualToString:@"https://oauth.example.com/userinfo"], + @"Update kUserInfoEndpoint with your own user info endpoint. " + "Instructions: " + "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); +#endif // !defined(NS_BLOCK_ASSERTIONS) +} + +- (void)stateChanged { + [self saveState]; + [self updateUI]; +} + +- (void)didChangeState:(OIDAuthState *)state { + [self stateChanged]; +} + +- (void)authState:(OIDAuthState *)state didEncounterAuthorizationError:(nonnull NSError *)error { + [self logMessage:@"Received authorization error: %@", error]; +} + +/*! @brief Initiates the sign-in. + @param sender IBAction sender. +*/ +- (IBAction)signin:(id)sender { + if (_cancelBlock) { + [self cancelSignIn:nil]; + } + + // builds authentication request + NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTVAuthorizationEndpoint]; + NSURL *tokenEndpoint = [NSURL URLWithString:kTokenEndpoint]; + + __weak __typeof(self) weakSelf = self; + + OIDTVServiceConfiguration *configuration = + [[OIDTVServiceConfiguration alloc] initWithTVAuthorizationEndpoint:TVAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; + OIDTVAuthorizationRequest *request = + [[OIDTVAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:kClientID + clientSecret:kClientSecret + scopes:@[ OIDScopeOpenID, OIDScopeProfile ] + additionalParameters:nil]; + + OIDTVAuthorizationInitialization initBlock = + ^(OIDTVAuthorizationResponse *_Nullable response, NSError *_Nullable error) { + if (response) { + [weakSelf logMessage:@"Authorization response: %@", response]; + weakSelf.signInView.hidden = NO; + weakSelf.cancelSignInButton.hidden = NO; + weakSelf.verificationURLLabel.text = response.verificationURI; + weakSelf.userCodeLabel.text = response.userCode; + } else { + [weakSelf logMessage:@"Initialization error %@", error]; + } + }; + + OIDTVAuthorizationCompletion completionBlock = + ^(OIDAuthState *_Nullable authState, NSError *_Nullable error) { + weakSelf.signInView.hidden = YES; + if (authState) { + [weakSelf setAuthState:authState]; + [weakSelf logMessage:@"Token response: %@", authState.lastTokenResponse]; + } else { + [weakSelf setAuthState:nil]; + [weakSelf logMessage:@"Error: %@", error]; + } + }; + + _cancelBlock = [OIDTVAuthorizationService authorizeTVRequest:request + initialization:initBlock + completion:completionBlock]; +} + +/*! @brief Cancels the active sign-in (if any), has no effect if a sign-in isn't in progress. + @param sender IBAction sender. +*/ +- (IBAction)cancelSignIn:(nullable id)sender { + if (_cancelBlock) { + _cancelBlock(); + _cancelBlock = nil; + } + _signInView.hidden = YES; + _cancelSignInButton.hidden = YES; +} + +- (void)setAuthState:(nullable OIDAuthState *)authState { + if (_authState == authState) { + return; + } + _authState = authState; + _authState.stateChangeDelegate = self; + [self stateChanged]; +} + +/*! @brief Saves the @c OIDAuthState to @c NSUSerDefaults. + */ +- (void)saveState { + // for production usage consider using the OS Keychain instead + NSData *archivedAuthState = [NSKeyedArchiver archivedDataWithRootObject:_authState]; + [[NSUserDefaults standardUserDefaults] setObject:archivedAuthState forKey:kExampleAuthStateKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + +/*! @brief Loads the @c OIDAuthState from @c NSUSerDefaults. + */ +- (void)loadState { + // loads OIDAuthState from NSUSerDefaults + NSData *archivedAuthState = + [[NSUserDefaults standardUserDefaults] objectForKey:kExampleAuthStateKey]; + OIDAuthState *authState = [NSKeyedUnarchiver unarchiveObjectWithData:archivedAuthState]; + [self setAuthState:authState]; +} + +/*! @brief Refreshes UI, typically called after the auth state changed. + */ +- (void)updateUI { + _signInButtons.hidden = [_authState isAuthorized]; + _signedInButtons.hidden = !_signInButtons.hidden; +} + +- (void)updateSignInUIWithResponse:(OIDTVAuthorizationResponse *)response { + _signInView.hidden = NO; + _cancelSignInButton.hidden = NO; + _verificationURLLabel.text = response.verificationURI; + _userCodeLabel.text = response.userCode; +} + +/*! @brief Forgets the authentication state, used to sign-out the user. + @param sender IBAction sender. +*/ +- (IBAction)clearAuthState:(nullable id)sender { + [self setAuthState:nil]; + [self logMessage:@"Authorization state cleared."]; + _cancelSignInButton.hidden = TRUE; +} + +- (IBAction)clearLog:(nullable id)sender { + _logTextView.text = @""; +} + +/*! @brief Performs an authenticated API call. + @param sender IBAction sender. +*/ +- (IBAction)userinfo:(nullable id)sender { + NSURL *userinfoEndpoint = [NSURL URLWithString:kUserInfoEndpoint]; + NSString *currentAccessToken = _authState.lastTokenResponse.accessToken; + + [self logMessage:@"Performing userinfo request"]; + + [_authState performActionWithFreshTokens:^(NSString *_Nonnull accessToken, + NSString *_Nonnull idToken, NSError *_Nullable error) { + if (error) { + [self logMessage:@"Error fetching fresh tokens: %@", [error localizedDescription]]; + return; + } + + // log whether a token refresh occurred + if (![currentAccessToken isEqual:accessToken]) { + [self logMessage:@"Access token was refreshed automatically (%@ to %@)", currentAccessToken, + accessToken]; + } else { + [self logMessage:@"Access token was fresh and not updated [%@]", accessToken]; + } + + // creates request to the userinfo endpoint, with access token in the Authorization header + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint]; + NSString *authorizationHeaderValue = [NSString stringWithFormat:@"Bearer %@", accessToken]; + [request addValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"]; + + NSURLSessionConfiguration *configuration = + [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration + delegate:nil + delegateQueue:nil]; + + // performs HTTP request + NSURLSessionDataTask *postDataTask = [session + dataTaskWithRequest:request + completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, + NSError *_Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^() { + if (error) { + [self logMessage:@"HTTP request failed %@", error]; + return; + } + if (![response isKindOfClass:[NSHTTPURLResponse class]]) { + [self logMessage:@"Non-HTTP response"]; + return; + } + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + id jsonDictionaryOrArray = [NSJSONSerialization JSONObjectWithData:data + options:0 + error:NULL]; + + if (httpResponse.statusCode != 200) { + // server replied with an error + NSString *responseText = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + if (httpResponse.statusCode == 401) { + // "401 Unauthorized" generally indicates there is an issue with the authorization + // grant. Puts OIDAuthState into an error state. + NSError *oauthError = [OIDErrorUtilities + resourceServerAuthorizationErrorWithCode:0 + errorResponse:jsonDictionaryOrArray + underlyingError:error]; + [self->_authState updateWithAuthorizationError:oauthError]; + // log error + [self logMessage:@"Authorization Error (%@). Response: %@", oauthError, + responseText]; + } else { + [self logMessage:@"HTTP: %d. Response: %@", (int)httpResponse.statusCode, + responseText]; + } + return; + } + + // success response + [self logMessage:@"Success: %@", jsonDictionaryOrArray]; + }); + }]; + + [postDataTask resume]; + }]; +} + +/*! @brief Logs a message to stdout and the textfield. + @param format The format string and arguments. + */ +- (void)logMessage:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2) { + // gets message as string + va_list argp; + va_start(argp, format); + NSString *log = [[NSString alloc] initWithFormat:format arguments:argp]; + va_end(argp); + + // outputs to stdout + NSLog(@"%@", log); + + // appends to output log + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = @"hh:mm:ss"; + NSString *dateString = [dateFormatter stringFromDate:[NSDate date]]; + NSString *logLine = [NSString stringWithFormat:@"\n%@: %@", dateString, log]; + UIFont *systemFont = [UIFont systemFontOfSize:36.0f]; + NSDictionary *fontAttributes = + [[NSDictionary alloc] initWithObjectsAndKeys:systemFont, NSFontAttributeName, nil]; + NSMutableAttributedString *logLineAttr = + [[NSMutableAttributedString alloc] initWithString:logLine attributes:fontAttributes]; + [[_logTextView textStorage] appendAttributedString:logLineAttr]; + + // Scroll to bottom + if (_logTextView.text.length > 0) { + NSRange bottom = NSMakeRange(_logTextView.text.length - 1, 1); + [_logTextView scrollRangeToVisible:bottom]; + } +} + +@end diff --git a/Examples/Example-tvOS/Example-tvOS/AppDelegate.h b/Examples/Example-tvOS/Example-tvOS/AppDelegate.h new file mode 100644 index 000000000..0a665e487 --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/AppDelegate.h @@ -0,0 +1,27 @@ +/*! @file AppDelegate.h + @brief AppAuth tvOS SDK Example + @copyright + Copyright 2016 Google Inc. + @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 + +/*! @brief The example application's delegate. + */ +@interface AppDelegate : UIResponder + +/*! @brief The example application's @c UIWindow. + */ +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/Examples/Example-tvOS/Example-tvOS/AppDelegate.m b/Examples/Example-tvOS/Example-tvOS/AppDelegate.m new file mode 100644 index 000000000..4ae47a2db --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/AppDelegate.m @@ -0,0 +1,20 @@ +/*! @file AppDelegate.m + @brief AppAuth tvOS SDK Example + @copyright + Copyright 2016 Google Inc. + @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 "AppDelegate.h" + +@implementation AppDelegate +@end diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 000000000..2e003356c --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json new file mode 100644 index 000000000..de59d885a --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ] +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 000000000..2e003356c --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 000000000..2e003356c --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 000000000..795cce172 --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json new file mode 100644 index 000000000..de59d885a --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ] +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 000000000..795cce172 --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 000000000..795cce172 --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json new file mode 100644 index 000000000..f47ba43da --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json @@ -0,0 +1,32 @@ +{ + "assets" : [ + { + "filename" : "App Icon - App Store.imagestack", + "idiom" : "tv", + "role" : "primary-app-icon", + "size" : "1280x768" + }, + { + "filename" : "App Icon.imagestack", + "idiom" : "tv", + "role" : "primary-app-icon", + "size" : "400x240" + }, + { + "filename" : "Top Shelf Image Wide.imageset", + "idiom" : "tv", + "role" : "top-shelf-image-wide", + "size" : "2320x720" + }, + { + "filename" : "Top Shelf Image.imageset", + "idiom" : "tv", + "role" : "top-shelf-image", + "size" : "1920x720" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json new file mode 100644 index 000000000..b65f0cddc --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + }, + { + "idiom" : "tv-marketing", + "scale" : "1x" + }, + { + "idiom" : "tv-marketing", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json new file mode 100644 index 000000000..b65f0cddc --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + }, + { + "idiom" : "tv-marketing", + "scale" : "1x" + }, + { + "idiom" : "tv-marketing", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/Contents.json b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-tvOS/Example-tvOS/Base.lproj/LaunchScreen.storyboard b/Examples/Example-tvOS/Example-tvOS/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..660ba53de --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Example-tvOS/Example-tvOS/Info.plist b/Examples/Example-tvOS/Example-tvOS/Info.plist new file mode 100644 index 000000000..959d2174c --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UIUserInterfaceStyle + Automatic + + diff --git a/Examples/Example-tvOS/Example-tvOS/Main.storyboard b/Examples/Example-tvOS/Example-tvOS/Main.storyboard new file mode 100644 index 000000000..b0725a43e --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/Main.storyboard @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Example-tvOS/Example-tvOS/main.m b/Examples/Example-tvOS/Example-tvOS/main.m new file mode 100644 index 000000000..151d7c872 --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/main.m @@ -0,0 +1,24 @@ +/*! @file main.m + @brief AppAuth tvOS SDK Example + @copyright + Copyright 2016 Google Inc. + @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 +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/Examples/Example-tvOS/Podfile b/Examples/Example-tvOS/Podfile new file mode 100644 index 000000000..979341635 --- /dev/null +++ b/Examples/Example-tvOS/Podfile @@ -0,0 +1,7 @@ +platform :tvos, '9.0' + +target 'Example-tvOS' do + # AppAuth Pod, TV subspec + # In production, just use `pod 'AppAuth/TV'` without the path reference. + pod 'AppAuth/TV', :path => '../../' +end From 8c33e8cdce87998919f8fab82ee2c594dcbe59c3 Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Thu, 13 Aug 2020 17:15:34 -0400 Subject: [PATCH 127/204] Add OIDC Discovery for the Device Authorization Endpoint Update the example project to support discovery --- .../AppAuthTVExampleViewController.m | 102 +++++++++++++----- Source/AppAuthCore/OIDServiceDiscovery.h | 6 ++ Source/AppAuthCore/OIDServiceDiscovery.m | 5 + Source/AppAuthTV/OIDTVAuthorizationService.h | 36 +++++++ Source/AppAuthTV/OIDTVAuthorizationService.m | 46 ++++++++ Source/AppAuthTV/OIDTVServiceConfiguration.h | 15 +-- Source/AppAuthTV/OIDTVServiceConfiguration.m | 15 +++ UnitTests/OIDServiceDiscoveryTests.m | 86 ++++++++------- 8 files changed, 240 insertions(+), 71 deletions(-) diff --git a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m index bb40c3426..c65ee4e0d 100644 --- a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m +++ b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m @@ -21,6 +21,12 @@ #import #import +/*! @brief Indicates whether YES to discover endpoints from @c kIssuer or NO to use the + @c kTVAuthorizationEndpoint, @c kTokenEndpoint, and @c kUserInfoEndpoint values defined + below. + */ +static BOOL const shouldDiscoverEndpoints = YES; + /*! @brief OAuth client ID. */ static NSString *const kClientID = @"YOUR_CLIENT_ID"; @@ -29,17 +35,21 @@ */ static NSString *const kClientSecret = @"YOUR_CLIENT_SECRET"; +/*! @brief The OIDC issuer from which the configuration will be discovered. + */ +static NSString *const kIssuer = @"https://issuer.example.com"; + /*! @brief Device authorization endpoint. */ -static NSString *const kTVAuthorizationEndpoint = @"https://oauth.example.com/device"; +static NSString *const kTVAuthorizationEndpoint = @"https://www.example.com/device"; /*! @brief Token endpoint. */ -static NSString *const kTokenEndpoint = @"https://oauth.example.com/token"; +static NSString *const kTokenEndpoint = @"https://www.example.com/token"; /*! @brief User info endpoint. */ -static NSString *const kUserInfoEndpoint = @"https://oauth.example.com/userinfo"; +static NSString *const kUserInfoEndpoint = @"https://www.example.com/userinfo"; /*! @brief NSCoding key for the authorization property. */ @@ -73,9 +83,6 @@ - (void)viewDidLoad { - (void)verifyConfig { #if !defined(NS_BLOCK_ASSERTIONS) - // The example needs to be configured with your own client details. - // See: https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md - NSAssert(![kClientID isEqualToString:@"YOUR_CLIENT_ID"], @"Update kClientID with your own client ID. " "Instructions: " @@ -86,20 +93,27 @@ - (void)verifyConfig { "Instructions: " "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); - NSAssert(![kTVAuthorizationEndpoint isEqualToString:@"https://oauth.example.com/device"], - @"Update kTVAuthorizationEndpoint with your own TV authorization endpoint. " - "Instructions: " - "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); - - NSAssert(![kTokenEndpoint isEqualToString:@"https://oauth.example.com/token"], - @"Update kTokenEndpoint with your own token endpoint. " - "Instructions: " - "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); - - NSAssert(![kUserInfoEndpoint isEqualToString:@"https://oauth.example.com/userinfo"], - @"Update kUserInfoEndpoint with your own user info endpoint. " - "Instructions: " - "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); + if (shouldDiscoverEndpoints) { + NSAssert(![kIssuer isEqualToString:@"https://issuer.example.com"], + @"Update kIssuer with your own issuer. " + "Instructions: " + "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); + } else { + NSAssert(![kTVAuthorizationEndpoint isEqualToString:@"https://www.example.com/device"], + @"Update kTVAuthorizationEndpoint with your own TV authorization endpoint. " + "Instructions: " + "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); + + NSAssert(![kTokenEndpoint isEqualToString:@"https://www.example.com/token"], + @"Update kTokenEndpoint with your own token endpoint. " + "Instructions: " + "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); + + NSAssert(![kUserInfoEndpoint isEqualToString:@"https://www.example.com/userinfo"], + @"Update kUserInfoEndpoint with your own user info endpoint. " + "Instructions: " + "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); + } #endif // !defined(NS_BLOCK_ASSERTIONS) } @@ -115,7 +129,6 @@ - (void)didChangeState:(OIDAuthState *)state { - (void)authState:(OIDAuthState *)state didEncounterAuthorizationError:(nonnull NSError *)error { [self logMessage:@"Received authorization error: %@", error]; } - /*! @brief Initiates the sign-in. @param sender IBAction sender. */ @@ -124,15 +137,40 @@ - (IBAction)signin:(id)sender { [self cancelSignIn:nil]; } - // builds authentication request - NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTVAuthorizationEndpoint]; - NSURL *tokenEndpoint = [NSURL URLWithString:kTokenEndpoint]; + if (shouldDiscoverEndpoints) { + NSURL *issuer = [NSURL URLWithString:kIssuer]; + + // Discover endpoints + [OIDTVAuthorizationService discoverServiceConfigurationForIssuer:issuer + completion:^(OIDTVServiceConfiguration *_Nullable configuration, NSError *_Nullable error) { + if (!configuration) { + [self logMessage:@"Error retrieving discovery document: %@", [error localizedDescription]]; + [self setAuthState:nil]; + return; + } + + [self logMessage:@"Got configuration: %@", configuration]; + + // Perform authorization flow + [self performAuthorizationWithConfiguration:configuration]; + }]; + } else { + NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTVAuthorizationEndpoint]; + NSURL *tokenEndpoint = [NSURL URLWithString:kTokenEndpoint]; + + OIDTVServiceConfiguration *configuration = + [[OIDTVServiceConfiguration alloc] initWithTVAuthorizationEndpoint:TVAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; + + // Perform authorization flow + [self performAuthorizationWithConfiguration:configuration]; + } +} +- (void)performAuthorizationWithConfiguration:(OIDTVServiceConfiguration *)configuration { + // builds authentication request __weak __typeof(self) weakSelf = self; - OIDTVServiceConfiguration *configuration = - [[OIDTVServiceConfiguration alloc] initWithTVAuthorizationEndpoint:TVAuthorizationEndpoint - tokenEndpoint:tokenEndpoint]; OIDTVAuthorizationRequest *request = [[OIDTVAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kClientID @@ -241,7 +279,15 @@ - (IBAction)clearLog:(nullable id)sender { @param sender IBAction sender. */ - (IBAction)userinfo:(nullable id)sender { - NSURL *userinfoEndpoint = [NSURL URLWithString:kUserInfoEndpoint]; + NSURL *userinfoEndpoint; + + if (shouldDiscoverEndpoints) { + userinfoEndpoint = _authState.lastAuthorizationResponse.request.configuration.discoveryDocument + .userinfoEndpoint; + } else { + userinfoEndpoint = [NSURL URLWithString:kUserInfoEndpoint]; + } + NSString *currentAccessToken = _authState.lastTokenResponse.accessToken; [self logMessage:@"Performing userinfo request"]; diff --git a/Source/AppAuthCore/OIDServiceDiscovery.h b/Source/AppAuthCore/OIDServiceDiscovery.h index 577700834..3998fa12b 100644 --- a/Source/AppAuthCore/OIDServiceDiscovery.h +++ b/Source/AppAuthCore/OIDServiceDiscovery.h @@ -44,6 +44,12 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, readonly) NSURL *authorizationEndpoint; +/*! @brief OPTIONAL. URL of the OP's OAuth 2.0 Device Authorization Endpoint. + @remarks device_authorization_endpoint + @seealso https://tools.ietf.org/html/rfc8628#section-4 + */ +@property(nonatomic, readonly, nullable) NSURL *deviceAuthorizationEndpoint; + /*! @brief URL of the OP's OAuth 2.0 Token Endpoint. This is REQUIRED unless only the Implicit Flow is used. @remarks token_endpoint diff --git a/Source/AppAuthCore/OIDServiceDiscovery.m b/Source/AppAuthCore/OIDServiceDiscovery.m index ca81108a8..739275251 100644 --- a/Source/AppAuthCore/OIDServiceDiscovery.m +++ b/Source/AppAuthCore/OIDServiceDiscovery.m @@ -26,6 +26,7 @@ /*! Field keys associated with an OpenID Connect Discovery Document. */ static NSString *const kIssuerKey = @"issuer"; static NSString *const kAuthorizationEndpointKey = @"authorization_endpoint"; +static NSString *const kDeviceAuthorizationEndpointKey = @"device_authorization_endpoint"; static NSString *const kTokenEndpointKey = @"token_endpoint"; static NSString *const kUserinfoEndpointKey = @"userinfo_endpoint"; static NSString *const kJWKSURLKey = @"jwks_uri"; @@ -217,6 +218,10 @@ - (NSURL *)authorizationEndpoint { return [NSURL URLWithString:_discoveryDictionary[kAuthorizationEndpointKey]]; } +- (nullable NSURL *)deviceAuthorizationEndpoint { + return [NSURL URLWithString:_discoveryDictionary[kDeviceAuthorizationEndpointKey]]; +} + - (NSURL *)tokenEndpoint { return [NSURL URLWithString:_discoveryDictionary[kTokenEndpointKey]]; } diff --git a/Source/AppAuthTV/OIDTVAuthorizationService.h b/Source/AppAuthTV/OIDTVAuthorizationService.h index 2a9bfe571..93158579c 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationService.h +++ b/Source/AppAuthTV/OIDTVAuthorizationService.h @@ -25,6 +25,14 @@ NS_ASSUME_NONNULL_BEGIN @class OIDTVAuthorizationResponse; @class OIDTVServiceConfiguration; +/*! @brief Represents the type of block used as a callback for creating a TV service configuration from + a remote OpenID Connect Discovery document. + @param configuration The TV service configuration, if available. + @param error The error if an error occurred. + */ +typedef void (^OIDTVDiscoveryCallback)(OIDTVServiceConfiguration *_Nullable configuration, + NSError *_Nullable error); + /*! @brief The block that is called when the TV authorization has initialized. @param response The authorization response, or nil if there was an error. Display @c OIDTVAuthorizationResponse.userCode and @c OIDTVAuthorizationResponse.verificationURI to @@ -52,6 +60,34 @@ typedef void (^OIDTVAuthorizationCancelBlock)(void); /*! @brief Performs authorization flows designed for TVs and other limited input devices. */ @interface OIDTVAuthorizationService : NSObject +/*! @internal + @brief Unavailable. This class should not be initialized. + */ +- (instancetype)init NS_UNAVAILABLE; + +/*! @brief Convenience method for creating a TV authorization service configuration from an OpenID + Connect compliant issuer URL. This method validates the presence of a device authorization + endpoint in the retrieved discovery document and instantiates an + @c OIDTVServiceConfiguration. + @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:(OIDTVDiscoveryCallback)completion; + +/*! @brief Convenience method for creating a TV authorization service configuration from an OpenID + Connect compliant identity provider's discovery document. This method validates the presence + of a device authorization endpoint in the retrieved discovery document and instantiates an + @c OIDTVServiceConfiguration. + @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:(OIDTVDiscoveryCallback)completion; /*! @brief Starts a TV authorization flow with the given request and polls for a response. @param request The TV authorization request to initiate. diff --git a/Source/AppAuthTV/OIDTVAuthorizationService.m b/Source/AppAuthTV/OIDTVAuthorizationService.m index 944d722df..eaa4d6bfc 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationService.m +++ b/Source/AppAuthTV/OIDTVAuthorizationService.m @@ -22,7 +22,9 @@ #import "OIDAuthState.h" #import "OIDDefines.h" #import "OIDErrorUtilities.h" +#import "OIDServiceDiscovery.h" #import "OIDURLQueryComponent.h" +#import "OIDURLSessionProvider.h" #import "OIDTVAuthorizationRequest.h" #import "OIDTVAuthorizationResponse.h" @@ -39,8 +41,52 @@ */ NSString *const kErrorCodeSlowDown = @"slow_down"; +/*! @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"; + @implementation OIDTVAuthorizationService +#pragma mark OIDC Discovery + ++ (void)discoverServiceConfigurationForIssuer:(NSURL *)issuerURL + completion:(OIDTVDiscoveryCallback)completion { + NSURL *fullDiscoveryURL = + [issuerURL URLByAppendingPathComponent:kOpenIDConfigurationWellKnownPath]; + + [[self class] discoverServiceConfigurationForDiscoveryURL:fullDiscoveryURL + completion:completion]; +} + ++ (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL + completion:(OIDTVDiscoveryCallback)completion { + // Call the corresponding discovery method in OIDAuthorizationService + [OIDAuthorizationService discoverServiceConfigurationForDiscoveryURL:discoveryURL + completion:^(OIDServiceConfiguration * _Nullable configuration, NSError * _Nullable error) { + if (configuration == nil) { + completion(nil, error); + return; + } + + if (configuration.discoveryDocument.deviceAuthorizationEndpoint == nil) { + NSError *missingEndpointError = [OIDErrorUtilities + errorWithCode:OIDErrorCodeInvalidDiscoveryDocument + underlyingError:nil + description:@"Discovery document does not contain device authorization endpoint."]; + + completion(nil, missingEndpointError); + return; + } + + // Create an OIDTVServiceConfiguration from the discovery document of the configuration + OIDTVServiceConfiguration *TVConfiguration = [[OIDTVServiceConfiguration alloc] + initWithDiscoveryDocument:configuration.discoveryDocument]; + + completion(TVConfiguration, nil); + }]; +} + #pragma mark - Initializers + (OIDTVAuthorizationCancelBlock)authorizeTVRequest:(OIDTVAuthorizationRequest *)request diff --git a/Source/AppAuthTV/OIDTVServiceConfiguration.h b/Source/AppAuthTV/OIDTVServiceConfiguration.h index b3d60e748..bdedcdd10 100644 --- a/Source/AppAuthTV/OIDTVServiceConfiguration.h +++ b/Source/AppAuthTV/OIDTVServiceConfiguration.h @@ -30,22 +30,23 @@ NS_ASSUME_NONNULL_BEGIN /*! @internal @brief Unavailable. Please use - @c initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint: + @c initWithTVAuthorizationEndpoint:tokenEndpoint: */ - (instancetype)init NS_UNAVAILABLE; /*! @internal @brief Unavailable. Please use - @c initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint: + @c initWithTVAuthorizationEndpoint:tokenEndpoint: */ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint NS_UNAVAILABLE; -/*! @internal - @brief Unavailable. Please use - @c initWithAuthorizationEndpoint:TVAuthorizationEndpoint:tokenEndpoint: - */ -- (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *)discoveryDocument NS_UNAVAILABLE; +/*! @brief Designated initializer. + @param discoveryDocument The discovery document from which to extract the required OAuth + configuration. +*/ +- (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *)discoveryDocument + NS_DESIGNATED_INITIALIZER; /*! @brief Designated initializer. @param TVAuthorizationEndpoint The TV authorization endpoint URI. diff --git a/Source/AppAuthTV/OIDTVServiceConfiguration.m b/Source/AppAuthTV/OIDTVServiceConfiguration.m index 0198475c3..c20883d18 100644 --- a/Source/AppAuthTV/OIDTVServiceConfiguration.m +++ b/Source/AppAuthTV/OIDTVServiceConfiguration.m @@ -19,6 +19,7 @@ #import "OIDTVServiceConfiguration.h" #import "OIDDefines.h" +#import "OIDServiceDiscovery.h" /*! @brief The key for the @c TVAuthorizationEndpoint property. */ @@ -44,6 +45,20 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithTVAuthorizationEndpoint:tokenEndpoint:)) +- (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *)discoveryDocument { + self = [super initWithDiscoveryDocument:discoveryDocument]; + + if (self) { + if (discoveryDocument.deviceAuthorizationEndpoint == nil) { + NSLog(@"Warning: Discovery document used to initialize %@ " + @"does not contain device authorization endpoint.", self); + } else { + _TVAuthorizationEndpoint = [discoveryDocument.deviceAuthorizationEndpoint copy]; + } + } + return self; +} + - (instancetype)initWithTVAuthorizationEndpoint:(NSURL *)TVAuthorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint { self = [super initWithAuthorizationEndpoint:[[NSURL alloc] initWithString:@""] diff --git a/UnitTests/OIDServiceDiscoveryTests.m b/UnitTests/OIDServiceDiscoveryTests.m index 574cd794f..17d6cd19e 100644 --- a/UnitTests/OIDServiceDiscoveryTests.m +++ b/UnitTests/OIDServiceDiscoveryTests.m @@ -39,6 +39,7 @@ /*! Field keys associated with an OpenID Connect Discovery Document. */ static NSString *const kIssuerKey = @"issuer"; static NSString *const kAuthorizationEndpointKey = @"authorization_endpoint"; +static NSString *const kDeviceAuthorizationEndpointKey = @"device_authorization_endpoint"; static NSString *const kTokenEndpointKey = @"token_endpoint"; static NSString *const kUserinfoEndpointKey = @"userinfo_endpoint"; static NSString *const kJWKSURLKey = @"jwks_uri"; @@ -103,6 +104,7 @@ + (NSDictionary *)completeServiceDiscoveryDictionary { return @{ kIssuerKey : @"http://www.example.com/issuer", kAuthorizationEndpointKey : @"http://www.example.com/authorization", + kDeviceAuthorizationEndpointKey : @"http://www.example.com/device", kTokenEndpointKey : @"http://www.example.com/token", kUserinfoEndpointKey : @"User Info Endpoint", kJWKSURLKey : @"http://www.example.com/jwks", @@ -152,47 +154,58 @@ + (NSURL *)googleDiscoveryAuthorizationEndpoint { // from https://accounts.google.com/.well-known/openid-configuration static NSString *const kDiscoveryDocument = - @"{\"issuer\":\"https://accounts.google.com\",\"authorization_endpoint\":\"https://account" - "s.google.com/o/oauth2/v2/auth\",\"token_endpoint\":\"https://www.googleapis.com/oauth2/v4/to" - "ken\",\"userinfo_endpoint\":\"https://www.googleapis.com/oauth2/v3/userinfo\",\"revocation_e" - "ndpoint\":\"https://accounts.google.com/o/oauth2/revoke\",\"jwks_uri\":\"https://www.googlea" - "pis.com/oauth2/v3/certs\",\"response_types_supported\":[\"code\",\"token\",\"id_token\",\"co" - "de token\",\"code id_token\",\"token id_token\",\"code token id_token\",\"none\"],\"subject_" - "types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"scope" - "s_supported\":[\"openid\",\"email\",\"profile\"],\"token_endpoint_auth_methods_supported\":[" - "\"client_secret_post\",\"client_secret_basic\"],\"claims_supported\":[\"aud\",\"email\",\"em" - "ail_verified\",\"exp\",\"family_name\",\"given_name\",\"iat\",\"iss\",\"locale\",\"name\",\"" - "picture\",\"sub\"]}"; + @"{\"issuer\":\"https://accounts.google.com\",\"authorization_endpoint\":\"https://" + @"accounts.google.com/o/oauth2/v2/auth\",\"device_authorization_endpoint\":\"https://" + @"oauth2.googleapis.com/device/code\",\"token_endpoint\":\"https://oauth2.googleapis.com/" + @"token\",\"userinfo_endpoint\":\"https://openidconnect.googleapis.com/v1/" + @"userinfo\",\"revocation_endpoint\":\"https://oauth2.googleapis.com/" + @"revoke\",\"jwks_uri\":\"https://www.googleapis.com/oauth2/v3/" + @"certs\",\"response_types_supported\":[\"code\",\"token\",\"id_token\",\"codetoken\",\"codeid_" + @"token\",\"tokenid_token\",\"codetokenid_token\",\"none\"],\"subject_types_supported\":[" + @"\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"scopes_supported\":[" + @"\"openid\",\"email\",\"profile\"],\"token_endpoint_auth_methods_supported\":[\"client_secret_" + @"post\",\"client_secret_basic\"],\"claims_supported\":[\"aud\",\"email\",\"email_verified\"," + @"\"exp\",\"family_name\",\"given_name\",\"iat\",\"iss\",\"locale\",\"name\",\"picture\"," + @"\"sub\"],\"code_challenge_methods_supported\":[\"plain\",\"S256\"],\"grant_types_supported\":" + @"[\"authorization_code\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\"," + @"\"urn:ietf:params:oauth:grant-type:jwt-bearer\"]}"; // from https://accounts.google.com/.well-known/openid-configuration with authorization_endpoint // removed static NSString *const kDiscoveryDocumentMissingField = - @"{\"issuer\":\"https://accounts.google.com\",\"token_endpoint\":\"https://www.googleapis." - "com/oauth2/v4/to" - "ken\",\"userinfo_endpoint\":\"https://www.googleapis.com/oauth2/v3/userinfo\",\"revocation_e" - "ndpoint\":\"https://accounts.google.com/o/oauth2/revoke\",\"jwks_uri\":\"https://www.googlea" - "pis.com/oauth2/v3/certs\",\"response_types_supported\":[\"code\",\"token\",\"id_token\",\"co" - "de token\",\"code id_token\",\"token id_token\",\"code token id_token\",\"none\"],\"subject_" - "types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"scope" - "s_supported\":[\"openid\",\"email\",\"profile\"],\"token_endpoint_auth_methods_supported\":[" - "\"client_secret_post\",\"client_secret_basic\"],\"claims_supported\":[\"aud\",\"email\",\"em" - "ail_verified\",\"exp\",\"family_name\",\"given_name\",\"iat\",\"iss\",\"locale\",\"name\",\"" - "picture\",\"sub\"]}"; - - // from https://accounts.google.com/.well-known/openid-configuration with authorization_endpoint - // and token_endpoint set to JSON 'null' + @"{\"issuer\":\"https://accounts.google.com\",\"device_authorization_endpoint\":\"https://" + @"oauth2.googleapis.com/device/code\",\"token_endpoint\":\"https://oauth2.googleapis.com/" + @"token\",\"userinfo_endpoint\":\"https://openidconnect.googleapis.com/v1/" + @"userinfo\",\"revocation_endpoint\":\"https://oauth2.googleapis.com/" + @"revoke\",\"jwks_uri\":\"https://www.googleapis.com/oauth2/v3/" + @"certs\",\"response_types_supported\":[\"code\",\"token\",\"id_token\",\"codetoken\",\"codeid_" + @"token\",\"tokenid_token\",\"codetokenid_token\",\"none\"],\"subject_types_supported\":[" + @"\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"scopes_supported\":[" + @"\"openid\",\"email\",\"profile\"],\"token_endpoint_auth_methods_supported\":[\"client_secret_" + @"post\",\"client_secret_basic\"],\"claims_supported\":[\"aud\",\"email\",\"email_verified\"," + @"\"exp\",\"family_name\",\"given_name\",\"iat\",\"iss\",\"locale\",\"name\",\"picture\"," + @"\"sub\"],\"code_challenge_methods_supported\":[\"plain\",\"S256\"],\"grant_types_supported\":" + @"[\"authorization_code\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\"," + @"\"urn:ietf:params:oauth:grant-type:jwt-bearer\"]}"; + +// from https://accounts.google.com/.well-known/openid-configuration with authorization_endpoint +// and token_endpoint set to JSON 'null' static NSString *const kDiscoveryDocumentNullField = - @"{\"issuer\":\"https://accounts.google.com\",\"authorization_endpoint\":null," - "\"token_endpoint\":null" - ",\"userinfo_endpoint\":\"https://www.googleapis.com/oauth2/v3/userinfo\",\"revocation_e" - "ndpoint\":\"https://accounts.google.com/o/oauth2/revoke\",\"jwks_uri\":\"https://www.googlea" - "pis.com/oauth2/v3/certs\",\"response_types_supported\":[\"code\",\"token\",\"id_token\",\"co" - "de token\",\"code id_token\",\"token id_token\",\"code token id_token\",\"none\"],\"subject_" - "types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"scope" - "s_supported\":[\"openid\",\"email\",\"profile\"],\"token_endpoint_auth_methods_supported\":[" - "\"client_secret_post\",\"client_secret_basic\"],\"claims_supported\":[\"aud\",\"email\",\"em" - "ail_verified\",\"exp\",\"family_name\",\"given_name\",\"iat\",\"iss\",\"locale\",\"name\",\"" - "picture\",\"sub\"]}"; + @"{\"issuer\":\"https://" + @"accounts.google.com\",\"authorization_endpoint\":null,\"device_authorization_endpoint\":" + @"\"https://oauth2.googleapis.com/device/" + @"code\",\"token_endpoint\":null,\"userinfo_endpoint\":\"https://openidconnect.googleapis.com/" + @"v1/userinfo\",\"revocation_endpoint\":\"https://oauth2.googleapis.com/" + @"revoke\",\"jwks_uri\":\"https://www.googleapis.com/oauth2/v3/" + @"certs\",\"response_types_supported\":[\"code\",\"token\",\"id_token\",\"codetoken\",\"codeid_" + @"token\",\"tokenid_token\",\"codetokenid_token\",\"none\"],\"subject_types_supported\":[" + @"\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"scopes_supported\":[" + @"\"openid\",\"email\",\"profile\"],\"token_endpoint_auth_methods_supported\":[\"client_secret_" + @"post\",\"client_secret_basic\"],\"claims_supported\":[\"aud\",\"email\",\"email_verified\"," + @"\"exp\",\"family_name\",\"given_name\",\"iat\",\"iss\",\"locale\",\"name\",\"picture\"," + @"\"sub\"],\"code_challenge_methods_supported\":[\"plain\",\"S256\"],\"grant_types_supported\":" + @"[\"authorization_code\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\"," + @"\"urn:ietf:params:oauth:grant-type:jwt-bearer\"]}"; static NSString *const kDiscoveryDocumentNotDictionary = @"[\"code\",\"token\",\"id_token\",\"code token\",\"code id_token\",\"token id_token\",\"code to" @@ -489,6 +502,7 @@ - (void)testField_##_field_ { TestURLFieldBackedBy(issuer, kIssuerKey, kTestURL) TestURLFieldBackedBy(authorizationEndpoint, kAuthorizationEndpointKey, kTestURL) +TestURLFieldBackedBy(deviceAuthorizationEndpoint, kDeviceAuthorizationEndpointKey, kTestURL) TestURLFieldBackedBy(tokenEndpoint, kTokenEndpointKey, kTestURL) TestURLFieldBackedBy(userinfoEndpoint, kUserinfoEndpointKey, kTestURL) TestURLFieldBackedBy(jwksURL, kJWKSURLKey, kTestURL) From e46c433a8dee333015edb9484ddf9c64276e9d49 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Sun, 11 Mar 2018 16:29:46 -0400 Subject: [PATCH 128/204] Add extension for Example-iOS_ObjC --- .../project.pbxproj | 225 +++++++++++++++++- .../Base.lproj/MainInterface.storyboard | 75 ++++++ .../Example-iOS_ObjC_Extension/Info.plist | 31 +++ .../TodayViewController.h | 13 + .../TodayViewController.m | 214 +++++++++++++++++ Examples/Example-iOS_ObjC/Podfile | 10 +- 6 files changed, 565 insertions(+), 3 deletions(-) create mode 100644 Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard create mode 100644 Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Info.plist create mode 100644 Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.h create mode 100644 Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj b/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj index 0e1a19c74..6d898dc8c 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 06D4812F2055C3D400D9DC32 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06D4812E2055C3D400D9DC32 /* NotificationCenter.framework */; }; + 06D481332055C3D400D9DC32 /* TodayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 06D481322055C3D400D9DC32 /* TodayViewController.m */; }; + 06D481362055C3D400D9DC32 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 06D481342055C3D400D9DC32 /* MainInterface.storyboard */; }; + 06D4813A2055C3D400D9DC32 /* Example-iOS_ObjC_Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 06D4812D2055C3D400D9DC32 /* Example-iOS_ObjC_Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 341310AA1E6DEF7000D5DEE5 /* AppAuthExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 341310A91E6DEF7000D5DEE5 /* AppAuthExampleTests.m */; }; 346E916E1C29D42800D3620B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 346E916D1C29D42800D3620B /* main.m */; }; 346E91711C29D42800D3620B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 346E91701C29D42800D3620B /* AppDelegate.m */; }; @@ -15,10 +19,18 @@ 346E91991C2A245000D3620B /* SafariServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 346E91981C2A245000D3620B /* SafariServices.framework */; }; 34CB09BD1C42007600A54261 /* AppAuthExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CB09BB1C42007600A54261 /* AppAuthExampleViewController.m */; }; 34CB09BE1C42007600A54261 /* AppAuthExampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34CB09BC1C42007600A54261 /* AppAuthExampleViewController.xib */; }; + D0B5FA33D74A6F7924A47471 /* libPods-Example-iOS_ObjC_Extension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 715EDB7EC4271193E47615ED /* libPods-Example-iOS_ObjC_Extension.a */; }; E46F8589CE9E5DDFA69D835B /* libPods-Example-iOS_ObjC.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B61A918CBE7B8142CD7D8B3 /* libPods-Example-iOS_ObjC.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 06D481382055C3D400D9DC32 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 346E91611C29D42800D3620B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 06D4812C2055C3D400D9DC32; + remoteInfo = "Example-iOS_ObjC_Extension"; + }; 341310AC1E6DEF7000D5DEE5 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 346E91611C29D42800D3620B /* Project object */; @@ -28,7 +40,27 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 06D4813E2055C3D400D9DC32 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 06D4813A2055C3D400D9DC32 /* Example-iOS_ObjC_Extension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ + 06D4812D2055C3D400D9DC32 /* Example-iOS_ObjC_Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Example-iOS_ObjC_Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + 06D4812E2055C3D400D9DC32 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; + 06D481312055C3D400D9DC32 /* TodayViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TodayViewController.h; sourceTree = ""; }; + 06D481322055C3D400D9DC32 /* TodayViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TodayViewController.m; sourceTree = ""; }; + 06D481352055C3D400D9DC32 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + 06D481372055C3D400D9DC32 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 09795EAF079B07A1781675D9 /* libPods-Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 0D29B2A58C931A5D41332144 /* Pods-Example-iOS_ObjC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS_ObjC.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Example-iOS_ObjC/Pods-Example-iOS_ObjC.debug.xcconfig"; sourceTree = ""; }; 341310A71E6DEF7000D5DEE5 /* AppAuthExample-iOS_ObjCTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "AppAuthExample-iOS_ObjCTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -46,6 +78,9 @@ 34CB09BA1C42007600A54261 /* AppAuthExampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAuthExampleViewController.h; sourceTree = ""; }; 34CB09BB1C42007600A54261 /* AppAuthExampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppAuthExampleViewController.m; sourceTree = ""; }; 34CB09BC1C42007600A54261 /* AppAuthExampleViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AppAuthExampleViewController.xib; sourceTree = ""; }; + 3A18DF082AC2FA0980C9DE57 /* Pods-Example-iOS_ObjC_Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS_ObjC_Extension.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example-iOS_ObjC_Extension/Pods-Example-iOS_ObjC_Extension.release.xcconfig"; sourceTree = ""; }; + 715EDB7EC4271193E47615ED /* libPods-Example-iOS_ObjC_Extension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Example-iOS_ObjC_Extension.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 86C7660572AE6FF7A4D1592A /* Pods-Example-iOS_ObjC_Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS_ObjC_Extension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Example-iOS_ObjC_Extension/Pods-Example-iOS_ObjC_Extension.debug.xcconfig"; sourceTree = ""; }; 8B61A918CBE7B8142CD7D8B3 /* libPods-Example-iOS_ObjC.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Example-iOS_ObjC.a"; sourceTree = BUILT_PRODUCTS_DIR; }; C4C31DB4A4928F246AA03805 /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = ""; }; D9867DC6FA9089CD613D4728 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = ""; }; @@ -53,6 +88,15 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 06D4812A2055C3D400D9DC32 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 06D4812F2055C3D400D9DC32 /* NotificationCenter.framework in Frameworks */, + D0B5FA33D74A6F7924A47471 /* libPods-Example-iOS_ObjC_Extension.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 341310A41E6DEF7000D5DEE5 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -72,6 +116,17 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 06D481302055C3D400D9DC32 /* Example-iOS_ObjC_Extension */ = { + isa = PBXGroup; + children = ( + 06D481312055C3D400D9DC32 /* TodayViewController.h */, + 06D481322055C3D400D9DC32 /* TodayViewController.m */, + 06D481342055C3D400D9DC32 /* MainInterface.storyboard */, + 06D481372055C3D400D9DC32 /* Info.plist */, + ); + path = "Example-iOS_ObjC_Extension"; + sourceTree = ""; + }; 341310A81E6DEF7000D5DEE5 /* Tests */ = { isa = PBXGroup; children = ( @@ -88,6 +143,8 @@ 346E91981C2A245000D3620B /* SafariServices.framework */, 09795EAF079B07A1781675D9 /* libPods-Example.a */, 8B61A918CBE7B8142CD7D8B3 /* libPods-Example-iOS_ObjC.a */, + 06D4812E2055C3D400D9DC32 /* NotificationCenter.framework */, + 715EDB7EC4271193E47615ED /* libPods-Example-iOS_ObjC_Extension.a */, ); name = Frameworks; sourceTree = ""; @@ -97,6 +154,7 @@ children = ( 346E916B1C29D42800D3620B /* Source */, 341310A81E6DEF7000D5DEE5 /* Tests */, + 06D481302055C3D400D9DC32 /* Example-iOS_ObjC_Extension */, 341564001C487ABA00ECA3D9 /* Frameworks */, 346E916A1C29D42800D3620B /* Products */, 6DB0B0125441549B9E4A3E6C /* Pods */, @@ -108,6 +166,7 @@ children = ( 346E91691C29D42800D3620B /* Example-iOS_ObjC.app */, 341310A71E6DEF7000D5DEE5 /* AppAuthExample-iOS_ObjCTests.xctest */, + 06D4812D2055C3D400D9DC32 /* Example-iOS_ObjC_Extension.appex */, ); name = Products; sourceTree = ""; @@ -135,6 +194,8 @@ C4C31DB4A4928F246AA03805 /* Pods-Example.release.xcconfig */, 0D29B2A58C931A5D41332144 /* Pods-Example-iOS_ObjC.debug.xcconfig */, ECBCCC4A1A779C83C72044F2 /* Pods-Example-iOS_ObjC.release.xcconfig */, + 86C7660572AE6FF7A4D1592A /* Pods-Example-iOS_ObjC_Extension.debug.xcconfig */, + 3A18DF082AC2FA0980C9DE57 /* Pods-Example-iOS_ObjC_Extension.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -142,6 +203,25 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 06D4812C2055C3D400D9DC32 /* Example-iOS_ObjC_Extension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 06D4813D2055C3D400D9DC32 /* Build configuration list for PBXNativeTarget "Example-iOS_ObjC_Extension" */; + buildPhases = ( + 836219F293F319703A16E68D /* [CP] Check Pods Manifest.lock */, + 06D481292055C3D400D9DC32 /* Sources */, + 06D4812A2055C3D400D9DC32 /* Frameworks */, + 06D4812B2055C3D400D9DC32 /* Resources */, + F998F170E4D14F53FCAA2B07 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Example-iOS_ObjC_Extension"; + productName = "Example-iOS_ObjC_Extension"; + productReference = 06D4812D2055C3D400D9DC32 /* Example-iOS_ObjC_Extension.appex */; + productType = "com.apple.product-type.app-extension"; + }; 341310A61E6DEF7000D5DEE5 /* AppAuthExample-iOS_ObjCTests */ = { isa = PBXNativeTarget; buildConfigurationList = 341310AE1E6DEF7000D5DEE5 /* Build configuration list for PBXNativeTarget "AppAuthExample-iOS_ObjCTests" */; @@ -170,10 +250,12 @@ 346E91671C29D42800D3620B /* Resources */, 0B0B46F67786AB6E322D5F2B /* [CP] Embed Pods Frameworks */, 3CB1FD4B438DD277394F39A7 /* [CP] Copy Pods Resources */, + 06D4813E2055C3D400D9DC32 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + 06D481392055C3D400D9DC32 /* PBXTargetDependency */, ); name = "Example-iOS_ObjC"; productName = Example; @@ -189,6 +271,10 @@ LastUpgradeCheck = 0720; ORGANIZATIONNAME = "William Denniss"; TargetAttributes = { + 06D4812C2055C3D400D9DC32 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; 341310A61E6DEF7000D5DEE5 = { CreatedOnToolsVersion = 8.2.1; ProvisioningStyle = Automatic; @@ -214,11 +300,20 @@ targets = ( 346E91681C29D42800D3620B /* Example-iOS_ObjC */, 341310A61E6DEF7000D5DEE5 /* AppAuthExample-iOS_ObjCTests */, + 06D4812C2055C3D400D9DC32 /* Example-iOS_ObjC_Extension */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 06D4812B2055C3D400D9DC32 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 06D481362055C3D400D9DC32 /* MainInterface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 341310A51E6DEF7000D5DEE5 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -269,24 +364,68 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example-iOS_ObjC/Pods-Example-iOS_ObjC-resources.sh\"\n"; showEnvVarsInLog = 0; }; + 836219F293F319703A16E68D /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Example-iOS_ObjC_Extension-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; C4DC5E3A0D4C7419380FA8C2 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Example-iOS_ObjC-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + F998F170E4D14F53FCAA2B07 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example-iOS_ObjC_Extension/Pods-Example-iOS_ObjC_Extension-resources.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 06D481292055C3D400D9DC32 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 06D481332055C3D400D9DC32 /* TodayViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 341310A31E6DEF7000D5DEE5 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -308,6 +447,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 06D481392055C3D400D9DC32 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 06D4812C2055C3D400D9DC32 /* Example-iOS_ObjC_Extension */; + targetProxy = 06D481382055C3D400D9DC32 /* PBXContainerItemProxy */; + }; 341310AD1E6DEF7000D5DEE5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 346E91681C29D42800D3620B /* Example-iOS_ObjC */; @@ -316,6 +460,14 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + 06D481342055C3D400D9DC32 /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 06D481352055C3D400D9DC32 /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; 346E917A1C29D42800D3620B /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -327,6 +479,66 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 06D4813B2055C3D400D9DC32 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 86C7660572AE6FF7A4D1592A /* Pods-Example-iOS_ObjC_Extension.debug.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "Example-iOS_ObjC_Extension/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-iOS-ObjC-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 06D4813C2055C3D400D9DC32 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3A18DF082AC2FA0980C9DE57 /* Pods-Example-iOS_ObjC_Extension.release.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "Example-iOS_ObjC_Extension/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-iOS-ObjC-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 341310AF1E6DEF7000D5DEE5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -456,6 +668,7 @@ baseConfigurationReference = 0D29B2A58C931A5D41332144 /* Pods-Example-iOS_ObjC.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; INFOPLIST_FILE = Source/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 7.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -469,6 +682,7 @@ baseConfigurationReference = ECBCCC4A1A779C83C72044F2 /* Pods-Example-iOS_ObjC.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; INFOPLIST_FILE = Source/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 7.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -480,6 +694,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 06D4813D2055C3D400D9DC32 /* Build configuration list for PBXNativeTarget "Example-iOS_ObjC_Extension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 06D4813B2055C3D400D9DC32 /* Debug */, + 06D4813C2055C3D400D9DC32 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 341310AE1E6DEF7000D5DEE5 /* Build configuration list for PBXNativeTarget "AppAuthExample-iOS_ObjCTests" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard new file mode 100644 index 000000000..f9e3f4b64 --- /dev/null +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Info.plist b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Info.plist new file mode 100644 index 000000000..8839cbabf --- /dev/null +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Example-iOS_ObjC_Extension + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.widget-extension + + + diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.h b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.h new file mode 100644 index 000000000..89f148304 --- /dev/null +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.h @@ -0,0 +1,13 @@ +// +// TodayViewController.h +// Example-iOS_ObjC_Extension +// +// Created by Julien Bodet on 2018-03-11. +// Copyright © 2018 William Denniss. All rights reserved. +// + +#import + +@interface TodayViewController : UIViewController + +@end diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m new file mode 100644 index 000000000..2c356326a --- /dev/null +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m @@ -0,0 +1,214 @@ +// +// TodayViewController.m +// Example-iOS_ObjC_Extension +// +// Created by Julien Bodet on 2018-03-11. +// Copyright © 2018 William Denniss. All rights reserved. +// + +#import "TodayViewController.h" +#import +#import + +static NSString *const kAppAuthExampleAuthStateKey = @"authState"; + +@interface TodayViewController () + +@property(nonatomic, readonly, nullable) OIDAuthState *authState; +@property (weak, nonatomic) IBOutlet UITextView *logTextView; + +@end + +@implementation TodayViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + if ([self.extensionContext respondsToSelector:@selector(setWidgetLargestAvailableDisplayMode:)]) { // iOS 10+ + [self.extensionContext setWidgetLargestAvailableDisplayMode:NCWidgetDisplayModeExpanded]; + } else { + self.preferredContentSize = CGSizeMake(0, 600.0); // iOS 10- + } + + [self loadState]; +} + +- (void) viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; +} + +- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode + withMaximumSize:(CGSize)maxSize { + + if (activeDisplayMode == NCWidgetDisplayModeExpanded) { + self.preferredContentSize = CGSizeMake(maxSize.width, 600.0); + } else if (activeDisplayMode == NCWidgetDisplayModeCompact) { + self.preferredContentSize = maxSize; + } +} + +- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { + // Perform any setup necessary in order to update the view. + + // If an error is encountered, use NCUpdateResultFailed + // If there's no update required, use NCUpdateResultNoData + // If there's an update, use NCUpdateResultNewData + + completionHandler(NCUpdateResultNewData); +} + +/*! @brief Loads the @c OIDAuthState from @c NSUSerDefaults. + */ +- (void)loadState { + // loads OIDAuthState from NSUSerDefaults + NSData *archivedAuthState = + [[NSUserDefaults standardUserDefaults] objectForKey:kAppAuthExampleAuthStateKey]; + OIDAuthState *authState = [NSKeyedUnarchiver unarchiveObjectWithData:archivedAuthState]; + [self setAuthState:authState]; +} + +/*! @brief Saves the @c OIDAuthState to @c NSUSerDefaults. + */ +- (void)saveState { + // for production usage consider using the OS Keychain instead + NSData *archivedAuthState = [ NSKeyedArchiver archivedDataWithRootObject:_authState]; + [[NSUserDefaults standardUserDefaults] setObject:archivedAuthState + forKey:kAppAuthExampleAuthStateKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + +- (void)setAuthState:(nullable OIDAuthState *)authState { + if (_authState == authState) { + return; + } + _authState = authState; + _authState.stateChangeDelegate = self; + [self stateChanged]; +} + +- (void)stateChanged { + [self saveState]; +} + +- (void)didChangeState:(OIDAuthState *)state { + [self stateChanged]; +} + +- (IBAction)getUserInfo:(UIButton *)sender { + NSURL *userinfoEndpoint = + _authState.lastAuthorizationResponse.request.configuration.discoveryDocument.userinfoEndpoint; + if (!userinfoEndpoint) { + [self logMessage:@"Userinfo endpoint not declared in discovery document"]; + return; + } + NSString *currentAccessToken = _authState.lastTokenResponse.accessToken; + + [self logMessage:@"Performing userinfo request"]; + + [_authState performActionWithFreshTokens:^(NSString *_Nonnull accessToken, + NSString *_Nonnull idToken, + NSError *_Nullable error) { + if (error) { + [self logMessage:@"Error fetching fresh tokens: %@", [error localizedDescription]]; + return; + } + + // log whether a token refresh occurred + if (![currentAccessToken isEqual:accessToken]) { + [self logMessage:@"Access token was refreshed automatically (%@ to %@)", + currentAccessToken, + accessToken]; + } else { + [self logMessage:@"Access token was fresh and not updated [%@]", accessToken]; + } + + // creates request to the userinfo endpoint, with access token in the Authorization header + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint]; + NSString *authorizationHeaderValue = [NSString stringWithFormat:@"Bearer %@", accessToken]; + [request addValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"]; + + NSURLSessionConfiguration *configuration = + [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration + delegate:nil + delegateQueue:nil]; + + // performs HTTP request + NSURLSessionDataTask *postDataTask = + [session dataTaskWithRequest:request + completionHandler:^(NSData *_Nullable data, + NSURLResponse *_Nullable response, + NSError *_Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^() { + if (error) { + [self logMessage:@"HTTP request failed %@", error]; + return; + } + if (![response isKindOfClass:[NSHTTPURLResponse class]]) { + [self logMessage:@"Non-HTTP response"]; + return; + } + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + id jsonDictionaryOrArray = + [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; + + if (httpResponse.statusCode != 200) { + // server replied with an error + NSString *responseText = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + if (httpResponse.statusCode == 401) { + // "401 Unauthorized" generally indicates there is an issue with the authorization + // grant. Puts OIDAuthState into an error state. + NSError *oauthError = + [OIDErrorUtilities resourceServerAuthorizationErrorWithCode:0 + errorResponse:jsonDictionaryOrArray + underlyingError:error]; + [_authState updateWithAuthorizationError:oauthError]; + // log error + [self logMessage:@"Authorization Error (%@). Response: %@", oauthError, responseText]; + } else { + [self logMessage:@"HTTP: %d. Response: %@", + (int)httpResponse.statusCode, + responseText]; + } + return; + } + + // success response + [self logMessage:@"Success: %@", jsonDictionaryOrArray]; + }); + }]; + + [postDataTask resume]; + }]; +} + +/*! @brief Logs a message to stdout and the textfield. + @param format The format string and arguments. + */ +- (void)logMessage:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2) { + // gets message as string + va_list argp; + va_start(argp, format); + NSString *log = [[NSString alloc] initWithFormat:format arguments:argp]; + va_end(argp); + + // outputs to stdout + NSLog(@"%@", log); + + // appends to output log + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = @"hh:mm:ss"; + NSString *dateString = [dateFormatter stringFromDate:[NSDate date]]; + _logTextView.text = [NSString stringWithFormat:@"%@%@%@: %@", + _logTextView.text, + ([_logTextView.text length] > 0) ? @"\n" : @"", + dateString, + log]; +} +- (IBAction)clearLogTextView:(UIButton *)sender { + _logTextView.text = @""; +} + +@end diff --git a/Examples/Example-iOS_ObjC/Podfile b/Examples/Example-iOS_ObjC/Podfile index 4d6ab0f6f..bf21b203a 100644 --- a/Examples/Example-iOS_ObjC/Podfile +++ b/Examples/Example-iOS_ObjC/Podfile @@ -1,7 +1,13 @@ -target 'Example-iOS_ObjC' do - platform :ios, '9.0' +platform :ios, '9.0' +target 'Example-iOS_ObjC' do # AppAuth Pod # In production, just use `pod 'AppAuth'` without the path reference. pod 'AppAuth', :path => '../../' end + +target 'Example-iOS_ObjC_Extension' do + # AppAuth Pod + # In production, just use `pod 'AppAuth'` without the path reference. + pod 'AppAuth', :path => '../../' +end From 099afb1e1ec3e9ec298fc9dc222c0949fc49834b Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Sun, 11 Mar 2018 16:48:45 -0400 Subject: [PATCH 129/204] Multiple fixes to the app extension example --- .../Example-iOS_ObjC_Extension.xcscheme | 120 ++++++++++++++++++ .../Base.lproj/MainInterface.storyboard | 10 -- .../TodayViewController.h | 24 +++- .../TodayViewController.m | 29 +++-- 4 files changed, 155 insertions(+), 28 deletions(-) create mode 100644 Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/xcshareddata/xcschemes/Example-iOS_ObjC_Extension.xcscheme diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/xcshareddata/xcschemes/Example-iOS_ObjC_Extension.xcscheme b/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/xcshareddata/xcschemes/Example-iOS_ObjC_Extension.xcscheme new file mode 100644 index 000000000..7a9720c27 --- /dev/null +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/xcshareddata/xcschemes/Example-iOS_ObjC_Extension.xcscheme @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard index f9e3f4b64..11e788e03 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard @@ -18,12 +18,6 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Example_Extension.entitlements b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Example_Extension.entitlements new file mode 100644 index 000000000..c250a2746 --- /dev/null +++ b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Example_Extension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.net.openid.appauth.Example + + + diff --git a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Info.plist b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Info.plist new file mode 100644 index 000000000..6ea3c3dc6 --- /dev/null +++ b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Example_Extension + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.widget-extension + + + diff --git a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.h b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.h new file mode 100644 index 000000000..9053ba197 --- /dev/null +++ b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.h @@ -0,0 +1,24 @@ +/*! @file TodayViewController.h + @brief AppAuth iOS SDK Example + @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 + +@interface TodayViewController : UIViewController + +@end + diff --git a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m new file mode 100644 index 000000000..2643f1747 --- /dev/null +++ b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m @@ -0,0 +1,223 @@ +/*! @file TodayViewController.m + @brief AppAuth iOS SDK Example + @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 "TodayViewController.h" +#import +#import + +static NSString *const kAppAuthExampleAuthStateKey = @"authState"; + +@interface TodayViewController () + +@property(nonatomic, readonly, nullable) OIDAuthState *authState; +@property (weak, nonatomic) IBOutlet UITextView *logTextView; + +@end + +@implementation TodayViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + if ([self.extensionContext respondsToSelector:@selector(setWidgetLargestAvailableDisplayMode:)]) { // iOS 10+ + [self.extensionContext setWidgetLargestAvailableDisplayMode:NCWidgetDisplayModeExpanded]; + } else { + self.preferredContentSize = CGSizeMake(0, 600.0); // iOS 10- + } + + [self loadState]; +} + +- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode + withMaximumSize:(CGSize)maxSize { + + if (activeDisplayMode == NCWidgetDisplayModeExpanded) { + self.preferredContentSize = CGSizeMake(maxSize.width, 600.0); + } else if (activeDisplayMode == NCWidgetDisplayModeCompact) { + self.preferredContentSize = maxSize; + } +} + +- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { + // Perform any setup necessary in order to update the view. + + // If an error is encountered, use NCUpdateResultFailed + // If there's no update required, use NCUpdateResultNoData + // If there's an update, use NCUpdateResultNewData + + completionHandler(NCUpdateResultNewData); +} + +/*! @brief Loads the @c OIDAuthState from @c NSUSerDefaults. + */ +- (void)loadState { + // loads OIDAuthState from NSUSerDefaults + NSUserDefaults* userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.net.openid.appauth.Example"]; + NSData *archivedAuthState = [userDefaults objectForKey:kAppAuthExampleAuthStateKey]; + OIDAuthState *authState = [NSKeyedUnarchiver unarchiveObjectWithData:archivedAuthState]; + [self setAuthState:authState]; +} + +/*! @brief Saves the @c OIDAuthState to @c NSUSerDefaults. + */ +- (void)saveState { + // for production usage consider using the OS Keychain instead + NSUserDefaults* userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.net.openid.appauth.Example"]; + NSData *archivedAuthState = [NSKeyedArchiver archivedDataWithRootObject:_authState]; + [userDefaults setObject:archivedAuthState + forKey:kAppAuthExampleAuthStateKey]; + [userDefaults synchronize]; +} + +- (void)setAuthState:(nullable OIDAuthState *)authState { + if (_authState == authState) { + return; + } + _authState = authState; + _authState.stateChangeDelegate = self; + [self stateChanged]; +} + +- (void)stateChanged { + [self saveState]; +} + +- (void)didChangeState:(OIDAuthState *)state { + [self stateChanged]; +} + +- (IBAction)getUserInfo:(UIButton *)sender { + NSURL *userinfoEndpoint = + _authState.lastAuthorizationResponse.request.configuration.discoveryDocument.userinfoEndpoint; + if (!userinfoEndpoint) { + [self logMessage:@"Userinfo endpoint not declared in discovery document"]; + return; + } + NSString *currentAccessToken = _authState.lastTokenResponse.accessToken; + + [self logMessage:@"Performing userinfo request"]; + + [_authState performActionWithFreshTokens:^(NSString *_Nonnull accessToken, + NSString *_Nonnull idToken, + NSError *_Nullable error) { + if (error) { + [self logMessage:@"Error fetching fresh tokens: %@", [error localizedDescription]]; + return; + } + + // log whether a token refresh occurred + if (![currentAccessToken isEqual:accessToken]) { + [self logMessage:@"Access token was refreshed automatically (%@ to %@)", + currentAccessToken, + accessToken]; + } else { + [self logMessage:@"Access token was fresh and not updated [%@]", accessToken]; + } + + // creates request to the userinfo endpoint, with access token in the Authorization header + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint]; + NSString *authorizationHeaderValue = [NSString stringWithFormat:@"Bearer %@", accessToken]; + [request addValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"]; + + NSURLSessionConfiguration *configuration = + [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration + delegate:nil + delegateQueue:nil]; + + // performs HTTP request + NSURLSessionDataTask *postDataTask = + [session dataTaskWithRequest:request + completionHandler:^(NSData *_Nullable data, + NSURLResponse *_Nullable response, + NSError *_Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^() { + if (error) { + [self logMessage:@"HTTP request failed %@", error]; + return; + } + if (![response isKindOfClass:[NSHTTPURLResponse class]]) { + [self logMessage:@"Non-HTTP response"]; + return; + } + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + id jsonDictionaryOrArray = + [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; + + if (httpResponse.statusCode != 200) { + // server replied with an error + NSString *responseText = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + if (httpResponse.statusCode == 401) { + // "401 Unauthorized" generally indicates there is an issue with the authorization + // grant. Puts OIDAuthState into an error state. + NSError *oauthError = + [OIDErrorUtilities resourceServerAuthorizationErrorWithCode:0 + errorResponse:jsonDictionaryOrArray + underlyingError:error]; + [_authState updateWithAuthorizationError:oauthError]; + // log error + [self logMessage:@"Authorization Error (%@). Response: %@", oauthError, responseText]; + } else { + [self logMessage:@"HTTP: %d. Response: %@", + (int)httpResponse.statusCode, + responseText]; + } + return; + } + + // success response + [self logMessage:@"Success: %@", jsonDictionaryOrArray]; + }); + }]; + + [postDataTask resume]; + }]; +} + +/*! @brief Logs a message to stdout and the textfield. + @param format The format string and arguments. + */ +- (void)logMessage:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2) { + // gets message as string + va_list argp; + va_start(argp, format); + NSString *log = [[NSString alloc] initWithFormat:format arguments:argp]; + va_end(argp); + + // outputs to stdout + NSLog(@"%@", log); + + // appends to output log + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = @"hh:mm:ss"; + NSString *dateString = [dateFormatter stringFromDate:[NSDate date]]; + _logTextView.text = [NSString stringWithFormat:@"%@%@%@: %@", + _logTextView.text, + ([_logTextView.text length] > 0) ? @"\n" : @"", + dateString, + log]; +} + +- (IBAction)clearLogTextView:(UIButton *)sender { + _logTextView.text = @""; +} + +@end + diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m b/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m index 68354c436..dc76a8c9c 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m +++ b/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m @@ -102,18 +102,19 @@ - (void)viewDidLoad { */ - (void)saveState { // for production usage consider using the OS Keychain instead + NSUserDefaults* userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.net.openid.appauth.Example"]; NSData *archivedAuthState = [ NSKeyedArchiver archivedDataWithRootObject:_authState]; - [[NSUserDefaults standardUserDefaults] setObject:archivedAuthState - forKey:kAppAuthExampleAuthStateKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; + [userDefaults setObject:archivedAuthState + forKey:kAppAuthExampleAuthStateKey]; + [userDefaults synchronize]; } /*! @brief Loads the @c OIDAuthState from @c NSUSerDefaults. */ - (void)loadState { // loads OIDAuthState from NSUSerDefaults - NSData *archivedAuthState = - [[NSUserDefaults standardUserDefaults] objectForKey:kAppAuthExampleAuthStateKey]; + NSUserDefaults* userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.net.openid.appauth.Example"]; + NSData *archivedAuthState = [userDefaults objectForKey:kAppAuthExampleAuthStateKey]; OIDAuthState *authState = [NSKeyedUnarchiver unarchiveObjectWithData:archivedAuthState]; [self setAuthState:authState]; } diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/Example.entitlements b/Examples/Example-iOS_ObjC-Carthage/Source/Example.entitlements new file mode 100644 index 000000000..c250a2746 --- /dev/null +++ b/Examples/Example-iOS_ObjC-Carthage/Source/Example.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.net.openid.appauth.Example + + + From 602066c7b0a60ee49abfe6e32acd72020bb56edc Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Sun, 18 Mar 2018 17:36:20 -0400 Subject: [PATCH 132/204] Add extension to swift example --- .../Example.xcodeproj/project.pbxproj | 185 ++++++++++++++- .../xcshareddata/xcschemes/Example.xcscheme | 93 ++++++++ .../xcschemes/Example_Extension.xcscheme | 120 ++++++++++ .../Base.lproj/MainInterface.storyboard | 65 +++++ .../Example_Extension.entitlements | 10 + .../Example_Extension/Info.plist | 31 +++ .../TodayViewController.swift | 223 ++++++++++++++++++ .../Source/AppAuthExampleViewController.swift | 10 +- .../Source/Example.entitlements | 10 + 9 files changed, 742 insertions(+), 5 deletions(-) create mode 100644 Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme create mode 100644 Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/xcshareddata/xcschemes/Example_Extension.xcscheme create mode 100644 Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard create mode 100644 Examples/Example-iOS_Swift-Carthage/Example_Extension/Example_Extension.entitlements create mode 100644 Examples/Example-iOS_Swift-Carthage/Example_Extension/Info.plist create mode 100644 Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift create mode 100644 Examples/Example-iOS_Swift-Carthage/Source/Example.entitlements diff --git a/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj index 825c2a30f..99724e0b6 100644 --- a/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj +++ b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj @@ -7,6 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 0646249B205F026300072191 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0646249A205F026300072191 /* NotificationCenter.framework */; }; + 0646249E205F026300072191 /* TodayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0646249D205F026300072191 /* TodayViewController.swift */; }; + 064624A1205F026300072191 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0646249F205F026300072191 /* MainInterface.storyboard */; }; + 064624A5205F026300072191 /* Example_Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 06462499205F026300072191 /* Example_Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 064624AC205F02FA00072191 /* AppAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F265BD91F9AE50400DC14BF /* AppAuth.framework */; }; 9F265BD11F9AC69300DC14BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9F265BC91F9AC69300DC14BF /* Assets.xcassets */; }; 9F265BD31F9AC69300DC14BF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9F265BCB1F9AC69300DC14BF /* LaunchScreen.storyboard */; }; 9F265BD41F9AC69300DC14BF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9F265BCD1F9AC69300DC14BF /* Main.storyboard */; }; @@ -15,7 +20,38 @@ 9FD378231FB7C6F800436204 /* AppAuthExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD378221FB7C6F800436204 /* AppAuthExampleViewController.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 064624A3205F026300072191 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9F265B8F1F9AC5D600DC14BF /* Project object */; + proxyType = 1; + remoteGlobalIDString = 06462498205F026300072191; + remoteInfo = Example_Extension; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 064624A9205F026300072191 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 064624A5205F026300072191 /* Example_Extension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ + 06462499205F026300072191 /* Example_Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Example_Extension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 0646249A205F026300072191 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; + 0646249D205F026300072191 /* TodayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewController.swift; sourceTree = ""; }; + 064624A0205F026300072191 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + 064624A2205F026300072191 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 064624AD205F034A00072191 /* Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Example.entitlements; sourceTree = ""; }; + 064624AE205F035D00072191 /* Example_Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Example_Extension.entitlements; sourceTree = ""; }; 9F265B971F9AC5D600DC14BF /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9F265BC91F9AC69300DC14BF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 9F265BCC1F9AC69300DC14BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -27,6 +63,15 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 06462496205F026300072191 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 064624AC205F02FA00072191 /* AppAuth.framework in Frameworks */, + 0646249B205F026300072191 /* NotificationCenter.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9F265B941F9AC5D600DC14BF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -38,10 +83,22 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0646249C205F026300072191 /* Example_Extension */ = { + isa = PBXGroup; + children = ( + 0646249D205F026300072191 /* TodayViewController.swift */, + 0646249F205F026300072191 /* MainInterface.storyboard */, + 064624A2205F026300072191 /* Info.plist */, + 064624AE205F035D00072191 /* Example_Extension.entitlements */, + ); + path = Example_Extension; + sourceTree = ""; + }; 9F265B8E1F9AC5D600DC14BF = { isa = PBXGroup; children = ( 9F265BC81F9AC69300DC14BF /* Source */, + 0646249C205F026300072191 /* Example_Extension */, 9F265BD81F9AE4CA00DC14BF /* Frameworks */, 9F265B981F9AC5D600DC14BF /* Products */, ); @@ -51,6 +108,7 @@ isa = PBXGroup; children = ( 9F265B971F9AC5D600DC14BF /* Example.app */, + 06462499205F026300072191 /* Example_Extension.appex */, ); name = Products; sourceTree = ""; @@ -64,6 +122,7 @@ 9F265BCD1F9AC69300DC14BF /* Main.storyboard */, 9F265BC91F9AC69300DC14BF /* Assets.xcassets */, 9F265BD01F9AC69300DC14BF /* Info.plist */, + 064624AD205F034A00072191 /* Example.entitlements */, ); path = Source; sourceTree = ""; @@ -72,6 +131,7 @@ isa = PBXGroup; children = ( 9F265BD91F9AE50400DC14BF /* AppAuth.framework */, + 0646249A205F026300072191 /* NotificationCenter.framework */, ); name = Frameworks; sourceTree = ""; @@ -79,6 +139,23 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 06462498205F026300072191 /* Example_Extension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 064624A8205F026300072191 /* Build configuration list for PBXNativeTarget "Example_Extension" */; + buildPhases = ( + 06462495205F026300072191 /* Sources */, + 06462496205F026300072191 /* Frameworks */, + 06462497205F026300072191 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Example_Extension; + productName = Example_Extension; + productReference = 06462499205F026300072191 /* Example_Extension.appex */; + productType = "com.apple.product-type.app-extension"; + }; 9F265B961F9AC5D600DC14BF /* Example */ = { isa = PBXNativeTarget; buildConfigurationList = 9F265BBF1F9AC5D600DC14BF /* Build configuration list for PBXNativeTarget "Example" */; @@ -87,10 +164,12 @@ 9F265B941F9AC5D600DC14BF /* Frameworks */, 9F265B951F9AC5D600DC14BF /* Resources */, 9F265BDB1F9AE52C00DC14BF /* ShellScript */, + 064624A9205F026300072191 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + 064624A4205F026300072191 /* PBXTargetDependency */, ); name = Example; productName = Example; @@ -103,13 +182,27 @@ 9F265B8F1F9AC5D600DC14BF /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 0900; + LastSwiftUpdateCheck = 0920; LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Google Inc."; TargetAttributes = { + 06462498205F026300072191 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + }; + }; 9F265B961F9AC5D600DC14BF = { CreatedOnToolsVersion = 9.0.1; ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + }; }; }; }; @@ -127,11 +220,20 @@ projectRoot = ""; targets = ( 9F265B961F9AC5D600DC14BF /* Example */, + 06462498205F026300072191 /* Example_Extension */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 06462497205F026300072191 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 064624A1205F026300072191 /* MainInterface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9F265B951F9AC5D600DC14BF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -162,6 +264,14 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 06462495205F026300072191 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0646249E205F026300072191 /* TodayViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9F265B931F9AC5D600DC14BF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -173,7 +283,23 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 064624A4205F026300072191 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 06462498205F026300072191 /* Example_Extension */; + targetProxy = 064624A3205F026300072191 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ + 0646249F205F026300072191 /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 064624A0205F026300072191 /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; 9F265BCB1F9AC69300DC14BF /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -193,6 +319,48 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 064624A6205F026300072191 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = Example_Extension/Example_Extension.entitlements; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = Example_Extension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 064624A7205F026300072191 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = Example_Extension/Example_Extension.entitlements; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = Example_Extension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 9F265BBD1F9AC5D600DC14BF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -303,8 +471,11 @@ 9F265BC01F9AC5D600DC14BF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Source/Example.entitlements; CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", @@ -322,8 +493,11 @@ 9F265BC11F9AC5D600DC14BF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Source/Example.entitlements; CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", @@ -341,6 +515,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 064624A8205F026300072191 /* Build configuration list for PBXNativeTarget "Example_Extension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 064624A6205F026300072191 /* Debug */, + 064624A7205F026300072191 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 9F265B921F9AC5D600DC14BF /* Build configuration list for PBXProject "Example" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme new file mode 100644 index 000000000..7ffcb4c0f --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/xcshareddata/xcschemes/Example_Extension.xcscheme b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/xcshareddata/xcschemes/Example_Extension.xcscheme new file mode 100644 index 000000000..719ebd973 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/xcshareddata/xcschemes/Example_Extension.xcscheme @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard b/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard new file mode 100644 index 000000000..5d44999b1 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Example-iOS_Swift-Carthage/Example_Extension/Example_Extension.entitlements b/Examples/Example-iOS_Swift-Carthage/Example_Extension/Example_Extension.entitlements new file mode 100644 index 000000000..c250a2746 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Example_Extension/Example_Extension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.net.openid.appauth.Example + + + diff --git a/Examples/Example-iOS_Swift-Carthage/Example_Extension/Info.plist b/Examples/Example-iOS_Swift-Carthage/Example_Extension/Info.plist new file mode 100644 index 000000000..6ea3c3dc6 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Example_Extension/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Example_Extension + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.widget-extension + + + diff --git a/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift b/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift new file mode 100644 index 000000000..7f5a55088 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift @@ -0,0 +1,223 @@ +// +// TodayViewController.swift +// +// Copyright (c) 2017 The AppAuth Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import NotificationCenter +import AppAuth + +/** + NSCoding key for the authState property. + */ +let kAppAuthExampleAuthStateKey: String = "authState"; + +class TodayViewController: UIViewController, NCWidgetProviding { + + @IBOutlet private weak var logTextView: UITextView! + + private var authState: OIDAuthState? + + override func viewDidLoad() { + super.viewDidLoad() + + if let extensionContext = self.extensionContext, extensionContext.responds(to: #selector(setter: NSExtensionContext.widgetLargestAvailableDisplayMode)) { // iOS 10+ + extensionContext.widgetLargestAvailableDisplayMode = .expanded + } else { + preferredContentSize = CGSize(width: 0, height: 600.0) // iOS 10- + } + + self.loadState() + } + + func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) { + if activeDisplayMode == .expanded { + preferredContentSize = CGSize(width: maxSize.width, height: 600.0) + } else if activeDisplayMode == .compact { + preferredContentSize = maxSize + } + } + + func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) { + // Perform any setup necessary in order to update the view. + + // If an error is encountered, use NCUpdateResult.Failed + // If there's no update required, use NCUpdateResult.NoData + // If there's an update, use NCUpdateResult.NewData + + completionHandler(NCUpdateResult.newData) + } + + @IBAction func userinfo(_ sender: UIButton) { + + guard let userinfoEndpoint = self.authState?.lastAuthorizationResponse.request.configuration.discoveryDocument?.userinfoEndpoint else { + self.logMessage("Userinfo endpoint not declared in discovery document") + return + } + + self.logMessage("Performing userinfo request") + + let currentAccessToken: String? = self.authState?.lastTokenResponse?.accessToken + + self.authState?.performAction() { (accessToken, idTOken, error) in + + if error != nil { + self.logMessage("Error fetching fresh tokens: \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let accessToken = accessToken else { + self.logMessage("Error getting accessToken") + return + } + + if currentAccessToken != accessToken { + self.logMessage("Access token was refreshed automatically (\(currentAccessToken ?? "CURRENT_ACCESS_TOKEN") to \(accessToken))") + } else { + self.logMessage("Access token was fresh and not updated \(accessToken)") + } + + var urlRequest = URLRequest(url: userinfoEndpoint) + urlRequest.allHTTPHeaderFields = ["Authorization":"Bearer \(accessToken)"] + + let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in + + DispatchQueue.main.async { + + guard error == nil else { + self.logMessage("HTTP request failed \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let response = response as? HTTPURLResponse else { + self.logMessage("Non-HTTP response") + return + } + + guard let data = data else { + self.logMessage("HTTP response data is empty") + return + } + + var json: [AnyHashable: Any]? + + do { + json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + } catch { + self.logMessage("JSON Serialization Error") + } + + if response.statusCode != 200 { + // server replied with an error + let responseText: String? = String(data: data, encoding: String.Encoding.utf8) + + if response.statusCode == 401 { + // "401 Unauthorized" generally indicates there is an issue with the authorization + // grant. Puts OIDAuthState into an error state. + let oauthError = OIDErrorUtilities.resourceServerAuthorizationError(withCode: 0, + errorResponse: json, + underlyingError: error) + self.authState?.update(withAuthorizationError: oauthError) + self.logMessage("Authorization Error (\(oauthError)). Response: \(responseText ?? "RESPONSE_TEXT")") + } else { + self.logMessage("HTTP: \(response.statusCode), Response: \(responseText ?? "RESPONSE_TEXT")") + } + + return + } + + if let json = json { + self.logMessage("Success: \(json)") + } + } + } + + task.resume() + } + } + + @IBAction func clearLogTextView(_ sender: UIButton) { + self.logTextView.text = "" + } +} + +//MARK: OIDAuthState Delegate +extension TodayViewController: OIDAuthStateChangeDelegate { + + func didChange(_ state: OIDAuthState) { + self.stateChanged() + } +} + +//MARK: Helper Methods +extension TodayViewController { + + func saveState() { + + var data: Data? = nil + + if let authState = self.authState { + data = NSKeyedArchiver.archivedData(withRootObject: authState) + } + + if let userDefaults = UserDefaults(suiteName: "group.net.openid.appauth.Example") { + userDefaults.set(data, forKey: kAppAuthExampleAuthStateKey) + userDefaults.synchronize() + } + } + + func loadState() { + guard let data = UserDefaults(suiteName: "group.net.openid.appauth.Example")?.object(forKey: kAppAuthExampleAuthStateKey) as? Data else { + return + } + + if let authState = NSKeyedUnarchiver.unarchiveObject(with: data) as? OIDAuthState { + self.setAuthState(authState) + } + } + + func setAuthState(_ authState: OIDAuthState?) { + if (self.authState == authState) { + return; + } + self.authState = authState; + self.authState?.stateChangeDelegate = self; + self.stateChanged() + } + + func stateChanged() { + self.saveState() + } + + func logMessage(_ message: String?) { + + guard let message = message else { + return + } + + print(message); + + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "hh:mm:ss"; + let dateString = dateFormatter.string(from: Date()) + + // appends to output log + DispatchQueue.main.async { + let logText = "\(self.logTextView.text ?? "")\n\(dateString): \(message)" + self.logTextView.text = logText + } + } +} diff --git a/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift index fe9c27242..f70540472 100644 --- a/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift +++ b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift @@ -463,13 +463,15 @@ extension AppAuthExampleViewController { if let authState = self.authState { data = NSKeyedArchiver.archivedData(withRootObject: authState) } - - UserDefaults.standard.set(data, forKey: kAppAuthExampleAuthStateKey) - UserDefaults.standard.synchronize() + + if let userDefaults = UserDefaults(suiteName: "group.net.openid.appauth.Example") { + userDefaults.set(data, forKey: kAppAuthExampleAuthStateKey) + userDefaults.synchronize() + } } func loadState() { - guard let data = UserDefaults.standard.object(forKey: kAppAuthExampleAuthStateKey) as? Data else { + guard let data = UserDefaults(suiteName: "group.net.openid.appauth.Example")?.object(forKey: kAppAuthExampleAuthStateKey) as? Data else { return } diff --git a/Examples/Example-iOS_Swift-Carthage/Source/Example.entitlements b/Examples/Example-iOS_Swift-Carthage/Source/Example.entitlements new file mode 100644 index 000000000..c250a2746 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Source/Example.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.net.openid.appauth.Example + + + From 216c5b5e4f5d035766627b9983f2469beba4aed2 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Sun, 18 Mar 2018 18:04:46 -0400 Subject: [PATCH 133/204] Fixed warnings in storyboards + formatting --- .../Base.lproj/MainInterface.storyboard | 1 + .../Base.lproj/MainInterface.storyboard | 1 + .../TodayViewController.h | 26 +++++++++---------- .../TodayViewController.m | 26 +++++++++---------- .../Base.lproj/MainInterface.storyboard | 1 + .../TodayViewController.swift | 6 ++--- 6 files changed, 32 insertions(+), 29 deletions(-) diff --git a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard index 26c353cd6..6d96036e2 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard +++ b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard @@ -45,6 +45,7 @@ + diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard index 11e788e03..12863f62a 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard @@ -43,6 +43,7 @@ + diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.h b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.h index 2dd53762e..73d2d5e3a 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.h +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.h @@ -1,19 +1,19 @@ /*! @file TodayViewController.h - @brief AppAuth iOS SDK Example - @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 + @brief AppAuth iOS SDK Example + @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 + 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. + 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 diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m index a9baa1ce9..caf555cb6 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m @@ -1,19 +1,19 @@ /*! @file TodayViewController.m - @brief AppAuth iOS SDK Example - @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 + @brief AppAuth iOS SDK Example + @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 + 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. + 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 "TodayViewController.h" diff --git a/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard b/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard index 5d44999b1..74d4f1fa2 100644 --- a/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard +++ b/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard @@ -43,6 +43,7 @@ + diff --git a/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift b/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift index 7f5a55088..a62fd4907 100644 --- a/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift +++ b/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift @@ -34,10 +34,10 @@ class TodayViewController: UIViewController, NCWidgetProviding { override func viewDidLoad() { super.viewDidLoad() - if let extensionContext = self.extensionContext, extensionContext.responds(to: #selector(setter: NSExtensionContext.widgetLargestAvailableDisplayMode)) { // iOS 10+ - extensionContext.widgetLargestAvailableDisplayMode = .expanded + if #available(iOS 10.0, *) { + self.extensionContext?.widgetLargestAvailableDisplayMode = .expanded } else { - preferredContentSize = CGSize(width: 0, height: 600.0) // iOS 10- + self.preferredContentSize = CGSize(width: 0, height: 600.0) } self.loadState() From 636c4d96c3eec7710309f68207038d1f881a12a0 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Mon, 26 Mar 2018 19:26:50 -0400 Subject: [PATCH 134/204] Change iOS version of example's app extensions + layout changes --- .../Example.xcodeproj/project.pbxproj | 4 +- .../Base.lproj/MainInterface.storyboard | 42 +++++++++--------- .../Example_Extension/TodayViewController.m | 9 ++-- .../project.pbxproj | 4 +- .../Base.lproj/MainInterface.storyboard | 44 ++++++++++--------- .../TodayViewController.m | 9 ++-- .../Example.xcodeproj/project.pbxproj | 4 +- .../Base.lproj/MainInterface.storyboard | 32 +++++++------- .../TodayViewController.swift | 5 ++- 9 files changed, 82 insertions(+), 71 deletions(-) diff --git a/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj b/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj index 6864919ea..88f29cf14 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj +++ b/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj @@ -350,7 +350,7 @@ ); GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Example_Extension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -385,7 +385,7 @@ ); GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Example_Extension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard index 6d96036e2..c2f74e69f 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard +++ b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard @@ -1,12 +1,11 @@ - + - @@ -14,43 +13,46 @@ + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + - diff --git a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m index 2643f1747..9da9c3aba 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m +++ b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m @@ -34,24 +34,27 @@ @implementation TodayViewController - (void)viewDidLoad { [super viewDidLoad]; - if ([self.extensionContext respondsToSelector:@selector(setWidgetLargestAvailableDisplayMode:)]) { // iOS 10+ + if (@available(iOS 10.0, *)) { [self.extensionContext setWidgetLargestAvailableDisplayMode:NCWidgetDisplayModeExpanded]; } else { - self.preferredContentSize = CGSizeMake(0, 600.0); // iOS 10- + self.preferredContentSize = CGSizeMake(0, 400.0); } [self loadState]; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" - (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize { if (activeDisplayMode == NCWidgetDisplayModeExpanded) { - self.preferredContentSize = CGSizeMake(maxSize.width, 600.0); + self.preferredContentSize = CGSizeMake(maxSize.width, 400.0); } else if (activeDisplayMode == NCWidgetDisplayModeCompact) { self.preferredContentSize = maxSize; } } +#pragma clang diagnostic pop - (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { // Perform any setup necessary in order to update the view. diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj b/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj index 9b84a9b08..276f53c3f 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj @@ -516,7 +516,7 @@ DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "Example-iOS_ObjC_Extension/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-iOS-ObjC-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -548,7 +548,7 @@ DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "Example-iOS_ObjC_Extension/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-iOS-ObjC-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard index 12863f62a..67a53a714 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard @@ -1,12 +1,11 @@ - + - @@ -14,43 +13,46 @@ + + + + - - + + - - - - - - - - - + + + + + + + + + - diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m index caf555cb6..5c86136f6 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m @@ -34,24 +34,27 @@ @implementation TodayViewController - (void)viewDidLoad { [super viewDidLoad]; - if ([self.extensionContext respondsToSelector:@selector(setWidgetLargestAvailableDisplayMode:)]) { // iOS 10+ + if (@available(iOS 10, *)) { [self.extensionContext setWidgetLargestAvailableDisplayMode:NCWidgetDisplayModeExpanded]; } else { - self.preferredContentSize = CGSizeMake(0, 600.0); // iOS 10- + self.preferredContentSize = CGSizeMake(0, 400.0); } [self loadState]; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" - (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize { if (activeDisplayMode == NCWidgetDisplayModeExpanded) { - self.preferredContentSize = CGSizeMake(maxSize.width, 600.0); + self.preferredContentSize = CGSizeMake(maxSize.width, 400.0); } else if (activeDisplayMode == NCWidgetDisplayModeCompact) { self.preferredContentSize = maxSize; } } +#pragma clang diagnostic pop - (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { // Perform any setup necessary in order to update the view. diff --git a/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj index 99724e0b6..652315182 100644 --- a/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj +++ b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj @@ -330,7 +330,7 @@ "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = Example_Extension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -351,7 +351,7 @@ "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = Example_Extension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard b/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard index 74d4f1fa2..170af8176 100644 --- a/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard +++ b/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard @@ -18,37 +18,37 @@ - - + + - - - - + + + + + - - + + - diff --git a/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift b/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift index a62fd4907..d1b1c13d8 100644 --- a/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift +++ b/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift @@ -37,15 +37,16 @@ class TodayViewController: UIViewController, NCWidgetProviding { if #available(iOS 10.0, *) { self.extensionContext?.widgetLargestAvailableDisplayMode = .expanded } else { - self.preferredContentSize = CGSize(width: 0, height: 600.0) + self.preferredContentSize = CGSize(width: 0, height: 400.0) } self.loadState() } + @available(iOSApplicationExtension 10.0, *) func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) { if activeDisplayMode == .expanded { - preferredContentSize = CGSize(width: maxSize.width, height: 600.0) + preferredContentSize = CGSize(width: maxSize.width, height: 400.0) } else if activeDisplayMode == .compact { preferredContentSize = maxSize } From 10ad90e664e23cf890774b3c0d1a6bfc686455c7 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Tue, 27 Mar 2018 22:41:30 -0400 Subject: [PATCH 135/204] Add NS_AVAILABLE_IOS(10.0) for widgetActiveDisplayModeDidChange:withMaximumSize: instead of #pragma --- .../Example_Extension/TodayViewController.m | 5 +---- .../Example-iOS_ObjC_Extension/TodayViewController.m | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m index 9da9c3aba..97f9eabdc 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m +++ b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m @@ -43,10 +43,8 @@ - (void)viewDidLoad { [self loadState]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpartial-availability" - (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode - withMaximumSize:(CGSize)maxSize { + withMaximumSize:(CGSize)maxSize NS_AVAILABLE_IOS(10.0) { if (activeDisplayMode == NCWidgetDisplayModeExpanded) { self.preferredContentSize = CGSizeMake(maxSize.width, 400.0); @@ -54,7 +52,6 @@ - (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode self.preferredContentSize = maxSize; } } -#pragma clang diagnostic pop - (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { // Perform any setup necessary in order to update the view. diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m index 5c86136f6..094601c49 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m @@ -43,10 +43,8 @@ - (void)viewDidLoad { [self loadState]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpartial-availability" - (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode - withMaximumSize:(CGSize)maxSize { + withMaximumSize:(CGSize)maxSize NS_AVAILABLE_IOS(10.0) { if (activeDisplayMode == NCWidgetDisplayModeExpanded) { self.preferredContentSize = CGSizeMake(maxSize.width, 400.0); @@ -54,7 +52,6 @@ - (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode self.preferredContentSize = maxSize; } } -#pragma clang diagnostic pop - (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { // Perform any setup necessary in order to update the view. From 13b3f639f3f77cce4dcf51bc86965b46d82d4441 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Mon, 19 Nov 2018 20:08:53 -0500 Subject: [PATCH 136/204] Fix indentation --- .../Example_Extension/TodayViewController.m | 288 +++++++++--------- .../TodayViewController.m | 288 +++++++++--------- 2 files changed, 288 insertions(+), 288 deletions(-) diff --git a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m index 97f9eabdc..fbfb01380 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m +++ b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m @@ -32,191 +32,191 @@ @interface TodayViewController () 0) ? @"\n" : @"", - dateString, - log]; + // gets message as string + va_list argp; + va_start(argp, format); + NSString *log = [[NSString alloc] initWithFormat:format arguments:argp]; + va_end(argp); + + // outputs to stdout + NSLog(@"%@", log); + + // appends to output log + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = @"hh:mm:ss"; + NSString *dateString = [dateFormatter stringFromDate:[NSDate date]]; + _logTextView.text = [NSString stringWithFormat:@"%@%@%@: %@", + _logTextView.text, + ([_logTextView.text length] > 0) ? @"\n" : @"", + dateString, + log]; } - (IBAction)clearLogTextView:(UIButton *)sender { - _logTextView.text = @""; + _logTextView.text = @""; } @end diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m index 094601c49..0db7017f2 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m @@ -32,191 +32,191 @@ @interface TodayViewController () 0) ? @"\n" : @"", - dateString, - log]; + // gets message as string + va_list argp; + va_start(argp, format); + NSString *log = [[NSString alloc] initWithFormat:format arguments:argp]; + va_end(argp); + + // outputs to stdout + NSLog(@"%@", log); + + // appends to output log + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = @"hh:mm:ss"; + NSString *dateString = [dateFormatter stringFromDate:[NSDate date]]; + _logTextView.text = [NSString stringWithFormat:@"%@%@%@: %@", + _logTextView.text, + ([_logTextView.text length] > 0) ? @"\n" : @"", + dateString, + log]; } - (IBAction)clearLogTextView:(UIButton *)sender { - _logTextView.text = @""; + _logTextView.text = @""; } @end From 8993d43d509c26c2a966d07819137a0552b585c4 Mon Sep 17 00:00:00 2001 From: Julien Bodet Date: Tue, 20 Nov 2018 20:04:03 -0500 Subject: [PATCH 137/204] Configure Carthage and update Podfile --- .../Example.xcodeproj/project.pbxproj | 28 +++++++++-- .../Example_Extension/TodayViewController.m | 2 +- .../project.pbxproj | 48 ------------------- .../TodayViewController.m | 2 +- Examples/Example-iOS_ObjC/Podfile | 6 +-- .../Example.xcodeproj/project.pbxproj | 33 +++++++++++-- .../TodayViewController.swift | 2 +- 7 files changed, 59 insertions(+), 62 deletions(-) diff --git a/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj b/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj index 88f29cf14..839e13e5b 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj +++ b/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj @@ -7,11 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 06375C8521A4E46500338E3F /* AppAuthCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06375C8421A4E46500338E3F /* AppAuthCore.framework */; }; 06462457205ED68000072191 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06462456205ED68000072191 /* NotificationCenter.framework */; }; 0646245B205ED68000072191 /* TodayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0646245A205ED68000072191 /* TodayViewController.m */; }; 0646245E205ED68000072191 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0646245C205ED68000072191 /* MainInterface.storyboard */; }; 06462463205ED68000072191 /* Example_Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 06462455205ED68000072191 /* Example_Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 0646246A205EE25800072191 /* AppAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5FEA25241E75C17E00C2D71B /* AppAuth.framework */; }; 346E916E1C29D42800D3620B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 346E916D1C29D42800D3620B /* main.m */; }; 346E91711C29D42800D3620B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 346E91701C29D42800D3620B /* AppDelegate.m */; }; 346E91791C29D42800D3620B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 346E91781C29D42800D3620B /* Assets.xcassets */; }; @@ -47,6 +47,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 06375C8421A4E46500338E3F /* AppAuthCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppAuthCore.framework; path = Carthage/Build/iOS/AppAuthCore.framework; sourceTree = ""; }; 06462455205ED68000072191 /* Example_Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Example_Extension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 06462456205ED68000072191 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; 06462459205ED68000072191 /* TodayViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TodayViewController.h; sourceTree = ""; }; @@ -74,8 +75,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 06375C8521A4E46500338E3F /* AppAuthCore.framework in Frameworks */, 06462457205ED68000072191 /* NotificationCenter.framework in Frameworks */, - 0646246A205EE25800072191 /* AppAuth.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -106,6 +107,7 @@ 341564001C487ABA00ECA3D9 /* Frameworks */ = { isa = PBXGroup; children = ( + 06375C8421A4E46500338E3F /* AppAuthCore.framework */, 5FEA25241E75C17E00C2D71B /* AppAuth.framework */, 346E91981C2A245000D3620B /* SafariServices.framework */, 06462456205ED68000072191 /* NotificationCenter.framework */, @@ -159,6 +161,7 @@ 06462451205ED68000072191 /* Sources */, 06462452205ED68000072191 /* Frameworks */, 06462453205ED68000072191 /* Resources */, + 06375C8621A4E48800338E3F /* Carthage */, ); buildRules = ( ); @@ -258,6 +261,25 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 06375C8621A4E48800338E3F /* Carthage */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/AppAuth.framework", + ); + name = Carthage; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; + }; 5FEA25261E75C1CF00C2D71B /* Carthage */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -271,7 +293,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/usr/local/bin/carthage copy-frameworks"; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m index fbfb01380..82681feb5 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m +++ b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/TodayViewController.m @@ -18,7 +18,7 @@ #import "TodayViewController.h" #import -#import +#import static NSString *const kAppAuthExampleAuthStateKey = @"authState"; diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj b/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj index 276f53c3f..eb38b576d 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj @@ -215,7 +215,6 @@ 06D481292055C3D400D9DC32 /* Sources */, 06D4812A2055C3D400D9DC32 /* Frameworks */, 06D4812B2055C3D400D9DC32 /* Resources */, - F998F170E4D14F53FCAA2B07 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -252,8 +251,6 @@ 346E91651C29D42800D3620B /* Sources */, 346E91661C29D42800D3620B /* Frameworks */, 346E91671C29D42800D3620B /* Resources */, - 0B0B46F67786AB6E322D5F2B /* [CP] Embed Pods Frameworks */, - 3CB1FD4B438DD277394F39A7 /* [CP] Copy Pods Resources */, 06D4813E2055C3D400D9DC32 /* Embed App Extensions */, ); buildRules = ( @@ -348,36 +345,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0B0B46F67786AB6E322D5F2B /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example-iOS_ObjC/Pods-Example-iOS_ObjC-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 3CB1FD4B438DD277394F39A7 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example-iOS_ObjC/Pods-Example-iOS_ObjC-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 836219F293F319703A16E68D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -414,21 +381,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - F998F170E4D14F53FCAA2B07 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example-iOS_ObjC_Extension/Pods-Example-iOS_ObjC_Extension-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m index 0db7017f2..1bfbd96ed 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m @@ -18,7 +18,7 @@ #import "TodayViewController.h" #import -#import +#import static NSString *const kAppAuthExampleAuthStateKey = @"authState"; diff --git a/Examples/Example-iOS_ObjC/Podfile b/Examples/Example-iOS_ObjC/Podfile index bf21b203a..a4354d010 100644 --- a/Examples/Example-iOS_ObjC/Podfile +++ b/Examples/Example-iOS_ObjC/Podfile @@ -7,7 +7,7 @@ target 'Example-iOS_ObjC' do end target 'Example-iOS_ObjC_Extension' do - # AppAuth Pod - # In production, just use `pod 'AppAuth'` without the path reference. - pod 'AppAuth', :path => '../../' + # AppAuth/Core Pod + # In production, just use `pod 'AppAuth/Core'` without the path reference. + pod 'AppAuth/Core', :path => '../../' end diff --git a/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj index 652315182..33f25f87b 100644 --- a/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj +++ b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj @@ -7,11 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 06375C8821A4E50E00338E3F /* AppAuthCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06375C8721A4E50E00338E3F /* AppAuthCore.framework */; }; 0646249B205F026300072191 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0646249A205F026300072191 /* NotificationCenter.framework */; }; 0646249E205F026300072191 /* TodayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0646249D205F026300072191 /* TodayViewController.swift */; }; 064624A1205F026300072191 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0646249F205F026300072191 /* MainInterface.storyboard */; }; 064624A5205F026300072191 /* Example_Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 06462499205F026300072191 /* Example_Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 064624AC205F02FA00072191 /* AppAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F265BD91F9AE50400DC14BF /* AppAuth.framework */; }; 9F265BD11F9AC69300DC14BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9F265BC91F9AC69300DC14BF /* Assets.xcassets */; }; 9F265BD31F9AC69300DC14BF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9F265BCB1F9AC69300DC14BF /* LaunchScreen.storyboard */; }; 9F265BD41F9AC69300DC14BF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9F265BCD1F9AC69300DC14BF /* Main.storyboard */; }; @@ -45,6 +45,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 06375C8721A4E50E00338E3F /* AppAuthCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppAuthCore.framework; path = Carthage/Build/iOS/AppAuthCore.framework; sourceTree = ""; }; 06462499205F026300072191 /* Example_Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Example_Extension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 0646249A205F026300072191 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; 0646249D205F026300072191 /* TodayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewController.swift; sourceTree = ""; }; @@ -67,7 +68,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 064624AC205F02FA00072191 /* AppAuth.framework in Frameworks */, + 06375C8821A4E50E00338E3F /* AppAuthCore.framework in Frameworks */, 0646249B205F026300072191 /* NotificationCenter.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -130,6 +131,7 @@ 9F265BD81F9AE4CA00DC14BF /* Frameworks */ = { isa = PBXGroup; children = ( + 06375C8721A4E50E00338E3F /* AppAuthCore.framework */, 9F265BD91F9AE50400DC14BF /* AppAuth.framework */, 0646249A205F026300072191 /* NotificationCenter.framework */, ); @@ -146,6 +148,7 @@ 06462495205F026300072191 /* Sources */, 06462496205F026300072191 /* Frameworks */, 06462497205F026300072191 /* Resources */, + 06375C8921A4E56B00338E3F /* Carthage */, ); buildRules = ( ); @@ -163,7 +166,7 @@ 9F265B931F9AC5D600DC14BF /* Sources */, 9F265B941F9AC5D600DC14BF /* Frameworks */, 9F265B951F9AC5D600DC14BF /* Resources */, - 9F265BDB1F9AE52C00DC14BF /* ShellScript */, + 9F265BDB1F9AE52C00DC14BF /* Carthage */, 064624A9205F026300072191 /* Embed App Extensions */, ); buildRules = ( @@ -247,7 +250,26 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 9F265BDB1F9AE52C00DC14BF /* ShellScript */ = { + 06375C8921A4E56B00338E3F /* Carthage */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/AppAuth.framework", + ); + name = Carthage; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; + }; + 9F265BDB1F9AE52C00DC14BF /* Carthage */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -255,11 +277,12 @@ inputPaths = ( "$(SRCROOT)/Carthage/Build/iOS/AppAuth.framework", ); + name = Carthage; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/usr/local/bin/carthage copy-frameworks"; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift b/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift index d1b1c13d8..fc663d1bd 100644 --- a/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift +++ b/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift @@ -18,7 +18,7 @@ import UIKit import NotificationCenter -import AppAuth +import AppAuthCore /** NSCoding key for the authState property. From 88136c310534caa9c32da410bbdb2a877bd15d64 Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Fri, 14 Aug 2020 19:38:12 -0400 Subject: [PATCH 138/204] Rename TVAuthorizationEndpoint to deviceAuthorizationEndpoint --- .../AppAuthTVExampleViewController.m | 16 +++---- Source/AppAuthTV/OIDTVAuthorizationRequest.m | 2 +- Source/AppAuthTV/OIDTVServiceConfiguration.h | 15 +++--- Source/AppAuthTV/OIDTVServiceConfiguration.m | 28 +++++------ .../OIDTVAuthorizationRequestTests.h | 8 ++-- .../OIDTVAuthorizationRequestTests.m | 46 +++++++++---------- .../OIDTVAuthorizationResponseTests.m | 8 ++-- UnitTests/AppAuthTV/OIDTVTokenRequestTests.m | 22 ++++----- 8 files changed, 73 insertions(+), 72 deletions(-) diff --git a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m index c65ee4e0d..3d461619d 100644 --- a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m +++ b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m @@ -22,7 +22,7 @@ #import /*! @brief Indicates whether YES to discover endpoints from @c kIssuer or NO to use the - @c kTVAuthorizationEndpoint, @c kTokenEndpoint, and @c kUserInfoEndpoint values defined + @c kDeviceAuthorizationEndpoint, @c kTokenEndpoint, and @c kUserInfoEndpoint values defined below. */ static BOOL const shouldDiscoverEndpoints = YES; @@ -41,7 +41,7 @@ /*! @brief Device authorization endpoint. */ -static NSString *const kTVAuthorizationEndpoint = @"https://www.example.com/device"; +static NSString *const kDeviceAuthorizationEndpoint = @"https://www.example.com/device"; /*! @brief Token endpoint. */ @@ -99,8 +99,8 @@ - (void)verifyConfig { "Instructions: " "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); } else { - NSAssert(![kTVAuthorizationEndpoint isEqualToString:@"https://www.example.com/device"], - @"Update kTVAuthorizationEndpoint with your own TV authorization endpoint. " + NSAssert(![kDeviceAuthorizationEndpoint isEqualToString:@"https://www.example.com/device"], + @"Update kDeviceAuthorizationEndpoint with your own device authorization endpoint. " "Instructions: " "https://github.com/openid/AppAuth-iOS/blob/master/Examples/Example-tvOS/README.md"); @@ -155,12 +155,12 @@ - (IBAction)signin:(id)sender { [self performAuthorizationWithConfiguration:configuration]; }]; } else { - NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTVAuthorizationEndpoint]; + NSURL *deviceAuthorizationEndpoint = [NSURL URLWithString:kDeviceAuthorizationEndpoint]; NSURL *tokenEndpoint = [NSURL URLWithString:kTokenEndpoint]; - OIDTVServiceConfiguration *configuration = - [[OIDTVServiceConfiguration alloc] initWithTVAuthorizationEndpoint:TVAuthorizationEndpoint - tokenEndpoint:tokenEndpoint]; + OIDTVServiceConfiguration *configuration = [[OIDTVServiceConfiguration alloc] + initWithDeviceAuthorizationEndpoint:deviceAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; // Perform authorization flow [self performAuthorizationWithConfiguration:configuration]; diff --git a/Source/AppAuthTV/OIDTVAuthorizationRequest.m b/Source/AppAuthTV/OIDTVAuthorizationRequest.m index 5d085beb2..da524d388 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationRequest.m +++ b/Source/AppAuthTV/OIDTVAuthorizationRequest.m @@ -105,7 +105,7 @@ - (NSURLRequest *)URLRequest { OIDTVServiceConfiguration *tvConfiguration = (OIDTVServiceConfiguration *)self.configuration; NSMutableURLRequest *URLRequest = - [[NSURLRequest requestWithURL:tvConfiguration.TVAuthorizationEndpoint] mutableCopy]; + [[NSURLRequest requestWithURL:tvConfiguration.deviceAuthorizationEndpoint] mutableCopy]; URLRequest.HTTPMethod = kHTTPPost; [URLRequest setValue:kHTTPContentTypeHeaderValue forHTTPHeaderField:kHTTPContentTypeHeaderKey]; NSString *bodyString = [query URLEncodedParameters]; diff --git a/Source/AppAuthTV/OIDTVServiceConfiguration.h b/Source/AppAuthTV/OIDTVServiceConfiguration.h index bdedcdd10..2afea6cac 100644 --- a/Source/AppAuthTV/OIDTVServiceConfiguration.h +++ b/Source/AppAuthTV/OIDTVServiceConfiguration.h @@ -24,19 +24,19 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDTVServiceConfiguration : OIDServiceConfiguration -/*! @brief The TV authorization endpoint URI. +/*! @brief The device authorization endpoint URI. */ -@property(nonatomic, readonly) NSURL *TVAuthorizationEndpoint; +@property(nonatomic, readonly) NSURL *deviceAuthorizationEndpoint; /*! @internal @brief Unavailable. Please use - @c initWithTVAuthorizationEndpoint:tokenEndpoint: + @c initWithDeviceAuthorizationEndpoint:tokenEndpoint: */ - (instancetype)init NS_UNAVAILABLE; /*! @internal @brief Unavailable. Please use - @c initWithTVAuthorizationEndpoint:tokenEndpoint: + @c initWithDeviceAuthorizationEndpoint:tokenEndpoint: */ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint NS_UNAVAILABLE; @@ -49,11 +49,12 @@ NS_ASSUME_NONNULL_BEGIN NS_DESIGNATED_INITIALIZER; /*! @brief Designated initializer. - @param TVAuthorizationEndpoint The TV authorization endpoint URI. + @param deviceAuthorizationEndpoint The device authorization endpoint URI. @param tokenEndpoint The token exchange and refresh endpoint URI. */ -- (instancetype)initWithTVAuthorizationEndpoint:(NSURL *)TVAuthorizationEndpoint - tokenEndpoint:(NSURL *)tokenEndpoint NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithDeviceAuthorizationEndpoint:(NSURL *)deviceAuthorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint + NS_DESIGNATED_INITIALIZER; @end diff --git a/Source/AppAuthTV/OIDTVServiceConfiguration.m b/Source/AppAuthTV/OIDTVServiceConfiguration.m index c20883d18..202aa9104 100644 --- a/Source/AppAuthTV/OIDTVServiceConfiguration.m +++ b/Source/AppAuthTV/OIDTVServiceConfiguration.m @@ -21,9 +21,9 @@ #import "OIDDefines.h" #import "OIDServiceDiscovery.h" -/*! @brief The key for the @c TVAuthorizationEndpoint property. +/*! @brief The key for the @c deviceAuthorizationEndpoint property. */ -static NSString *const kTVAuthorizationEndpointKey = @"TVAuthorizationEndpoint"; +static NSString *const kDeviceAuthorizationEndpointKey = @"deviceAuthorizationEndpoint"; NS_ASSUME_NONNULL_BEGIN @@ -39,11 +39,11 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIAL @implementation OIDTVServiceConfiguration - (instancetype)init - OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithTVAuthorizationEndpoint:tokenEndpoint:)) + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithDeviceAuthorizationEndpoint:tokenEndpoint:)) - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint - OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithTVAuthorizationEndpoint:tokenEndpoint:)) + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithDeviceAuthorizationEndpoint:tokenEndpoint:)) - (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *)discoveryDocument { self = [super initWithDiscoveryDocument:discoveryDocument]; @@ -53,18 +53,18 @@ - (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *)discoveryDocume NSLog(@"Warning: Discovery document used to initialize %@ " @"does not contain device authorization endpoint.", self); } else { - _TVAuthorizationEndpoint = [discoveryDocument.deviceAuthorizationEndpoint copy]; + _deviceAuthorizationEndpoint = [discoveryDocument.deviceAuthorizationEndpoint copy]; } } return self; } -- (instancetype)initWithTVAuthorizationEndpoint:(NSURL *)TVAuthorizationEndpoint - tokenEndpoint:(NSURL *)tokenEndpoint { +- (instancetype)initWithDeviceAuthorizationEndpoint:(NSURL *)deviceAuthorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint { self = [super initWithAuthorizationEndpoint:[[NSURL alloc] initWithString:@""] tokenEndpoint:tokenEndpoint]; if (self) { - _TVAuthorizationEndpoint = [TVAuthorizationEndpoint copy]; + _deviceAuthorizationEndpoint = [deviceAuthorizationEndpoint copy]; } return self; } @@ -78,25 +78,25 @@ + (BOOL)supportsSecureCoding { - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { - NSURL *TVAuthorizationEndpoint = [aDecoder decodeObjectOfClass:[NSURL class] - forKey:kTVAuthorizationEndpointKey]; - _TVAuthorizationEndpoint = TVAuthorizationEndpoint; + NSURL *deviceAuthorizationEndpoint = + [aDecoder decodeObjectOfClass:[NSURL class] forKey:kDeviceAuthorizationEndpointKey]; + _deviceAuthorizationEndpoint = deviceAuthorizationEndpoint; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [super encodeWithCoder:aCoder]; - [aCoder encodeObject:_TVAuthorizationEndpoint forKey:kTVAuthorizationEndpointKey]; + [aCoder encodeObject:_deviceAuthorizationEndpoint forKey:kDeviceAuthorizationEndpointKey]; } #pragma mark - description - (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p, TVAuthorizationEndpoint: %@ tokenEndpoint: %@>", + return [NSString stringWithFormat:@"<%@: %p, deviceAuthorizationEndpoint: %@ tokenEndpoint: %@>", NSStringFromClass([self class]), (void *)self, - _TVAuthorizationEndpoint, + _deviceAuthorizationEndpoint, self.tokenEndpoint]; } diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.h b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.h index 4a2fb49b0..d4198a448 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.h +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.h @@ -33,14 +33,14 @@ NS_ASSUME_NONNULL_BEGIN - (void)testInitializer; /*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying - * process and checking to make sure the source and destination both contain the - * @c TVAuthorizationEndpoint + * process and checking to make sure the source and destination both contain the + * @c deviceAuthorizationEndpoint */ - (void)testCopying; /*! @brief Tests the @c NSSecureCoding implementation by round-tripping an instance through the - * coding process and checking to make sure the source and destination both contain the - * @c TVAuthorizationEndpoint + * coding process and checking to make sure the source and destination both contain the + * @c deviceAuthorizationEndpoint */ - (void)testSecureCoding; diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m index b86869c34..7b1d19c95 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m @@ -32,9 +32,9 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wgnu" -/*! @brief Test value for the @c TVAuthorizationEndpoint property. +/*! @brief Test value for the @c deviceAuthorizationEndpoint property. */ -static NSString *const kTestTVAuthorizationEndpoint = @"https://www.example.com/device/code"; +static NSString *const kTestDeviceAuthorizationEndpoint = @"https://www.example.com/device/code"; /*! @brief Test value for the @c tokenEndpoint property. */ @@ -89,10 +89,10 @@ @implementation OIDTVAuthorizationRequestTests - (OIDTVServiceConfiguration *)testServiceConfiguration { NSURL *tokenEndpoint = [NSURL URLWithString:kTestTokenEndpoint]; - NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTestTVAuthorizationEndpoint]; + NSURL *deviceAuthorizationEndpoint = [NSURL URLWithString:kTestDeviceAuthorizationEndpoint]; OIDTVServiceConfiguration *configuration = - [[OIDTVServiceConfiguration alloc] initWithTVAuthorizationEndpoint:TVAuthorizationEndpoint + [[OIDTVServiceConfiguration alloc] initWithDeviceAuthorizationEndpoint:deviceAuthorizationEndpoint tokenEndpoint:tokenEndpoint]; return configuration; } @@ -131,8 +131,8 @@ - (void)testInitializer { scopes:testScopes additionalParameters:testAdditionalParameters]; - NSURL *authRequestTVAuthorizationEndpoint = - ((OIDTVServiceConfiguration *)authRequest.configuration).TVAuthorizationEndpoint; + NSURL *authRequestDeviceAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)authRequest.configuration).deviceAuthorizationEndpoint; XCTAssertEqualObjects(authRequest.clientID, kTestClientID); XCTAssertEqualObjects(authRequest.clientSecret, kTestClientSecret); @@ -140,13 +140,13 @@ - (void)testInitializer { XCTAssertEqualObjects(authRequest.additionalParameters, testAdditionalParameters); XCTAssertEqualObjects(authRequest.responseType, OIDResponseTypeCode); XCTAssertEqualObjects(authRequest.redirectURL, [[NSURL alloc] initWithString:@""]); - XCTAssertEqualObjects(authRequestTVAuthorizationEndpoint, - serviceConfiguration.TVAuthorizationEndpoint); + XCTAssertEqualObjects(authRequestDeviceAuthorizationEndpoint, + serviceConfiguration.deviceAuthorizationEndpoint); } /*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying - * process and checking to make sure the source and destination both contain the - * @c TVAuthorizationEndpoint + * process and checking to make sure the source and destination both contain the + * @c deviceAuthorizationEndpoint */ - (void)testCopying { OIDTVServiceConfiguration *serviceConfiguration = [self testServiceConfiguration]; @@ -159,16 +159,16 @@ - (void)testCopying { additionalParameters:nil]; OIDTVAuthorizationRequest *authRequestCopy = [authRequest copy]; - NSURL *authRequestCopyTVAuthorizationEndpoint = - ((OIDTVServiceConfiguration *)authRequestCopy.configuration).TVAuthorizationEndpoint; + NSURL *authRequestCopyDeviceAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)authRequestCopy.configuration).deviceAuthorizationEndpoint; - XCTAssertEqualObjects(authRequestCopyTVAuthorizationEndpoint, - serviceConfiguration.TVAuthorizationEndpoint); + XCTAssertEqualObjects(authRequestCopyDeviceAuthorizationEndpoint, + serviceConfiguration.deviceAuthorizationEndpoint); } /*! @brief Tests the @c NSSecureCoding implementation by round-tripping an instance through the - * coding process and checking to make sure the source and destination both contain the - * @c TVAuthorizationEndpoint + * coding process and checking to make sure the source and destination both contain the + * @c deviceAuthorizationEndpoint */ - (void)testSecureCoding { OIDTVServiceConfiguration *serviceConfiguration = [self testServiceConfiguration]; @@ -183,11 +183,11 @@ - (void)testSecureCoding { NSData *data = [NSKeyedArchiver archivedDataWithRootObject:authRequest]; OIDTVAuthorizationRequest *authRequestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - NSURL *authRequestCopyTVAuthorizationEndpoint = - ((OIDTVServiceConfiguration *)authRequestCopy.configuration).TVAuthorizationEndpoint; + NSURL *authRequestCopyDeviceAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)authRequestCopy.configuration).deviceAuthorizationEndpoint; - XCTAssertEqualObjects(authRequestCopyTVAuthorizationEndpoint, - serviceConfiguration.TVAuthorizationEndpoint); + XCTAssertEqualObjects(authRequestCopyDeviceAuthorizationEndpoint, + serviceConfiguration.deviceAuthorizationEndpoint); } /*! @brief Tests the @c URLRequest method on a request with no scopes or additional parameters @@ -207,7 +207,7 @@ - (void)testURLRequestBasicClientAuth { XCTAssertEqualObjects(URLRequest.HTTPMethod, kHTTPPost); XCTAssertEqualObjects([URLRequest valueForHTTPHeaderField:kHTTPContentTypeHeaderKey], kHTTPContentTypeHeaderValue); - XCTAssertEqualObjects(URLRequest.URL, serviceConfiguration.TVAuthorizationEndpoint); + XCTAssertEqualObjects(URLRequest.URL, serviceConfiguration.deviceAuthorizationEndpoint); NSDictionary *bodyParameters = [self bodyParametersFromURLRequest:URLRequest]; @@ -238,7 +238,7 @@ - (void)testURLRequestScopes { XCTAssertEqualObjects([URLRequest HTTPMethod], kHTTPPost); XCTAssertEqualObjects([URLRequest valueForHTTPHeaderField:kHTTPContentTypeHeaderKey], kHTTPContentTypeHeaderValue); - XCTAssertEqualObjects(URLRequest.URL, serviceConfiguration.TVAuthorizationEndpoint); + XCTAssertEqualObjects(URLRequest.URL, serviceConfiguration.deviceAuthorizationEndpoint); NSDictionary *bodyParameters = [self bodyParametersFromURLRequest:URLRequest]; @@ -269,7 +269,7 @@ - (void)testURLRequestAdditionalParams { XCTAssertEqualObjects([URLRequest HTTPMethod], kHTTPPost); XCTAssertEqualObjects([URLRequest valueForHTTPHeaderField:kHTTPContentTypeHeaderKey], kHTTPContentTypeHeaderValue); - XCTAssertEqualObjects(URLRequest.URL, serviceConfiguration.TVAuthorizationEndpoint); + XCTAssertEqualObjects(URLRequest.URL, serviceConfiguration.deviceAuthorizationEndpoint); NSDictionary *bodyParameters = [self bodyParametersFromURLRequest:URLRequest]; diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m index 00612fd8f..555f6e177 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m @@ -29,9 +29,9 @@ #import "Source/AppAuthTV/OIDTVTokenRequest.h" #endif -/*! @brief Test value for the @c TVAuthorizationEndpoint property. +/*! @brief Test value for the @c deviceAuthorizationEndpoint property. */ -static NSString *const kTestTVAuthorizationEndpoint = @"https://www.example.com/device/code"; +static NSString *const kTestDeviceAuthorizationEndpoint = @"https://www.example.com/device/code"; /*! @brief Test value for the @c tokenEndpoint property. */ @@ -110,10 +110,10 @@ @implementation OIDTVAuthorizationResponseTests - (OIDTVServiceConfiguration *)testServiceConfiguration { NSURL *tokenEndpoint = [NSURL URLWithString:kTestTokenEndpoint]; - NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTestTVAuthorizationEndpoint]; + NSURL *deviceAuthorizationEndpoint = [NSURL URLWithString:kTestDeviceAuthorizationEndpoint]; OIDTVServiceConfiguration *configuration = - [[OIDTVServiceConfiguration alloc] initWithTVAuthorizationEndpoint:TVAuthorizationEndpoint + [[OIDTVServiceConfiguration alloc] initWithDeviceAuthorizationEndpoint:deviceAuthorizationEndpoint tokenEndpoint:tokenEndpoint]; return configuration; } diff --git a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m index eecb48d35..cf0bf4963 100644 --- a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m +++ b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m @@ -33,9 +33,9 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wgnu" -/*! @brief Test value for the @c TVAuthorizationEndpoint property. +/*! @brief Test value for the @c deviceAuthorizationEndpoint property. */ -static NSString *const kTestTVAuthorizationEndpoint = +static NSString *const kTestDeviceAuthorizationEndpoint = @"https://www.example.com/device/code"; /*! @brief Test value for the @c tokenEndpoint property. @@ -106,11 +106,11 @@ @implementation OIDTVTokenRequestTests - (OIDTVServiceConfiguration *)testServiceConfiguration { NSURL *tokenEndpoint = [NSURL URLWithString:kTestTokenEndpoint]; - NSURL *TVAuthorizationEndpoint = [NSURL URLWithString:kTestTVAuthorizationEndpoint]; + NSURL *deviceAuthorizationEndpoint = [NSURL URLWithString:kTestDeviceAuthorizationEndpoint]; - OIDTVServiceConfiguration *configuration = - [[OIDTVServiceConfiguration alloc] initWithTVAuthorizationEndpoint:TVAuthorizationEndpoint - tokenEndpoint:tokenEndpoint]; + OIDTVServiceConfiguration *configuration = [[OIDTVServiceConfiguration alloc] + initWithDeviceAuthorizationEndpoint:deviceAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; return configuration; } @@ -128,11 +128,11 @@ - (OIDTVTokenRequest *)testTokenRequest { */ - (void)testInitializer { OIDTVTokenRequest *request = [self testTokenRequest]; - NSURL *requestTVAuthorizationEndpoint = - ((OIDTVServiceConfiguration *)request.configuration).TVAuthorizationEndpoint; - - XCTAssertEqualObjects(requestTVAuthorizationEndpoint, - [self testServiceConfiguration].TVAuthorizationEndpoint); + NSURL *requestDeviceAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)request.configuration).deviceAuthorizationEndpoint; + + XCTAssertEqualObjects(requestDeviceAuthorizationEndpoint, + [self testServiceConfiguration].deviceAuthorizationEndpoint); XCTAssertEqualObjects(request.deviceCode, kDeviceCodeValue); XCTAssertEqualObjects(request.grantType, kOIDTVDeviceTokenGrantType); XCTAssertEqualObjects(request.clientID, kTestClientID); From e191b1beadf3041259652e6e464eaceff16e5fbb Mon Sep 17 00:00:00 2001 From: Souleiman Benhida Date: Fri, 14 Aug 2020 19:57:07 -0400 Subject: [PATCH 139/204] Document tvOS support in READMEs - Create a tvOS example README - Document tvOS support in main README - Link to tvOS example from the example README - Add Google IdP tvOS details --- Examples/Example-tvOS/README.md | 64 ++++++++++++++ Examples/README-Google.md | 12 ++- Examples/README.md | 1 + README.md | 143 +++++++++++++++++++++++++++++--- 4 files changed, 207 insertions(+), 13 deletions(-) create mode 100644 Examples/Example-tvOS/README.md diff --git a/Examples/Example-tvOS/README.md b/Examples/Example-tvOS/README.md new file mode 100644 index 000000000..4787247b6 --- /dev/null +++ b/Examples/Example-tvOS/README.md @@ -0,0 +1,64 @@ +# Example Project + +## Setup & Open the Project + +1. In the `Example-tvOS` folder, run the following command to install the AppAuth pod with the TV +subspec. + +``` +pod install +``` + +2. Open the `Example-tvOS.xcworkspace` workspace. + +``` +open Example-tvOS.xcworkspace +``` + +This workspace is configured to include AppAuth via CocoaPods. You can also include AppAuthTV using +Carthage or Swift Package Manager, please see the main [README](../../README.md) for instructions. + +## Configuration + +The example doesn't work out of the box; you need to configure it with your own client and IdP details. + +### Information You'll Need + +* Client ID +* Client Secret (optional) + +If you are choosing to automatically discover endpoints: + +* Issuer URL + +If you are choosing to manually specify endpoints: + +* Device Authorization Endpoint +* Token Endpoint +* User Info Endpoint + +How to get this information varies by IdP, but we have +[instructions](../README.md#openid-certified-providers) for some OpenID Certified providers. + +### Configure the Example + +#### In the file `AppAuthTVExampleViewController.m` + +1. Update `kClientID` with your new client ID. +2. Update `kClientSecret` with your client ID's secret, or set to `""` if not using. + +If you are choosing to automatically discover endpoints, also: + +1. Update `kIssuer` with the issuer URL. +2. Set `shouldDiscoverEndpoints` to `YES` + +If you are choosing to manually specify endpoints, also: + +1. Set `shouldDiscoverEndpoints` to `NO` +2. Update `kDeviceAuthorizationEndpoint` with the device authorization endpoint. +3. Update `kTokenEndpoint` with the token endpoint. +4. Update `kUserInfoEndpoint` with the token endpoint. + +### Running the Example + +Now your example should be ready to run. diff --git a/Examples/README-Google.md b/Examples/README-Google.md index 5a9c3f720..5b572b60b 100644 --- a/Examples/README-Google.md +++ b/Examples/README-Google.md @@ -21,7 +21,6 @@ Then, setup the example with your configuration: | Client ID | The value named `Client ID` in the console, has the format `IDENTIFIER.apps.googleusercontent.com`.| | Client Secret | Google's iOS clients do not have a secret.| | Redirect URI | The value for `iOS URL scheme` wil be the scheme of your redirect URI. This is the Client ID in reverse domain name notation, e.g. ` com.googleusercontent.apps.IDENTIFIER`. To construct the redirect URI, add your own path component. E.g. ` com.googleusercontent.apps.IDENTIFIER:/oauth2redirect/google`. Note that there is only a single slash (`/`) after the scheme.| -| ## macOS @@ -36,3 +35,14 @@ Then, setup the example with your configuration: | Client Secret | The value named `Client secret` in the console.| | Redirect URI | For macOS, you can use either the loopback interface (where AppAuth will generate the redirect URI for you), or a custom scheme. To create a custom scheme redirect URI, reverse the client id to get the URI scheme, for example ` com.googleusercontent.apps.IDENTIFIER` and, add your own path component. E.g. `com.googleusercontent.apps.IDENTIFIER:/oauth2redirect/google`. Note that there is only a single slash (`/`) after the scheme.| +## tvOS + +Select "TVs and Limited Input devices" as the application type. + +Then, setup the example with your configuration. + +| Configuration | Description | +|---------------------------|------------------| +| Issuer | `https://accounts.google.com` | +| Client ID | The value named `Client ID` in the console, has the format `IDENTIFIER.apps.googleusercontent.com`.| +| Client Secret | The value named `Client secret` in the console.| diff --git a/Examples/README.md b/Examples/README.md index 2361d3d49..820c0d5bb 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -11,6 +11,7 @@ Each example has docs on how to configure: * [Example for iOS (Objective-C)](Example-iOS_ObjC/README.md) * [Example for iOS w/ Carthage (Objective-C)](Example-iOS_ObjC-Carthage/README.md) * [Example for macOS](Example-macOS/README.md) +* [Example for tvOS](Example-tvOS/README.md) To get the Issuer, Client ID, and Redirect URI, for your particular IdP, you may view the IdP-specific information in the next section. diff --git a/README.md b/README.md index 50ffc204a..051223462 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![Pod Platform](https://img.shields.io/cocoapods/p/AppAuth.svg?style=flat)](https://cocoapods.org/pods/AppAuth) [![Catalyst compatible](https://img.shields.io/badge/Catalyst-compatible-brightgreen.svg?style=flat)](https://developer.apple.com/documentation/xcode/creating_a_mac_version_of_your_ipad_app) -AppAuth for iOS and macOS is a client SDK for communicating with +AppAuth for iOS and macOS, and tvOS is a client SDK for communicating with [OAuth 2.0](https://tools.ietf.org/html/rfc6749) and [OpenID Connect](http://openid.net/specs/openid-connect-core-1_0.html) providers. It strives to @@ -30,6 +30,9 @@ custom URI scheme redirects are used. The library is friendly to other extensions (standard or otherwise), with the ability to handle additional params in all protocol requests and responses. +For tvOS, AppAuth implements [OAuth 2.0 Device Authorization Grant +](https://tools.ietf.org/html/rfc8628) to allow for tvOS sign-ins through a secondary device. + ## Specification ### iOS @@ -70,6 +73,16 @@ either through custom URI schemes, or loopback HTTP redirects. Authorization servers that assume all clients are web-based, or require clients to maintain confidentiality of the client secrets may not work well. +### tvOS + +#### Supported Versions + +AppAuth supports tvOS 9.0 and above. Please note that while it is possible to run the standard AppAuth library on tvOS, the documentation below describes implementing [OAuth 2.0 Device Authorization Grant](https://tools.ietf.org/html/rfc8628) (AppAuthTV). + +#### Authorization Server Requirements + +AppAuthTV is designed for servers that support the device authorization flow as documented in [RFC 8628](https://tools.ietf.org/html/rfc8628). + ## Try Want to try out AppAuth? Just run: @@ -84,6 +97,20 @@ client info to try the demo). AppAuth supports four options for dependency management. +### CocoaPods + +With [CocoaPods](https://guides.cocoapods.org/using/getting-started.html), +add the following line to your `Podfile`: + + pod 'AppAuth' + +Then, run `pod install`. + +**tvOS:** Use the `TV` subspec: + + pod 'AppAuth/TV' + + ### Swift Package Manager With [Swift Package Manager](https://swift.org/package-manager), @@ -95,14 +122,7 @@ dependencies: [ ] ``` -### CocoaPods - -With [CocoaPods](https://guides.cocoapods.org/using/getting-started.html), -add the following line to your `Podfile`: - - pod 'AppAuth' - -Then, run `pod install`. +**tvOS:** Use the `AppAuthTV` target. ### Carthage @@ -113,6 +133,8 @@ line to your `Cartfile`: Then, run `carthage bootstrap`. +**tvOS:** Use the `AppAuthTV` framework. + ### Static Library You can also use AppAuth as a static library. This requires linking the library @@ -125,6 +147,8 @@ Linked Framework and Libraries" section of your target). 4. Add `AppAuth-iOS/Source` to your search paths of your target ("Build Settings -> "Header Search Paths"). +*Note: There is no static library for AppAuthTV.* + ## Auth Flow AppAuth supports both manual interaction with the authorization server @@ -166,6 +190,24 @@ let configuration = OIDServiceConfiguration(authorizationEndpoint: authorization // perform the auth request... ``` +**tvOS** + +Objective-C +```objc +NSURL *deviceAuthorizationEndpoint = + [NSURL URLWithString:@"https://oauth2.googleapis.com/device/code"]; +NSURL *tokenEndpoint = + [NSURL URLWithString:@"https://www.googleapis.com/oauth2/v4/token"]; + +OIDTVServiceConfiguration *configuration = + [[OIDTVServiceConfiguration alloc] + initWithDeviceAuthorizationEndpoint:deviceAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; + +// perform the auth request... +``` + + Or through discovery: Objective-C @@ -201,6 +243,26 @@ OIDAuthorizationService.discoverConfiguration(forIssuer: issuer) { configuration } ``` +**tvOS** + +Objective-C +```objc +NSURL *issuer = [NSURL URLWithString:@"https://accounts.google.com"]; + +[OIDTVAuthorizationService discoverServiceConfigurationForIssuer:issuer + completion:^(OIDTVServiceConfiguration *_Nullable configuration, + NSError *_Nullable error) { + + if (!configuration) { + NSLog(@"Error retrieving discovery document: %@", + [error localizedDescription]); + return; + } + + // perform the auth request... +}]; +``` + ### Authorizing – iOS First, you need to have a property in your `UIApplicationDelegate` @@ -428,6 +490,63 @@ _redirectHTTPHandler.currentAuthorizationFlow = }]; ``` + +### Authorizing – tvOS + +Ensure that your main class is a delegate of `OIDAuthStateChangeDelegate`, `OIDAuthStateErrorDelegate`, implement the corresponding methods, and include the following property and instance variable: + +Objective-C +```objc +// property of the containing class +@property(nonatomic, strong, nullable) OIDAuthState *authState; + +// instance variable of the containing class +OIDTVAuthorizationCancelBlock _cancelBlock; +``` + +Then, build and perform the authorization request. + +Objective-C +```objc +// builds authentication request +__weak __typeof(self) weakSelf = self; + +OIDTVAuthorizationRequest *request = + [[OIDTVAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:kClientID + clientSecret:kClientSecret + scopes:@[ OIDScopeOpenID, OIDScopeProfile ] + additionalParameters:nil]; + +// performs authentication request +OIDTVAuthorizationInitialization initBlock = + ^(OIDTVAuthorizationResponse *_Nullable response, NSError *_Nullable error) { + if (response) { + // process authorization response + NSLog(@"Got authorization response: %@", response); + } else { + // handle initialization error + NSLog(@"Error: %@", error); + } + }; + +OIDTVAuthorizationCompletion completionBlock = + ^(OIDAuthState *_Nullable authState, NSError *_Nullable error) { + weakSelf.signInView.hidden = YES; + if (authState) { + NSLog(@"Token response: %@", authState.lastTokenResponse); + [weakSelf setAuthState:authState]; + } else { + NSLog(@"Error: %@", error); + [weakSelf setAuthState:nil]; + } + }; + +_cancelBlock = [OIDTVAuthorizationService authorizeTVRequest:request + initialization:initBlock + completion:completionBlock]; +``` + ### Making API Calls AppAuth gives you the raw token information, if you need it. However, we @@ -470,7 +589,7 @@ self.authState?.performAction() { (accessToken, idToken, error) in } ``` -### Custom User-Agents +### Custom User-Agents (iOS and macOS) Each OAuth flow involves presenting an external user-agent to the user, that allows them to interact with the OAuth authorization server. Typical examples @@ -522,7 +641,7 @@ Include the `EnterpriseUserAgent` subspec alongside any pods/subspecs you were a Make sure to import `AppAuthEnterpriseUserAgent.h` in addition to `AppAuth.h` if you are using the full `AppAuth` functionality. ##### Carthage -Use the `AppAuthEnterpriseUserAgent` Framework, which includes all the headers of the `AppAuth` framework. +Use the `AppAuthEnterpriseUserAgent` framework, which includes all the headers of the `AppAuth` framework. Make sure to import ``. This includes all the files included by AppAuth/AppAuthCore, so only this import is necessary. ##### Swift Package Manager @@ -616,4 +735,4 @@ Browse the [API documentation](http://openid.github.io/AppAuth-iOS/docs/latest/a ## Included Samples -Sample apps that explore core AppAuth features are available for iOS and macOS; follow the instructions in [Examples/README.md](Examples/README.md) to get started. +Sample apps that explore core AppAuth features are available for iOS, macOS and tvOS; follow the instructions in [Examples/README.md](Examples/README.md) to get started. From d17f5867fcd45220591d2fbb2b894b1fac5cbf63 Mon Sep 17 00:00:00 2001 From: Daniel Blakemore Date: Thu, 24 Jun 2021 10:39:50 -0700 Subject: [PATCH 140/204] Remove OIDTVTokenRequestTests from sources for AppAuth-iOS scheme. AppleTV test file was included in a source target which did not have XCTest dependencies and was causing builds to fail. --- AppAuth.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 27449610b..6b0073603 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -158,7 +158,6 @@ 2DA8D82624C6190400FDFB34 /* OIDTVAuthorizationResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DA8D82424C6190300FDFB34 /* OIDTVAuthorizationResponseTests.m */; }; 2DEB065624CA1D9300DF47E7 /* OIDTVTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DEB065424CA1D9300DF47E7 /* OIDTVTokenRequest.h */; }; 2DEB065724CA1D9300DF47E7 /* OIDTVTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB065524CA1D9300DF47E7 /* OIDTVTokenRequest.m */; }; - 2DEB066124CF5CE000DF47E7 /* OIDTVTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */; }; 2DEB066224CF5CFB00DF47E7 /* OIDTVTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */; }; 340DAE571D5821A100EC285B /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; 340DAE581D5821A100EC285B /* OIDExternalUserAgentMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentMac.m */; }; @@ -2353,7 +2352,6 @@ 340DAECB1D582DE100EC285B /* OIDAuthorizationService+IOS.m in Sources */, 341741E31C5D8243000EF209 /* OIDResponseTypes.m in Sources */, 341741E41C5D8243000EF209 /* OIDScopes.m in Sources */, - 2DEB066124CF5CE000DF47E7 /* OIDTVTokenRequestTests.m in Sources */, 341741E71C5D8243000EF209 /* OIDServiceDiscovery.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; From cceee2552dfed90822875544da9c3af110dfa44c Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Tue, 30 Nov 2021 11:35:00 -0800 Subject: [PATCH 141/204] Get tests running again (#666) * Add GitHub Actions CI workflow. * Skip conformance tests until we migrate to new backend. --- .github/workflows/tests.yml | 21 +++++++++++++++++++++ UnitTests/OIDRPProfileCode.m | 3 +++ 2 files changed, 24 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..2279874a4 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,21 @@ +name: tests + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + + spm-build-test: + runs-on: macOS-latest + steps: + - uses: actions/checkout@v2 + - name: Build unit test target + run: swift build + - name: Run unit test target + run: swift test diff --git a/UnitTests/OIDRPProfileCode.m b/UnitTests/OIDRPProfileCode.m index 0bb61fe95..1c37b1f30 100644 --- a/UnitTests/OIDRPProfileCode.m +++ b/UnitTests/OIDRPProfileCode.m @@ -118,10 +118,12 @@ typedef void (^UserInfoCompletion)(OIDAuthState *_Nullable authState, @implementation OIDRPProfileCode - (void)setUp { + XCTSkip("Need to migrate to the new OpenID conformance testing system."); [super setUp]; } - (void)tearDown { + XCTSkip("Need to migrate to the new OpenID conformance testing system."); [super tearDown]; [self endCertificationTest]; @@ -264,6 +266,7 @@ - (void)codeFlowWithExchangeExpectSuccessForTest:(NSString *)test { } - (void)testRP_response_type_code { + XCTSkip("Not working at the moment."); NSString *testName = @"rp-response_type-code"; [self startCertificationTest:testName]; [self codeFlowWithExchangeExpectSuccessForTest:testName]; From fce32e889abe87f0f74f012d6270fb3b9d123b75 Mon Sep 17 00:00:00 2001 From: danblakemore Date: Tue, 30 Nov 2021 11:45:51 -0800 Subject: [PATCH 142/204] Update init unavailable doc on OIDServiceDiscovery (#640) Initially referred to a non-existent serviceDiscoveryWithURL:callback: method. --- Source/AppAuthCore/OIDServiceDiscovery.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/AppAuthCore/OIDServiceDiscovery.h b/Source/AppAuthCore/OIDServiceDiscovery.h index 3998fa12b..1a4929c53 100644 --- a/Source/AppAuthCore/OIDServiceDiscovery.h +++ b/Source/AppAuthCore/OIDServiceDiscovery.h @@ -326,7 +326,7 @@ NS_ASSUME_NONNULL_BEGIN /*! @internal @brief Unavailable. Please use @c initWithDictionary:error:, @c initWithJSON:error, or the - @c serviceDiscoveryWithURL:callback: factory method. + @c discoverServiceConfigurationForDiscoveryURL:callback: from @c OIDAuthorizationService. */ - (nonnull instancetype)init NS_UNAVAILABLE; From 73180f7b09e3468308c358a4299de7e8b946e624 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Tue, 30 Nov 2021 11:47:37 -0800 Subject: [PATCH 143/204] Fix branch name --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2279874a4..cb8a13186 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,10 +3,10 @@ name: tests on: push: branches: - - main + - master pull_request: branches: - - main + - master workflow_dispatch: jobs: From 77f36f2c9410298b742247a7c4023a99c8c67ba9 Mon Sep 17 00:00:00 2001 From: danblakemore Date: Tue, 30 Nov 2021 11:50:30 -0800 Subject: [PATCH 144/204] Remove unmatched diagnostic pop (#641) There seems to be a hanging diagnostic pop that has no push which is causing Travis to fail. It's been in this file throughout the history of the repo with no matching push so I deleted it. --- UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m index 555f6e177..a6d1bb2f5 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m @@ -281,5 +281,3 @@ - (void)testTokenPollRequestWithAdditionalParameters { } @end - -#pragma GCC diagnostic pop From 70ddc9704e52eace5df4955830549b88fc4d5850 Mon Sep 17 00:00:00 2001 From: dmaclach Date: Tue, 30 Nov 2021 12:30:20 -0800 Subject: [PATCH 145/204] Fix up type in designated initializer (#662) Move from OIDServiceDiscovery to NSURL --- Source/AppAuthCore/OIDServiceConfiguration.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/AppAuthCore/OIDServiceConfiguration.m b/Source/AppAuthCore/OIDServiceConfiguration.m index ca48a8c33..a5aa43e7a 100644 --- a/Source/AppAuthCore/OIDServiceConfiguration.m +++ b/Source/AppAuthCore/OIDServiceConfiguration.m @@ -72,7 +72,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint issuer:(nullable NSURL *)issuer registrationEndpoint:(nullable NSURL *)registrationEndpoint - endSessionEndpoint:(nullable OIDServiceDiscovery *)endSessionEndpoint + endSessionEndpoint:(nullable NSURL *)endSessionEndpoint discoveryDocument:(nullable OIDServiceDiscovery *)discoveryDocument { self = [super init]; From fc4339451e9657b0b428f26c6b1d659d39e0903d Mon Sep 17 00:00:00 2001 From: pinlu Date: Wed, 2 Feb 2022 13:21:54 -0800 Subject: [PATCH 146/204] Use TARGET_OS_OSX for macOS rather than TARGET_OS_MAC (#681) --- Source/AppAuth.h | 2 +- Source/AppAuth/macOS/OIDExternalUserAgentMac.m | 2 +- Source/AppAuth/macOS/OIDRedirectHTTPHandler.m | 2 +- Source/Framework/AppAuth.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/AppAuth.h b/Source/AppAuth.h index 0b68463fb..a313a75ad 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -50,7 +50,7 @@ #import "OIDAuthorizationService+IOS.h" #import "OIDExternalUserAgentIOS.h" #import "OIDExternalUserAgentCatalyst.h" -#elif TARGET_OS_MAC +#elif TARGET_OS_OSX #import "OIDAuthState+Mac.h" #import "OIDAuthorizationService+Mac.h" #import "OIDExternalUserAgentMac.h" diff --git a/Source/AppAuth/macOS/OIDExternalUserAgentMac.m b/Source/AppAuth/macOS/OIDExternalUserAgentMac.m index c35a7a327..3409f5f5e 100644 --- a/Source/AppAuth/macOS/OIDExternalUserAgentMac.m +++ b/Source/AppAuth/macOS/OIDExternalUserAgentMac.m @@ -78,4 +78,4 @@ - (void)cleanUp { NS_ASSUME_NONNULL_END -#endif // TARGET_OS_MAC +#endif // TARGET_OS_OSX diff --git a/Source/AppAuth/macOS/OIDRedirectHTTPHandler.m b/Source/AppAuth/macOS/OIDRedirectHTTPHandler.m index 8a3df6cc2..3d0d765d8 100644 --- a/Source/AppAuth/macOS/OIDRedirectHTTPHandler.m +++ b/Source/AppAuth/macOS/OIDRedirectHTTPHandler.m @@ -174,4 +174,4 @@ - (void)dealloc { @end -#endif // TARGET_OS_MAC +#endif // TARGET_OS_OSX diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index d53ed1347..3139df37f 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -58,7 +58,7 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import "AppAuth/OIDExternalUserAgentCatalyst.h" -#elif TARGET_OS_MAC +#elif TARGET_OS_OSX #import #import #import From 9ae7c245694c38b24635c31689fb5063fa181686 Mon Sep 17 00:00:00 2001 From: pinlu Date: Mon, 14 Feb 2022 15:29:42 -0800 Subject: [PATCH 147/204] Add ASWebAuthenticationSession support in OIDExternalUserAgentMac. (#675) * Add ASWebAuthenticationSession support in OIDExternalUserAgentMac. * Incorporate OIDExternalUserAgentMac change in OIDState+Mac and modify the macOS example app. * Incorporate OIDExternalUserAgentMac change in OIDAuthorization+Mac and modify the macOS example app. * Clean APIs and add more API documentations. * Fix indentations in macOS example --- AppAuth.podspec | 3 +- .../Source/AppAuthExampleViewController.m | 22 ++--- Source/AppAuth/macOS/OIDAuthState+Mac.h | 17 ++++ Source/AppAuth/macOS/OIDAuthState+Mac.m | 10 +++ .../macOS/OIDAuthorizationService+Mac.h | 17 +++- .../macOS/OIDAuthorizationService+Mac.m | 9 +++ .../AppAuth/macOS/OIDExternalUserAgentMac.h | 8 ++ .../AppAuth/macOS/OIDExternalUserAgentMac.m | 80 ++++++++++++++++++- 8 files changed, 154 insertions(+), 12 deletions(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index d4191ad79..5fc3cd71a 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -57,8 +57,9 @@ It follows the OAuth 2.0 for Native Apps best current practice externalUserAgent.ios.weak_frameworks = "AuthenticationServices" # macOS - externalUserAgent.osx.source_files = "Source/AppAuth/macOS/**/*.{h,m}" + externalUserAgent.osx.source_files = "Source/AppAuth/macOS/**/*.{h,m}" externalUserAgent.osx.deployment_target = '10.9' + externalUserAgent.osx.weak_frameworks = "AuthenticationServices" end s.subspec 'EnterpriseUserAgent' do |enterpriseUserAgent| diff --git a/Examples/Example-macOS/Source/AppAuthExampleViewController.m b/Examples/Example-macOS/Source/AppAuthExampleViewController.m index 0cc8064b5..38235cd17 100644 --- a/Examples/Example-macOS/Source/AppAuthExampleViewController.m +++ b/Examples/Example-macOS/Source/AppAuthExampleViewController.m @@ -214,8 +214,9 @@ - (IBAction)authWithAutoCodeExchange:(nullable id)sender { // performs authentication request self.appDelegate.currentAuthorizationFlow = [OIDAuthState authStateByPresentingAuthorizationRequest:request - callback:^(OIDAuthState *_Nullable authState, - NSError *_Nullable error) { + presentingWindow:self.view.window + callback:^(OIDAuthState *_Nullable authState, + NSError *_Nullable error) { if (authState) { [self setAuthState:authState]; [self logMessage:@"Got authorization tokens. Access token: %@", @@ -282,8 +283,8 @@ - (IBAction)authWithAutoCodeExchangeHTTP:(nullable id)sender { __weak __typeof(self) weakSelf = self; _redirectHTTPHandler.currentAuthorizationFlow = [OIDAuthState authStateByPresentingAuthorizationRequest:request - callback:^(OIDAuthState *_Nullable authState, - NSError *_Nullable error) { + presentingWindow:self.view.window + callback:^(OIDAuthState *_Nullable authState, NSError *_Nullable error) { // Brings this app to the foreground. [[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | @@ -329,11 +330,12 @@ - (IBAction)authNoCodeExchange:(nullable id)sender { additionalParameters:nil]; // performs authentication request [self logMessage:@"Initiating authorization request %@", request]; + self.appDelegate.currentAuthorizationFlow = [OIDAuthorizationService presentAuthorizationRequest:request - callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, - NSError *_Nullable error) { - + presentingWindow:self.view.window + callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, + NSError *_Nullable error) { if (authorizationResponse) { OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:authorizationResponse]; @@ -412,7 +414,7 @@ - (IBAction)userinfo:(nullable id)sender { [request addValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"]; NSURLSessionConfiguration *configuration = - [NSURLSessionConfiguration defaultSessionConfiguration]; + [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:nil delegateQueue:nil]; @@ -488,7 +490,9 @@ - (void)logMessage:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2) { NSString *dateString = [dateFormatter stringFromDate:[NSDate date]]; NSString *logLine = [NSString stringWithFormat:@"\n%@: %@", dateString, log]; NSAttributedString* logLineAttr = [[NSAttributedString alloc] initWithString:logLine]; - [[_logTextView textStorage] appendAttributedString:logLineAttr]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[_logTextView textStorage] appendAttributedString:logLineAttr]; + }); } @end diff --git a/Source/AppAuth/macOS/OIDAuthState+Mac.h b/Source/AppAuth/macOS/OIDAuthState+Mac.h index f32d27c08..debde1318 100644 --- a/Source/AppAuth/macOS/OIDAuthState+Mac.h +++ b/Source/AppAuth/macOS/OIDAuthState+Mac.h @@ -20,6 +20,7 @@ #if TARGET_OS_OSX +#import #import "OIDAuthState.h" NS_ASSUME_NONNULL_BEGIN @@ -35,15 +36,31 @@ NS_ASSUME_NONNULL_BEGIN and update the OIDAuthState with the results (@c OIDAuthState.updateWithTokenResponse:error:). @param authorizationRequest The authorization request to present. + @param presentingWindow The window to present the authentication flow. @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. + @discussion This method adopts ASWebAuthenticationSession for macOS 10.15 and above or the default browser otherwise. */ + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest + presentingWindow:(NSWindow *)presentingWindow callback:(OIDAuthStateAuthorizationCallback)callback; +/*! @param authorizationRequest The authorization request to present. + @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. + @discussion This method uses the default browser to present the authentication flow. + */ ++ (id) + authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest + callback:(OIDAuthStateAuthorizationCallback)callback + __deprecated_msg("For macOS 10.15 and above please use " + "authStateByPresentingAuthorizationRequest:presentingWindow:callback:"); + @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuth/macOS/OIDAuthState+Mac.m b/Source/AppAuth/macOS/OIDAuthState+Mac.m index 16b061724..2796223c1 100644 --- a/Source/AppAuth/macOS/OIDAuthState+Mac.m +++ b/Source/AppAuth/macOS/OIDAuthState+Mac.m @@ -26,6 +26,16 @@ @implementation OIDAuthState (Mac) ++ (id) + authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest + presentingWindow:(NSWindow *)presentingWindow + callback:(OIDAuthStateAuthorizationCallback)callback { + OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] initWithPresentingWindow:presentingWindow]; + return [self authStateByPresentingAuthorizationRequest:authorizationRequest + externalUserAgent:externalUserAgent + callback:callback]; +} + + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest callback:(OIDAuthStateAuthorizationCallback)callback { diff --git a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h index 37de63f16..ec3c9c890 100644 --- a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h +++ b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h @@ -20,6 +20,7 @@ #if TARGET_OS_OSX +#import #import "OIDAuthorizationService.h" NS_ASSUME_NONNULL_BEGIN @@ -28,6 +29,19 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDAuthorizationService (Mac) +/*! @brief Perform an authorization flow. + @param request The authorization request. + @param presentingWindow The window to present the authentication flow. + @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. + @discussion This method adopts ASWebAuthenticationSession for macOS 10.15 and above or the default browser otherwise. + */ ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request + presentingWindow:(NSWindow *)presentingWindow + callback:(OIDAuthorizationCallback)callback; + /*! @brief Perform an authorization flow using the default browser. @param request The authorization request. @param callback The method called when the request has completed or failed. @@ -36,7 +50,8 @@ NS_ASSUME_NONNULL_BEGIN @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message. */ + (id)presentAuthorizationRequest:(OIDAuthorizationRequest *)request - callback:(OIDAuthorizationCallback)callback; + callback:(OIDAuthorizationCallback)callback + __deprecated_msg("For macOS 10.15 and above please use presentAuthorizationRequest:presentingWindow:callback:"); @end diff --git a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m index c0abec8a4..af93186f9 100644 --- a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m +++ b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m @@ -28,6 +28,15 @@ @implementation OIDAuthorizationService (Mac) ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request + presentingWindow:(NSWindow *)presentingWindow + callback:(OIDAuthorizationCallback)callback { + OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] initWithPresentingWindow:presentingWindow]; + return [self presentAuthorizationRequest:request + externalUserAgent:externalUserAgent + callback:callback]; +} + + (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request callback:(OIDAuthorizationCallback)callback { OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] init]; diff --git a/Source/AppAuth/macOS/OIDExternalUserAgentMac.h b/Source/AppAuth/macOS/OIDExternalUserAgentMac.h index b7122aee0..cb864e1cc 100644 --- a/Source/AppAuth/macOS/OIDExternalUserAgentMac.h +++ b/Source/AppAuth/macOS/OIDExternalUserAgentMac.h @@ -20,6 +20,7 @@ #if TARGET_OS_OSX +#import #import "OIDExternalUserAgent.h" NS_ASSUME_NONNULL_BEGIN @@ -29,6 +30,13 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDExternalUserAgentMac : NSObject +/*! @brief The designated initializer. + @param presentingWindow The window from which to present the ASWebAuthenticationSession. + */ +- (instancetype)initWithPresentingWindow:(NSWindow *)presentingWindow NS_DESIGNATED_INITIALIZER; + +- (instancetype)init __deprecated_msg("Use initWithPresentingWindow for macOS 10.15 and above."); + @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuth/macOS/OIDExternalUserAgentMac.m b/Source/AppAuth/macOS/OIDExternalUserAgentMac.m index 3409f5f5e..301a24af7 100644 --- a/Source/AppAuth/macOS/OIDExternalUserAgentMac.m +++ b/Source/AppAuth/macOS/OIDExternalUserAgentMac.m @@ -27,12 +27,38 @@ #import "OIDErrorUtilities.h" #import "OIDExternalUserAgentSession.h" #import "OIDExternalUserAgentRequest.h" +#import + NS_ASSUME_NONNULL_BEGIN +@interface OIDExternalUserAgentMac () +@end + @implementation OIDExternalUserAgentMac { BOOL _externalUserAgentFlowInProgress; __weak id _session; + + NSWindow *_presentingWindow; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" + ASWebAuthenticationSession *_webAuthenticationSession; +#pragma clang diagnostic pop +} + +- (instancetype)initWithPresentingWindow:(NSWindow *)presentingWindow { + self = [super init]; + if (self) { + _presentingWindow = presentingWindow; + } + return self; +} + +- (instancetype)init { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + return [self initWithPresentingWindow:nil]; +#pragma clang diagnostic pop } - (BOOL)presentExternalUserAgentRequest:(id)request @@ -46,6 +72,38 @@ - (BOOL)presentExternalUserAgentRequest:(id)request _session = session; NSURL *requestURL = [request externalUserAgentRequestURL]; + if (@available(macOS 10.15, *)) { + if (_presentingWindow) { + __weak OIDExternalUserAgentMac *weakSelf = self; + NSString *redirectScheme = request.redirectScheme; + ASWebAuthenticationSession *authenticationSession = + [[ASWebAuthenticationSession alloc] initWithURL:requestURL + callbackURLScheme:redirectScheme + completionHandler:^(NSURL * _Nullable callbackURL, + NSError * _Nullable error) { + __strong OIDExternalUserAgentMac *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + strongSelf->_webAuthenticationSession = nil; + if (callbackURL) { + [strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL]; + } else { + NSError *safariError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow + underlyingError:error + description:nil]; + [strongSelf->_session failExternalUserAgentFlowWithError:safariError]; + } + }]; + + authenticationSession.presentationContextProvider = self; + + _webAuthenticationSession = authenticationSession; + return [authenticationSession start]; + } + } + BOOL openedBrowser = [[NSWorkspace sharedWorkspace] openURL:requestURL]; if (!openedBrowser) { [self cleanUp]; @@ -63,15 +121,35 @@ - (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(voi if (completion) completion(); return; } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" + ASWebAuthenticationSession *webAuthenticationSession = _webAuthenticationSession; +#pragma clang diagnostic pop + // Ideally the browser tab with the URL should be closed here, but the AppAuth library does not // control the browser. [self cleanUp]; - if (completion) completion(); + if (webAuthenticationSession) { + // dismiss the ASWebAuthenticationSession + [webAuthenticationSession cancel]; + if (completion) completion(); + } else if (completion) { + completion(); + } } - (void)cleanUp { _session = nil; _externalUserAgentFlowInProgress = NO; + _webAuthenticationSession = nil; +} + +#pragma mark - ASWebAuthenticationPresentationContextProviding + +- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session + API_AVAILABLE(macosx(10.15)) { + return _presentingWindow; } @end From ddaa0ced4d8a74ed047cbecf4552384e113d28c0 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Fri, 4 Mar 2022 15:19:17 -0800 Subject: [PATCH 148/204] Document the design principles behind AppAuth Originally drafted June 1, 2018. --- DESIGN.md | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 DESIGN.md diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 000000000..ac2dcb87b --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,120 @@ +# AppAuth Project Design Principles + +## About this Doc + +The goal of this doc is to define a scope for AppAuth that we can reference when rejecting or +accepting feature requests, and give clarity to extension creators for what features should be +developed outside of the core project. + +## What is AppAuth + +### OAuth and OpenID Connect standards that support native apps + +The goal of AppAuth is to provide a client library that follows best current practices for native +apps to use the OAuth and OpenID Connect authorization and authentication standards. We aim to +implement standards that are designed for, or work well with native apps and are in common use (and +will implement just those components of these standards that are commonly used). + +These standards are currently supported: +1. [OAuth 2.0](https://tools.ietf.org/html/rfc67490) +2. [Proof Key for Code Exchange by OAuth Public Clients (PKCE)](https://tools.ietf.org/html/rfc7636) +3. [OAuth 2.0 for Native Apps](https://tools.ietf.org/html/rfc8252) +4. [OpenID Connect Core 1.0](http://openid.net/specs/openid-connect-core-1_0.html) +5. [Open ID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html) +6. [OpenID Connect Dynamic Client Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html) + +Support for the following standards is also considered in-scope (see the below section on +prioritization): +1. [OAuth 2.0 Incremental Auth](https://tools.ietf.org/html/draft-ietf-oauth-incremental-auth) +2. [OAuth 2.0 Device Flow for Browserless and Input Constrained Devices](https://tools.ietf.org/html/rfc8628) +3. [OpenID Connect Front-Channel Logout 1.0](http://openid.net/specs/openid-connect-frontchannel-1_0.html) + +### Design Principle + +AppAuth aims to present as close to a 1:1 mapping of the spec as is possible, while following +language-specific idioms (like parameter capitalization). It performs some of the heavy lifting for +you so you don’t need to implement the spec yourself, but it does not hide the complexity of the +underlying specs. + +Providers who want extremely simple user-friendly libraries aimed at developers who know nothing +about authorization and authentication standards are encouraged to wrap AppAuth in their own +purpose-built libraries. + +### Extensibility + +AppAuth aims to be as extensible as possible. Just as you can extend OAuth by adding parameters to a +URL (for example), the same should be possible in AppAuth. + +This helps reduce the surface area of the library, as not everything needs to be hardcoded in as a +first-class citizen. + +## What is out-of-scope for AppAuth + +### Non-best-practice patterns + +AppAuth is a best-practice library, it’s why we built it. We will not support things like embedded +WebViews, that are not considered a best practice. Please don’t ask. + +### Implementing *every* spec parameter or branch + +AppAuth exposes some required and popular parameters directly in its data model. It is a non-goal to +offer this support for every single parameter in the specifications. Less popular parameters, or +ones that simply don’t need special handling can be passed using the “additionalParameters” dictionary. + +Likewise, some specifications document several different sub-protocols. We don’t aim to implement +every single branch of the spec. + +### Non-standard protocols and parameters + +Non-standard protocols and parameters are not supported by AppAuth. However, AppAuth, like the +specs it implements, is largely extensible and you may be able to achieve what you need through +these extension points. For example, simple additional parameters can be passed in the +“additionalParameters” dictionary, and the TokenRequest can support other grant types. + +By way of example, the tvOS device flow support (which may actually be considered in-scope one day) +was initially implemented on top of these extension points without needing to change AppAuth. + +### Provider-specific workarounds, hacks or features + +AppAuth implements the pure authentication and authorization standards. Where these standards are +clear in their meaning, errors in provider implementations are not supported or worked around by +AppAuth. + +Identity standards are well specified, and typically undergo years of review, including security +review before publication. If we accept every provider-specific idiosyncrasy then we are changing +the surface area in ways that are harder to maintain, and are less researched from a security +perspective. There is generally no reason providers can’t offer correct standards-based +implementations, so this is what we expect. + +When all providers follow the standards correctly, interoperability is improved for everyone. The +OpenID Connect foundation has been particularly active in supporting certification efforts to verify +implementations, and the test suits for these are available at no charge. + +If the spec itself has a bug that cannot be worked around, then changes should be proposed through +the IETF and/or OpenID Foundation channels. AppAuth may implement such proposals, even while they +are in the early stages, if they are well received. + +### Provider-specific examples + +As AppAuth is pure standards-based, there is no need for provider-specific samples. Instead, we +encourage providers who have proved their compliance with the relevant standards to provide a +customized readme so their users can configure their own samples. + +We also encourage providers to host their own AppAuth samples, in their own repositories. + +## A word on feature requests + +### Priorities + +Just because something is in-scope, does not mean that the maintainers of AppAuth will add support +for it, or rapidly integrate pull requests. + +The priority of AppAuth is stability and quality, not rapidly integrating feature requests. Pull +requests are thoroughly reviewed for style, the quality of the API, and future maintainability. + +### Review Time + +Expect review periods of 6 months to a year for integrating major new in-scope features. Our +priority as previously stated is stability and quality, not adding features as quickly as possible. +While you work with us to integrate your feature, we highly encourage you to maintain your own fork +so you and others can use the feature immediately – and gain valuable implementation experience. From de08e7847ec9bbc064eda107399d20b423c0e076 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Wed, 16 Mar 2022 10:06:47 -0700 Subject: [PATCH 149/204] Set min iOS version to 9 for SPM (#690) --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 563bdfdde..55bf7dfb8 100644 --- a/Package.swift +++ b/Package.swift @@ -24,7 +24,7 @@ let package = Package( name: "AppAuth", platforms: [ .macOS(.v10_10), - .iOS(.v8), + .iOS(.v9), .tvOS(.v9), .watchOS(.v2) ], From cd55670ca44c707aa0110faee2af123d507cdeca Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Thu, 17 Mar 2022 21:41:03 -0700 Subject: [PATCH 150/204] Update tests badge. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 051223462..7651dbe9d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![AppAuth for iOS and macOS](https://rawgit.com/openid/AppAuth-iOS/master/appauth_lockup.svg) -[![Build Status](https://travis-ci.org/openid/AppAuth-iOS.svg?branch=master)](https://travis-ci.org/openid/AppAuth-iOS) +[![tests](https://github.com/openid/AppAuth-iOS/actions/workflows/tests.yml/badge.svg?event=push)](https://github.com/openid/AppAuth-iOS/actions/workflows/tests.yml) [![codecov](https://codecov.io/gh/openid/AppAuth-iOS/branch/master/graph/badge.svg)](https://codecov.io/gh/openid/AppAuth-iOS) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg?style=flat)](https://github.com/Carthage/Carthage) [![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager) From 63996b0c77e981b3632a3c6408e6ad41cdec8488 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Fri, 18 Mar 2022 00:38:13 -0700 Subject: [PATCH 151/204] Reenable Codecov. --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cb8a13186..c97998169 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,3 +19,5 @@ jobs: run: swift build - name: Run unit test target run: swift test + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v2 From 5198a96e7e5bcb07d02f7dd49a4d802257b83cca Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Fri, 18 Mar 2022 01:05:42 -0700 Subject: [PATCH 152/204] Have swift test generate code coverage. --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c97998169..2658cd290 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,6 +18,6 @@ jobs: - name: Build unit test target run: swift build - name: Run unit test target - run: swift test + run: swift test --enable-code-coverage - name: Upload coverage to Codecov uses: codecov/codecov-action@v2 From 4faf43645b8f8725485750799bfd478c72fa32ec Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Fri, 18 Mar 2022 09:44:13 -0700 Subject: [PATCH 153/204] Run pod lib lint tests (#695) * Add Bundler config for pod on Actions. * Add pod lib lint tests. --- .github/workflows/tests.yml | 18 +++++++ Gemfile | 3 ++ Gemfile.lock | 97 +++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 Gemfile create mode 100644 Gemfile.lock diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2658cd290..66771e146 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,6 +11,24 @@ on: jobs: + pod-lib-lint: + runs-on: macOS-latest + strategy: + matrix: + flags: [ + '', + '--use-libraries', + '--use-static-frameworks' + ] + steps: + - uses: actions/checkout@v2 + - name: Update Bundler + run: bundle update --bundler + - name: Install Ruby gems with Bundler + run: bundle install + - name: Lint podspec using local source + run: pod lib lint --verbose ${{ matrix.flags }} + spm-build-test: runs-on: macOS-latest steps: diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..4e9ba5251 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gem 'cocoapods', '1.11.3' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..2f9cb67a2 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,97 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.5) + rexml + activesupport (6.1.5) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + algoliasearch (1.27.5) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) + atomos (0.1.3) + claide (1.1.0) + cocoapods (1.11.3) + addressable (~> 2.8) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.11.3) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.4.0, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.4.0, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.8.0) + nap (~> 1.0) + ruby-macho (>= 1.0, < 3.0) + xcodeproj (>= 1.21.0, < 2.0) + cocoapods-core (1.11.3) + activesupport (>= 5.0, < 7) + addressable (~> 2.8) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + netrc (~> 0.11) + public_suffix (~> 4.0) + typhoeus (~> 1.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (1.5.1) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.1) + cocoapods-trunk (1.6.0) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.2.0) + colored2 (3.1.2) + concurrent-ruby (1.1.9) + escape (0.0.4) + ethon (0.15.0) + ffi (>= 1.15.0) + ffi (1.15.5) + fourflusher (2.3.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + httpclient (2.8.3) + i18n (1.10.0) + concurrent-ruby (~> 1.0) + json (2.6.1) + minitest (5.15.0) + molinillo (0.8.0) + nanaimo (0.3.0) + nap (1.1.0) + netrc (0.11.0) + public_suffix (4.0.6) + rexml (3.2.5) + ruby-macho (2.5.1) + typhoeus (1.4.0) + ethon (>= 0.9.0) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) + xcodeproj (1.21.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) + zeitwerk (2.5.4) + +PLATFORMS + ruby + +DEPENDENCIES + cocoapods (= 1.11.3) + +BUNDLED WITH + 1.17.2 From ac3e1e06bbbdf089e01449e1398c4bcbe9348283 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Fri, 18 Mar 2022 10:30:27 -0700 Subject: [PATCH 154/204] Run tests against the Xcode project (#696) * Update tests.yml * Use macosx12.1 sdk. * Use latest simulator SDK versions. --- .github/workflows/tests.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 66771e146..166a260f7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,6 +11,26 @@ on: jobs: + xcode-project-test: + runs-on: macOS-latest + strategy: + matrix: + flags: [ + "-scheme AppAuth-iOS -destination 'platform=iOS Simulator,name=iPhone 11,OS=15.2' -sdk 'iphonesimulator15.2'", + "-scheme AppAuth-macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx12.1'", + "-scheme AppAuth_macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx12.1'", + "-scheme AppAuth-tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=15.2' -sdk 'appletvsimulator15.2'", + "-scheme AppAuth_tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=15.2' -sdk 'appletvsimulator15.2'", + "-scheme AppAuthTV -destination 'platform=tvOS Simulator,name=Apple TV,OS=15.2' -sdk 'appletvsimulator15.2'" + ] + steps: + - uses: actions/checkout@v2 + - name: Run unit test targets + run: | + xcodebuild test \ + -project AppAuth.xcodeproj \ + ${{ matrix.flags }} + pod-lib-lint: runs-on: macOS-latest strategy: From 6f48321e04ca3ad6417717246f085dc73d307369 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Fri, 18 Mar 2022 11:07:21 -0700 Subject: [PATCH 155/204] Rollback the EnterpriseUserAgent target. (#693) --- AppAuth.podspec | 7 - AppAuth.xcodeproj/project.pbxproj | 264 +----------------- .../AppAuthEnterpriseUserAgent.xcscheme | 67 ----- Package.swift | 13 - README.md | 21 -- Source/AppAuth.h | 1 + .../OIDExternalUserAgentIOSCustomBrowser.h | 0 .../OIDExternalUserAgentIOSCustomBrowser.m | 0 Source/AppAuthEnterpriseUserAgent.h | 21 -- .../AppAuthEnterpriseUserAgent.h | 59 ---- .../EnterpriseUserAgentFramework/Info.plist | 26 -- Source/Framework/AppAuth.h | 1 + 12 files changed, 16 insertions(+), 464 deletions(-) delete mode 100644 AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthEnterpriseUserAgent.xcscheme rename Source/{AppAuthEnterpriseUserAgent => AppAuth}/iOS/OIDExternalUserAgentIOSCustomBrowser.h (100%) rename Source/{AppAuthEnterpriseUserAgent => AppAuth}/iOS/OIDExternalUserAgentIOSCustomBrowser.m (100%) delete mode 100644 Source/AppAuthEnterpriseUserAgent.h delete mode 100644 Source/EnterpriseUserAgentFramework/AppAuthEnterpriseUserAgent.h delete mode 100644 Source/EnterpriseUserAgentFramework/Info.plist diff --git a/AppAuth.podspec b/AppAuth.podspec index 5fc3cd71a..40470c7b7 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -62,13 +62,6 @@ It follows the OAuth 2.0 for Native Apps best current practice externalUserAgent.osx.weak_frameworks = "AuthenticationServices" end - s.subspec 'EnterpriseUserAgent' do |enterpriseUserAgent| - enterpriseUserAgent.dependency 'AppAuth/Core' - - enterpriseUserAgent.ios.source_files = "Source/AppAuthEnterpriseUserAgent.h", "Source/AppAuthEnterpriseUserAgent/iOS/**/*.{h,m}" - enterpriseUserAgent.ios.deployment_target = "7.0" - end - # Subspec for the full AppAuth library, including platform-dependant external user agents. s.subspec 'TV' do |tv| tv.source_files = "Source/AppAuthTV.h", "Source/AppAuthTV/*.{h,m}" diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 6b0073603..2f4dcaaf5 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -13,7 +13,6 @@ 06C19E9B22B474A200C19CE1 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; 06C19E9C22B474A600C19CE1 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 06C19E9D22B474AD00C19CE1 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; - 2D0BB86C249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D0BB86B249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2D47AAE1249A87020059B5A4 /* OIDTVAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AAD8249A87010059B5A4 /* OIDTVAuthorizationResponse.m */; }; 2D47AAE4249A87020059B5A4 /* OIDTVServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */; }; 2D47AAE8249A87020059B5A4 /* OIDTVAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADD249A87010059B5A4 /* OIDTVAuthorizationRequest.m */; }; @@ -36,66 +35,6 @@ 2D81121424C103F300984DA7 /* OIDRegistrationRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F821DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m */; }; 2D81121524C103F300984DA7 /* OIDRegistrationResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */; }; 2D81121624C103F300984DA7 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; - 2D91B81F249053190005B197 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; - 2D91B820249053190005B197 /* OIDExternalUserAgentCatalyst.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A7082D2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m */; }; - 2D91B821249053190005B197 /* OIDFieldMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C41C5D8243000EF209 /* OIDFieldMapping.m */; }; - 2D91B822249053190005B197 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; - 2D91B823249053190005B197 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; - 2D91B824249053190005B197 /* OIDAuthState.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741BB1C5D8243000EF209 /* OIDAuthState.m */; }; - 2D91B825249053190005B197 /* OIDAuthState+IOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */; }; - 2D91B826249053190005B197 /* OIDTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D41C5D8243000EF209 /* OIDTokenResponse.m */; }; - 2D91B827249053190005B197 /* OIDErrorUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C21C5D8243000EF209 /* OIDErrorUtilities.m */; }; - 2D91B828249053190005B197 /* OIDURLQueryComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D81C5D8243000EF209 /* OIDURLQueryComponent.m */; }; - 2D91B829249053190005B197 /* OIDAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */; }; - 2D91B82A249053190005B197 /* OIDAuthorizationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B91C5D8243000EF209 /* OIDAuthorizationService.m */; }; - 2D91B82B249053190005B197 /* OIDClientMetadataParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F791DE4276800DA0DC3 /* OIDClientMetadataParameters.m */; }; - 2D91B82C249053190005B197 /* OIDTokenUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D61C5D8243000EF209 /* OIDTokenUtilities.m */; }; - 2D91B82D249053190005B197 /* OIDServiceDiscovery.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D01C5D8243000EF209 /* OIDServiceDiscovery.m */; }; - 2D91B82E249053190005B197 /* OIDTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741D21C5D8243000EF209 /* OIDTokenRequest.m */; }; - 2D91B82F249053190005B197 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */; }; - 2D91B830249053190005B197 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; - 2D91B831249053190005B197 /* OIDAuthorizationService+IOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */; }; - 2D91B832249053190005B197 /* OIDServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CE1C5D8243000EF209 /* OIDServiceConfiguration.m */; }; - 2D91B833249053190005B197 /* OIDRegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */; }; - 2D91B834249053190005B197 /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; }; - 2D91B835249053190005B197 /* OIDScopes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CA1C5D8243000EF209 /* OIDScopes.m */; }; - 2D91B836249053190005B197 /* OIDScopeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741CC1C5D8243000EF209 /* OIDScopeUtilities.m */; }; - 2D91B837249053190005B197 /* OIDGrantTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C61C5D8243000EF209 /* OIDGrantTypes.m */; }; - 2D91B838249053190005B197 /* OIDRegistrationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7B1DE42E1000DA0DC3 /* OIDRegistrationRequest.m */; }; - 2D91B839249053190005B197 /* OIDResponseTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C81C5D8243000EF209 /* OIDResponseTypes.m */; }; - 2D91B83A249053190005B197 /* OIDAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741B71C5D8243000EF209 /* OIDAuthorizationResponse.m */; }; - 2D91B83B249053190005B197 /* OIDError.m in Sources */ = {isa = PBXBuildFile; fileRef = 341741C01C5D8243000EF209 /* OIDError.m */; }; - 2D91B83E249053190005B197 /* OIDAuthorizationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B83F249053190005B197 /* OIDScopes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C91C5D8243000EF209 /* OIDScopes.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B840249053190005B197 /* OIDExternalUserAgentRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B841249053190005B197 /* OIDAuthStateChangeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BC1C5D8243000EF209 /* OIDAuthStateChangeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B842249053190005B197 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B844249053190005B197 /* OIDExternalUserAgentCatalyst.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A7082C2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B845249053190005B197 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B846249053190005B197 /* OIDResponseTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C71C5D8243000EF209 /* OIDResponseTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B847249053190005B197 /* OIDTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D11C5D8243000EF209 /* OIDTokenRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B848249053190005B197 /* OIDScopeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CB1C5D8243000EF209 /* OIDScopeUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B849249053190005B197 /* OIDTokenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D31C5D8243000EF209 /* OIDTokenResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B84A249053190005B197 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B84B249053190005B197 /* OIDServiceDiscovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CF1C5D8243000EF209 /* OIDServiceDiscovery.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B84C249053190005B197 /* OIDGrantTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C51C5D8243000EF209 /* OIDGrantTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B84D249053190005B197 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B84E249053190005B197 /* OIDAuthState+IOS.h in Headers */ = {isa = PBXBuildFile; fileRef = F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B84F249053190005B197 /* OIDRegistrationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 60140F7E1DE4335200DA0DC3 /* OIDRegistrationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B850249053190005B197 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B851249053190005B197 /* OIDExternalUserAgentSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B852249053190005B197 /* OIDServiceConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741CD1C5D8243000EF209 /* OIDServiceConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B853249053190005B197 /* OIDAuthStateErrorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BD1C5D8243000EF209 /* OIDAuthStateErrorDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B854249053190005B197 /* OIDAuthorizationService.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B81C5D8243000EF209 /* OIDAuthorizationService.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B855249053190005B197 /* OIDRegistrationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 60140F7D1DE42E3000DA0DC3 /* OIDRegistrationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B856249053190005B197 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B857249053190005B197 /* OIDAuthState.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BA1C5D8243000EF209 /* OIDAuthState.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B858249053190005B197 /* OIDErrorUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741C11C5D8243000EF209 /* OIDErrorUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B859249053190005B197 /* OIDAuthorizationService+IOS.h in Headers */ = {isa = PBXBuildFile; fileRef = F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B85A249053190005B197 /* OIDAuthorizationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B85B249053190005B197 /* OIDTokenUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741D51C5D8243000EF209 /* OIDTokenUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B85C249053190005B197 /* OIDError.h in Headers */ = {isa = PBXBuildFile; fileRef = 341741BF1C5D8243000EF209 /* OIDError.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2D91B85D249053190005B197 /* OIDExternalUserAgentIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2D9385DE24B3861E009A12D7 /* AppAuthTV.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D9385B924B37CAD009A12D7 /* AppAuthTV.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2D9385DF24B38646009A12D7 /* OIDTVAuthorizationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D47AAD9249A87010059B5A4 /* OIDTVAuthorizationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2D9385E024B38658009A12D7 /* OIDTVAuthorizationResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D47AADC249A87010059B5A4 /* OIDTVAuthorizationResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -653,6 +592,9 @@ A6DEABB52018ECF30022AC32 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEABB62018ECF30022AC32 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEABB72018ECF40022AC32 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C14E3B6827E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = C14E3B6627E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C14E3B6927E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = C14E3B6727E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.m */; }; + C14E3B6A27E3BFB800CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = C14E3B6727E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.m */; }; CF37C06E1F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; CF37C06F1F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; CF37C0701F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; @@ -755,8 +697,6 @@ 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDURLSessionProvider.h; sourceTree = ""; }; 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDURLSessionProvider.m; sourceTree = ""; }; 0396974C1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDURLSessionProviderTests.m; sourceTree = ""; }; - 2D0BB86A249D5B75005BA653 /* AppAuthEnterpriseUserAgent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppAuthEnterpriseUserAgent.h; sourceTree = ""; }; - 2D0BB86B249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAuthEnterpriseUserAgent.h; sourceTree = ""; }; 2D47AAD8249A87010059B5A4 /* OIDTVAuthorizationResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVAuthorizationResponse.m; sourceTree = ""; }; 2D47AAD9249A87010059B5A4 /* OIDTVAuthorizationRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVAuthorizationRequest.h; sourceTree = ""; }; 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVServiceConfiguration.m; sourceTree = ""; }; @@ -769,8 +709,6 @@ 2D8111F524C0FD4C00984DA7 /* AppAuthTVTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuthTVTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 2D81120024C1036700984DA7 /* OIDTVAuthorizationRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVAuthorizationRequestTests.m; sourceTree = ""; }; 2D81120324C1036700984DA7 /* OIDTVAuthorizationRequestTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVAuthorizationRequestTests.h; sourceTree = ""; }; - 2D91B862249053190005B197 /* AppAuthEnterpriseUserAgent.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuthEnterpriseUserAgent.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 2D91B868249177180005B197 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2D9385B724B37CAD009A12D7 /* AppAuthTV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuthTV.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2D9385B924B37CAD009A12D7 /* AppAuthTV.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppAuthTV.h; sourceTree = ""; }; 2D9385BA24B37CAD009A12D7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -899,6 +837,8 @@ A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentRequest.h; sourceTree = ""; }; A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDExternalUserAgentIOS.m; sourceTree = ""; }; A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentIOS.h; sourceTree = ""; }; + C14E3B6627E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentIOSCustomBrowser.h; sourceTree = ""; }; + C14E3B6727E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDExternalUserAgentIOSCustomBrowser.m; sourceTree = ""; }; CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionRequest.h; sourceTree = ""; }; CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionRequest.m; sourceTree = ""; }; CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionResponse.h; sourceTree = ""; }; @@ -920,13 +860,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2D91B83C249053190005B197 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 2D9385B424B37CAD009A12D7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1106,15 +1039,6 @@ path = AppAuthTV; sourceTree = ""; }; - 2D91B867249177180005B197 /* EnterpriseUserAgentFramework */ = { - isa = PBXGroup; - children = ( - 2D0BB86B249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h */, - 2D91B868249177180005B197 /* Info.plist */, - ); - path = EnterpriseUserAgentFramework; - sourceTree = ""; - }; 2D9385B824B37CAD009A12D7 /* TVFramework */ = { isa = PBXGroup; children = ( @@ -1172,7 +1096,6 @@ 342F42C32177B1FC00574F24 /* AppAuthCore.framework */, 348970972177B3B000ABEED4 /* AppAuthCoreTests.xctest */, 2D9385B724B37CAD009A12D7 /* AppAuthTV.framework */, - 2D91B862249053190005B197 /* AppAuthEnterpriseUserAgent.framework */, 2D8111F524C0FD4C00984DA7 /* AppAuthTVTests.xctest */, ); name = Products; @@ -1182,7 +1105,6 @@ isa = PBXGroup; children = ( 2D47AAD2249988770059B5A4 /* AppAuthEnterpriseUserAgent */, - 2D91B867249177180005B197 /* EnterpriseUserAgentFramework */, 348970992178F40600ABEED4 /* CoreFramework */, 343AAA4C1E8345B600F9D36E /* Framework */, 2D9385B824B37CAD009A12D7 /* TVFramework */, @@ -1192,7 +1114,6 @@ 341741AF1C5D8243000EF209 /* AppAuth.h */, 3489709E21791B0C00ABEED4 /* AppAuthCore.h */, 2D47AADB249A87010059B5A4 /* AppAuthTV.h */, - 2D0BB86A249D5B75005BA653 /* AppAuthEnterpriseUserAgent.h */, ); path = Source; sourceTree = ""; @@ -1348,6 +1269,8 @@ F6F60FAF1D2BFEF000325CB3 /* iOS */ = { isa = PBXGroup; children = ( + C14E3B6627E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.h */, + C14E3B6727E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.m */, F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */, F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */, F6F60FB51D2BFEFE00325CB3 /* OIDAuthState+IOS.h */, @@ -1363,45 +1286,6 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 2D91B83D249053190005B197 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 2D0BB86C249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h in Headers */, - 2D91B842249053190005B197 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */, - 2D91B83E249053190005B197 /* OIDAuthorizationResponse.h in Headers */, - 2D91B83F249053190005B197 /* OIDScopes.h in Headers */, - 2D91B840249053190005B197 /* OIDExternalUserAgentRequest.h in Headers */, - 2D91B841249053190005B197 /* OIDAuthStateChangeDelegate.h in Headers */, - 2D91B845249053190005B197 /* OIDIDToken.h in Headers */, - 2D91B846249053190005B197 /* OIDResponseTypes.h in Headers */, - 2D91B847249053190005B197 /* OIDTokenRequest.h in Headers */, - 2D91B848249053190005B197 /* OIDScopeUtilities.h in Headers */, - 2D91B849249053190005B197 /* OIDTokenResponse.h in Headers */, - 2D91B84A249053190005B197 /* OIDEndSessionResponse.h in Headers */, - 2D91B84B249053190005B197 /* OIDServiceDiscovery.h in Headers */, - 2D91B84C249053190005B197 /* OIDGrantTypes.h in Headers */, - 2D91B84D249053190005B197 /* OIDURLSessionProvider.h in Headers */, - 2D91B84F249053190005B197 /* OIDRegistrationResponse.h in Headers */, - 2D91B850249053190005B197 /* OIDExternalUserAgent.h in Headers */, - 2D91B851249053190005B197 /* OIDExternalUserAgentSession.h in Headers */, - 2D91B852249053190005B197 /* OIDServiceConfiguration.h in Headers */, - 2D91B853249053190005B197 /* OIDAuthStateErrorDelegate.h in Headers */, - 2D91B854249053190005B197 /* OIDAuthorizationService.h in Headers */, - 2D91B855249053190005B197 /* OIDRegistrationRequest.h in Headers */, - 2D91B856249053190005B197 /* OIDEndSessionRequest.h in Headers */, - 2D91B857249053190005B197 /* OIDAuthState.h in Headers */, - 2D91B858249053190005B197 /* OIDErrorUtilities.h in Headers */, - 2D91B85A249053190005B197 /* OIDAuthorizationRequest.h in Headers */, - 2D91B85B249053190005B197 /* OIDTokenUtilities.h in Headers */, - 2D91B85C249053190005B197 /* OIDError.h in Headers */, - 2D91B859249053190005B197 /* OIDAuthorizationService+IOS.h in Headers */, - 2D91B84E249053190005B197 /* OIDAuthState+IOS.h in Headers */, - 2D91B85D249053190005B197 /* OIDExternalUserAgentIOS.h in Headers */, - 2D91B844249053190005B197 /* OIDExternalUserAgentCatalyst.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 2D9385B224B37CAD009A12D7 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1518,6 +1402,7 @@ 343AAAE31E83499000F9D36E /* OIDAuthorizationRequest.h in Headers */, 343AAAF91E83499000F9D36E /* OIDTokenUtilities.h in Headers */, 343AAAEC1E83499000F9D36E /* OIDError.h in Headers */, + C14E3B6827E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */, A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentIOS.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1650,24 +1535,6 @@ productReference = 2D8111F524C0FD4C00984DA7 /* AppAuthTVTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - 2D91B81D249053190005B197 /* AppAuthEnterpriseUserAgent */ = { - isa = PBXNativeTarget; - buildConfigurationList = 2D91B85F249053190005B197 /* Build configuration list for PBXNativeTarget "AppAuthEnterpriseUserAgent" */; - buildPhases = ( - 2D91B81E249053190005B197 /* Sources */, - 2D91B83C249053190005B197 /* Frameworks */, - 2D91B83D249053190005B197 /* Headers */, - 2D91B85E249053190005B197 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = AppAuthEnterpriseUserAgent; - productName = AppAuth_iOS; - productReference = 2D91B862249053190005B197 /* AppAuthEnterpriseUserAgent.framework */; - productType = "com.apple.product-type.framework"; - }; 2D9385B624B37CAD009A12D7 /* AppAuthTV */ = { isa = PBXNativeTarget; buildConfigurationList = 2D9385C824B37CAD009A12D7 /* Build configuration list for PBXNativeTarget "AppAuthTV" */; @@ -2073,7 +1940,6 @@ 342F42842177B1FC00574F24 /* AppAuthCore */, 3489707D2177B3B000ABEED4 /* AppAuthCoreTests */, 2D9385B624B37CAD009A12D7 /* AppAuthTV */, - 2D91B81D249053190005B197 /* AppAuthEnterpriseUserAgent */, 2D8111F424C0FD4C00984DA7 /* AppAuthTVTests */, ); }; @@ -2087,13 +1953,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2D91B85E249053190005B197 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 2D9385B524B37CAD009A12D7 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -2214,42 +2073,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2D91B81E249053190005B197 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 2D91B82F249053190005B197 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */, - 2D91B821249053190005B197 /* OIDFieldMapping.m in Sources */, - 2D91B822249053190005B197 /* OIDIDToken.m in Sources */, - 2D91B823249053190005B197 /* OIDEndSessionResponse.m in Sources */, - 2D91B824249053190005B197 /* OIDAuthState.m in Sources */, - 2D91B826249053190005B197 /* OIDTokenResponse.m in Sources */, - 2D91B827249053190005B197 /* OIDErrorUtilities.m in Sources */, - 2D91B828249053190005B197 /* OIDURLQueryComponent.m in Sources */, - 2D91B829249053190005B197 /* OIDAuthorizationRequest.m in Sources */, - 2D91B82A249053190005B197 /* OIDAuthorizationService.m in Sources */, - 2D91B82B249053190005B197 /* OIDClientMetadataParameters.m in Sources */, - 2D91B82C249053190005B197 /* OIDTokenUtilities.m in Sources */, - 2D91B82D249053190005B197 /* OIDServiceDiscovery.m in Sources */, - 2D91B82E249053190005B197 /* OIDTokenRequest.m in Sources */, - 2D91B830249053190005B197 /* OIDEndSessionRequest.m in Sources */, - 2D91B832249053190005B197 /* OIDServiceConfiguration.m in Sources */, - 2D91B833249053190005B197 /* OIDRegistrationResponse.m in Sources */, - 2D91B834249053190005B197 /* OIDURLSessionProvider.m in Sources */, - 2D91B835249053190005B197 /* OIDScopes.m in Sources */, - 2D91B836249053190005B197 /* OIDScopeUtilities.m in Sources */, - 2D91B837249053190005B197 /* OIDGrantTypes.m in Sources */, - 2D91B838249053190005B197 /* OIDRegistrationRequest.m in Sources */, - 2D91B839249053190005B197 /* OIDResponseTypes.m in Sources */, - 2D91B83A249053190005B197 /* OIDAuthorizationResponse.m in Sources */, - 2D91B83B249053190005B197 /* OIDError.m in Sources */, - 2D91B825249053190005B197 /* OIDAuthState+IOS.m in Sources */, - 2D91B831249053190005B197 /* OIDAuthorizationService+IOS.m in Sources */, - 2D91B81F249053190005B197 /* OIDExternalUserAgentIOS.m in Sources */, - 2D91B820249053190005B197 /* OIDExternalUserAgentCatalyst.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 2D9385B324B37CAD009A12D7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2326,6 +2149,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C14E3B6A27E3BFB800CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */, A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentIOS.m in Sources */, 341741E01C5D8243000EF209 /* OIDErrorUtilities.m in Sources */, 34A6632D1E871DD40060B664 /* OIDIDToken.m in Sources */, @@ -2502,6 +2326,7 @@ F9A7082F2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m in Sources */, 343AAA881E83478900F9D36E /* OIDFieldMapping.m in Sources */, 34A663311E871DD40060B664 /* OIDIDToken.m in Sources */, + C14E3B6927E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */, A6DEAB862017A7060022AC32 /* OIDEndSessionResponse.m in Sources */, 343AAA841E83478900F9D36E /* OIDAuthState.m in Sources */, 343AAA701E83467D00F9D36E /* OIDAuthState+IOS.m in Sources */, @@ -2829,58 +2654,6 @@ }; name = Release; }; - 2D91B860249053190005B197 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CODE_SIGN_IDENTITY = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/EnterpriseUserAgentFramework/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthEnterpriseUserAgent; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SUPPORTS_MACCATALYST = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 2D91B861249053190005B197 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CODE_SIGN_IDENTITY = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/EnterpriseUserAgentFramework/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthEnterpriseUserAgent; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SUPPORTS_MACCATALYST = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; 2D9385C924B37CAD009A12D7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3024,7 +2797,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -3080,7 +2853,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; @@ -3112,7 +2885,7 @@ CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/"; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauth.AppAuthTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3129,7 +2902,7 @@ CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/"; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauth.AppAuthTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3639,15 +3412,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 2D91B85F249053190005B197 /* Build configuration list for PBXNativeTarget "AppAuthEnterpriseUserAgent" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 2D91B860249053190005B197 /* Debug */, - 2D91B861249053190005B197 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 2D9385C824B37CAD009A12D7 /* Build configuration list for PBXNativeTarget "AppAuthTV" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthEnterpriseUserAgent.xcscheme b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthEnterpriseUserAgent.xcscheme deleted file mode 100644 index 3c18d8b46..000000000 --- a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuthEnterpriseUserAgent.xcscheme +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Package.swift b/Package.swift index 55bf7dfb8..b031680df 100644 --- a/Package.swift +++ b/Package.swift @@ -35,9 +35,6 @@ let package = Package( .library( name: "AppAuth", targets: ["AppAuth"]), - .library( - name: "AppAuthEnterpriseUserAgent", - targets: ["AppAuthEnterpriseUserAgent"]), .library( name: "AppAuthTV", targets: ["AppAuthTV"]) @@ -61,16 +58,6 @@ let package = Package( .headerSearchPath("macOS/LoopbackHTTPServer"), ] ), - .target( - name: "AppAuthEnterpriseUserAgent", - dependencies: ["AppAuthCore"], - path: "Source/AppAuthEnterpriseUserAgent", - sources: ["iOS"], - publicHeadersPath: "", - cSettings: [ - .headerSearchPath("iOS"), - ] - ), .target( name: "AppAuthTV", dependencies: ["AppAuthCore"], diff --git a/README.md b/README.md index 7651dbe9d..78f79959f 100644 --- a/README.md +++ b/README.md @@ -631,24 +631,6 @@ try, or use as a reference for your own implementation. One of them, enables you to use a different browser for authentication, like Chrome for iOS or Firefox for iOS. -##### CocoaPods -Include the `EnterpriseUserAgent` subspec alongside any pods/subspecs you were already using, e.g.: -``` - pod 'AppAuth' - pod 'AppAuth/EnterpriseUserAgent' -``` - -Make sure to import `AppAuthEnterpriseUserAgent.h` in addition to `AppAuth.h` if you are using the full `AppAuth` functionality. - -##### Carthage -Use the `AppAuthEnterpriseUserAgent` framework, which includes all the headers of the `AppAuth` framework. -Make sure to import ``. This includes all the files included by AppAuth/AppAuthCore, so only this import is necessary. - -##### Swift Package Manager -Include the `AppAuthEnterpriseUserAgent` target alongside any targets you were already using. - -Make sure to import `AppAuthEnterpriseUserAgent.h` in addition to `AppAuth.h` if you are using the full `AppAuth` functionality. - Here's how to configure AppAuth to use a custom browser using the `OIDExternalUserAgentIOSCustomBrowser` user agent: @@ -669,9 +651,6 @@ This is required so that AppAuth can test for the browser and open the app store if it's not installed (the default behavior of this user-agent). You only need to include the URL scheme of the actual browser you intend to use. -Next, make sure to import the correct header file. -If using CocoaPods/Swift Package manager, make sure to import AppAuthEnterpriseUserAgent alongside AppAuth/AppAuthCore. - Objective-C ```objc // performs authentication request diff --git a/Source/AppAuth.h b/Source/AppAuth.h index a313a75ad..19abc55e1 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -49,6 +49,7 @@ #import "OIDAuthState+IOS.h" #import "OIDAuthorizationService+IOS.h" #import "OIDExternalUserAgentIOS.h" +#import "OIDExternalUserAgentIOSCustomBrowser.h" #import "OIDExternalUserAgentCatalyst.h" #elif TARGET_OS_OSX #import "OIDAuthState+Mac.h" diff --git a/Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.h b/Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h similarity index 100% rename from Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.h rename to Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h diff --git a/Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.m b/Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m similarity index 100% rename from Source/AppAuthEnterpriseUserAgent/iOS/OIDExternalUserAgentIOSCustomBrowser.m rename to Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m diff --git a/Source/AppAuthEnterpriseUserAgent.h b/Source/AppAuthEnterpriseUserAgent.h deleted file mode 100644 index 3bc505418..000000000 --- a/Source/AppAuthEnterpriseUserAgent.h +++ /dev/null @@ -1,21 +0,0 @@ -/*! @file AppAuthEnterpriseUserAgent.h - @brief AppAuth iOS SDK - @copyright - Copyright 2020 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 "AppAuthCore.h" - -#import "OIDExternalUserAgentIOSCustomBrowser.h" diff --git a/Source/EnterpriseUserAgentFramework/AppAuthEnterpriseUserAgent.h b/Source/EnterpriseUserAgentFramework/AppAuthEnterpriseUserAgent.h deleted file mode 100644 index 70c152deb..000000000 --- a/Source/EnterpriseUserAgentFramework/AppAuthEnterpriseUserAgent.h +++ /dev/null @@ -1,59 +0,0 @@ -/*! @file AppAuthEnterpriseUserAgentEnterpriseUserAgent.h - @brief AppAuthEnterpriseUserAgent iOS SDK - @copyright - Copyright 2020 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 - -//! Project version number for AppAuthEnterpriseUserAgentEnterpriseUserAgentFramework. -FOUNDATION_EXPORT double AppAuthEnterpriseUserAgentEnterpriseUserAgentVersionNumber; - -//! Project version string for AppAuthEnterpriseUserAgentEnterpriseUserAgentFramework. -FOUNDATION_EXPORT const unsigned char AppAuthEnterpriseUserAgentEnterpriseUserAgentVersionString[]; - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#import -#import -#import -#import "AppAuthEnterpriseUserAgent/OIDExternalUserAgentCatalyst.h" - -#import diff --git a/Source/EnterpriseUserAgentFramework/Info.plist b/Source/EnterpriseUserAgentFramework/Info.plist deleted file mode 100644 index fdd42e8ac..000000000 --- a/Source/EnterpriseUserAgentFramework/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSHumanReadableCopyright - Copyright © 2020 The AppAuth Authors - NSPrincipalClass - - - diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index 3139df37f..f1916de77 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -57,6 +57,7 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import +#import #import "AppAuth/OIDExternalUserAgentCatalyst.h" #elif TARGET_OS_OSX #import From 33660c271c961f8ce1084cc13f2ea8195e864f7d Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Fri, 18 Mar 2022 11:15:14 -0700 Subject: [PATCH 156/204] Prepare for 1.5.0 release (#692) * Bump podspec version for 1.5.0 release. * Update changelog for 1.5.0 release. --- AppAuth.podspec | 2 +- CHANGELOG.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 40470c7b7..c4b573b05 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.4.0" + s.version = "1.5.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ebb29dee..fc68c324b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # AppAuth for iOS and macOS Changelog +## 1.5.0 +- Improved tvOS support. ([#111](https://github.com/openid/AppAuth-iOS/issues/111)) +- ASWebAuthencationSession on macOS. ([#675](https://github.com/openid/AppAuth-iOS/pull/675)) + ## 1.4.0 ### Added From 25f44d131cedb5d5808211dbebe8648320f513ba Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Fri, 18 Mar 2022 22:11:02 -0700 Subject: [PATCH 157/204] Fix changelog typo. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc68c324b..0ac138f0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 1.5.0 - Improved tvOS support. ([#111](https://github.com/openid/AppAuth-iOS/issues/111)) -- ASWebAuthencationSession on macOS. ([#675](https://github.com/openid/AppAuth-iOS/pull/675)) +- ASWebAuthenticationSession on macOS. ([#675](https://github.com/openid/AppAuth-iOS/pull/675)) ## 1.4.0 From d49491a5ec8613ead20cf3ab5ec80a1b87996c1d Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Thu, 31 Mar 2022 08:57:49 -0700 Subject: [PATCH 158/204] Remove empty AppAuthEnterpriseUserAgent folder from Xcode project. (#697) --- AppAuth.xcodeproj/project.pbxproj | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 2f4dcaaf5..1a945644d 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -803,8 +803,6 @@ 343AAAAE1E83489A00F9D36E /* AppAuth_tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_tvOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAAC21E8348A900F9D36E /* AppAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_macOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDExternalUserAgentIOSCustomBrowser.m; sourceTree = ""; }; - 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentIOSCustomBrowser.h; sourceTree = ""; }; 347423F61E7F4B5600D3E6D6 /* libAppAuth-watchOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-watchOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 348970972177B3B000ABEED4 /* AppAuthCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuthCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3489709A2178F40600ABEED4 /* AppAuthCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAuthCore.h; sourceTree = ""; }; @@ -992,23 +990,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 2D47AAD2249988770059B5A4 /* AppAuthEnterpriseUserAgent */ = { - isa = PBXGroup; - children = ( - 2D47AAD3249988B90059B5A4 /* iOS */, - ); - path = AppAuthEnterpriseUserAgent; - sourceTree = ""; - }; - 2D47AAD3249988B90059B5A4 /* iOS */ = { - isa = PBXGroup; - children = ( - 345AE746202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.h */, - 345AE745202D526800738D22 /* OIDExternalUserAgentIOSCustomBrowser.m */, - ); - path = iOS; - sourceTree = ""; - }; 2D47AAD7249A86E30059B5A4 /* AppAuthTV */ = { isa = PBXGroup; children = ( @@ -1104,7 +1085,6 @@ 341741AE1C5D8243000EF209 /* Source */ = { isa = PBXGroup; children = ( - 2D47AAD2249988770059B5A4 /* AppAuthEnterpriseUserAgent */, 348970992178F40600ABEED4 /* CoreFramework */, 343AAA4C1E8345B600F9D36E /* Framework */, 2D9385B824B37CAD009A12D7 /* TVFramework */, From 0612db562911ee813733ed9255181f1cd5ed78fa Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Fri, 1 Apr 2022 00:57:58 -0700 Subject: [PATCH 159/204] Update issue templates (#699) --- .github/ISSUE_TEMPLATE/bug_report.md | 6 +++--- .github/ISSUE_TEMPLATE/feature_request.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a598b7fbf..dd84ea782 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,12 +1,12 @@ --- name: Bug report about: Create a report to help us improve +title: '' +labels: '' +assignees: '' --- -**Are you filing an issue about iOS 12?** -You can find preliminary support for iOS 12 in the (ios12beta branch)[https://github.com/openid/AppAuth-iOS/tree/ios12beta] and background information in (the associated pull request)[https://github.com/openid/AppAuth-iOS/pull/246]. Please review these materials before filing iOS 12 issues. Thanks. - **Describe the bug** A clear and concise description of what the bug is. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 5ad6b46bf..bbcbbe7d6 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,12 +1,12 @@ --- name: Feature request about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' --- -**Are you filing an issue about iOS 12?** -You can find preliminary support for iOS 12 in the (ios12beta branch)[https://github.com/openid/AppAuth-iOS/tree/ios12beta] and background information in (the associated pull request)[https://github.com/openid/AppAuth-iOS/pull/246]. Please review these materials before filing iOS 12 issues. Thanks. - **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] From 98ed5e15ceedcacc0b3fd6c3521a22411acc2212 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Apr 2022 08:45:49 -0700 Subject: [PATCH 160/204] Bump cocoapods-downloader from 1.5.1 to 1.6.3 (#700) Bumps [cocoapods-downloader](https://github.com/CocoaPods/cocoapods-downloader) from 1.5.1 to 1.6.3. - [Release notes](https://github.com/CocoaPods/cocoapods-downloader/releases) - [Changelog](https://github.com/CocoaPods/cocoapods-downloader/blob/master/CHANGELOG.md) - [Commits](https://github.com/CocoaPods/cocoapods-downloader/compare/1.5.1...1.6.3) --- updated-dependencies: - dependency-name: cocoapods-downloader dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2f9cb67a2..9ed5f1d66 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -45,7 +45,7 @@ GEM public_suffix (~> 4.0) typhoeus (~> 1.0) cocoapods-deintegrate (1.0.5) - cocoapods-downloader (1.5.1) + cocoapods-downloader (1.6.3) cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.1) From 8582608849acbd2a434307a19da3a4f8f7267564 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Wed, 1 Jun 2022 15:55:27 -0700 Subject: [PATCH 161/204] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea782..107a71184 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,6 @@ --- name: Bug report -about: Create a report to help us improve +about: Create a bug report to help us improve title: '' labels: '' assignees: '' @@ -23,16 +23,10 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] +**Environment** +- Device: [ e.g. iPhone 13, MacBook Pro, etc ] +- OS: [ e.g. iOS 15, macOS 11, etc ] +- Browser: [ e.g. Safari, Chrome, etc ] **Additional context** Add any other context about the problem here. From a4473150393702c4009c6ea73df69bba85811eae Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Wed, 17 Aug 2022 15:43:22 -0700 Subject: [PATCH 162/204] Update ObjC style guide link. --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 51537d3a0..d4fe7b6fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,8 +48,7 @@ discussing your proposal, or email the ## Coding Standards The AppAuth library follows the -[Google Coding Style](https://google.github.io/styleguide/objcguide.xml) for -the Objective-C language. Please review your own code for adherence to the +[Google Objective-C Style Guide](https://github.com/google/styleguide/blob/gh-pages/objcguide.md). Please review your own code for adherence to the standard. ## Pull Request Reviews From c1a5080e5fe60579ba8f4c753535e18a248eb5b0 Mon Sep 17 00:00:00 2001 From: danblakemore Date: Thu, 18 Aug 2022 19:09:04 -0700 Subject: [PATCH 163/204] Add prefersEphemeralSession parameter for external user-agents (#645) * Add emphemeral browser option iOS/Catalyst user agents. Adds BOOL properties to iOS/Catalyst-related methods which allow setting the ASWebAuthenticationSession property prefersEphemeralWebBrowserSession. * Add emphemeral browser option to macOS user agent. Adds BOOL arguments to macOS-related methods which allow setting the ASWebAuthenticationSession property prefersEphemeralWebBrowserSession. * Update doc comments based on review feedback. Also fixes some formatting and code symbol annotation to support Xcode Quick Help. * Replace 'Catalyst' with 'external' in method brief --- Source/AppAuth/iOS/OIDAuthState+IOS.h | 27 ++++++++++++++++--- Source/AppAuth/iOS/OIDAuthState+IOS.m | 20 ++++++++++++++ .../AppAuth/iOS/OIDAuthorizationService+IOS.h | 23 +++++++++++++--- .../AppAuth/iOS/OIDAuthorizationService+IOS.m | 16 +++++++++++ .../iOS/OIDExternalUserAgentCatalyst.h | 9 +++++++ .../iOS/OIDExternalUserAgentCatalyst.m | 12 +++++++++ Source/AppAuth/iOS/OIDExternalUserAgentIOS.h | 20 ++++++++++++-- Source/AppAuth/iOS/OIDExternalUserAgentIOS.m | 14 +++++++++- Source/AppAuth/macOS/OIDAuthState+Mac.h | 26 +++++++++++++++++- Source/AppAuth/macOS/OIDAuthState+Mac.m | 12 +++++++++ .../macOS/OIDAuthorizationService+Mac.h | 20 +++++++++++++- .../macOS/OIDAuthorizationService+Mac.m | 12 +++++++++ .../AppAuth/macOS/OIDExternalUserAgentMac.h | 12 ++++++++- .../AppAuth/macOS/OIDExternalUserAgentMac.m | 11 ++++++++ 14 files changed, 222 insertions(+), 12 deletions(-) diff --git a/Source/AppAuth/iOS/OIDAuthState+IOS.h b/Source/AppAuth/iOS/OIDAuthState+IOS.h index 99ca45734..1a1ee63a0 100644 --- a/Source/AppAuth/iOS/OIDAuthState+IOS.h +++ b/Source/AppAuth/iOS/OIDAuthState+IOS.h @@ -37,9 +37,7 @@ NS_ASSUME_NONNULL_BEGIN and update the OIDAuthState with the results (@c OIDAuthState.updateWithTokenResponse:error:). @param authorizationRequest The authorization request to present. - @param presentingViewController The view controller from which to present the - @c SFSafariViewController. On iOS 13, the window of this UIViewController - is used as the ASPresentationAnchor. + @param presentingViewController The view controller to use for presenting the authentication UI. @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 @@ -50,6 +48,29 @@ NS_ASSUME_NONNULL_BEGIN presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthStateAuthorizationCallback)callback; +/*! @brief Convenience method to create a @c OIDAuthState by presenting an authorization request + (optionally using an emphemeral browser session that shares no cookies or data with the + normal browser session) 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 presentingViewController The view controller to use for presenting the authentication UI. + @param prefersEphemeralSession Whether the caller prefers to use a private authentication + session. See @c ASWebAuthenticationSession.prefersEphemeralWebBrowserSession for more. + @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) + authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest + presentingViewController:(UIViewController *)presentingViewController + prefersEphemeralSession:(BOOL)prefersEphemeralSession + callback:(OIDAuthStateAuthorizationCallback)callback + API_AVAILABLE(ios(13)); + + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest callback:(OIDAuthStateAuthorizationCallback)callback API_AVAILABLE(ios(11)) API_UNAVAILABLE(macCatalyst) diff --git a/Source/AppAuth/iOS/OIDAuthState+IOS.m b/Source/AppAuth/iOS/OIDAuthState+IOS.m index 9f3a4e8c8..c474a77d1 100644 --- a/Source/AppAuth/iOS/OIDAuthState+IOS.m +++ b/Source/AppAuth/iOS/OIDAuthState+IOS.m @@ -42,6 +42,26 @@ @implementation OIDAuthState (IOS) callback:callback]; } ++ (id) + authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest + presentingViewController:(UIViewController *)presentingViewController + prefersEphemeralSession:(BOOL)prefersEphemeralSession + callback:(OIDAuthStateAuthorizationCallback)callback { + id externalUserAgent; +#if TARGET_OS_MACCATALYST + externalUserAgent = [[OIDExternalUserAgentCatalyst alloc] + initWithPresentingViewController:presentingViewController + prefersEphemeralSession:prefersEphemeralSession]; +#else // TARGET_OS_MACCATALYST + externalUserAgent = [[OIDExternalUserAgentIOS alloc] + initWithPresentingViewController:presentingViewController + prefersEphemeralSession:prefersEphemeralSession]; +#endif // TARGET_OS_MACCATALYST + return [self authStateByPresentingAuthorizationRequest:authorizationRequest + externalUserAgent:externalUserAgent + callback:callback]; +} + #if !TARGET_OS_MACCATALYST + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest diff --git a/Source/AppAuth/iOS/OIDAuthorizationService+IOS.h b/Source/AppAuth/iOS/OIDAuthorizationService+IOS.h index c6e14f194..c7c685d28 100644 --- a/Source/AppAuth/iOS/OIDAuthorizationService+IOS.h +++ b/Source/AppAuth/iOS/OIDAuthorizationService+IOS.h @@ -31,10 +31,10 @@ NS_ASSUME_NONNULL_BEGIN */ @interface OIDAuthorizationService (IOS) -/*! @brief Perform an authorization flow using \SFSafariViewController. +/*! @brief Perform an authorization flow, presenting an appropriate browser for the user to + authenticate. @param request The authorization request. - @param presentingViewController The view controller from which to present the - \SFSafariViewController. + @param presentingViewController The view controller from which to present authentication UI. @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 @@ -43,6 +43,23 @@ NS_ASSUME_NONNULL_BEGIN + (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthorizationCallback)callback; + +/*! @brief Perform an authorization flow using the @c ASWebAuthenticationSession optionally using an + emphemeral browser session that shares no cookies or data with the normal browser session. + @param request The authorization request. + @param presentingViewController The view controller from which to present authentication UI. + @param prefersEphemeralSession Whether the caller prefers to use a private authentication + session. See @c ASWebAuthenticationSession.prefersEphemeralWebBrowserSession for more. + @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) presentAuthorizationRequest:(OIDAuthorizationRequest *)request + presentingViewController:(UIViewController *)presentingViewController + prefersEphemeralSession:(BOOL)prefersEphemeralSession + callback:(OIDAuthorizationCallback)callback API_AVAILABLE(ios(13)); + @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuth/iOS/OIDAuthorizationService+IOS.m b/Source/AppAuth/iOS/OIDAuthorizationService+IOS.m index 05ccff5db..4ca07c55e 100644 --- a/Source/AppAuth/iOS/OIDAuthorizationService+IOS.m +++ b/Source/AppAuth/iOS/OIDAuthorizationService+IOS.m @@ -41,6 +41,22 @@ @implementation OIDAuthorizationService (IOS) return [self presentAuthorizationRequest:request externalUserAgent:externalUserAgent callback:callback]; } ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request + presentingViewController:(UIViewController *)presentingViewController + prefersEphemeralSession:(BOOL)prefersEphemeralSession + callback:(OIDAuthorizationCallback)callback { + id externalUserAgent; +#if TARGET_OS_MACCATALYST + externalUserAgent = [[OIDExternalUserAgentCatalyst alloc] + initWithPresentingViewController:presentingViewController + prefersEphemeralSession:prefersEphemeralSession]; +#else // TARGET_OS_MACCATALYST + externalUserAgent = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController:presentingViewController + prefersEphemeralSession:prefersEphemeralSession]; +#endif // TARGET_OS_MACCATALYST + return [self presentAuthorizationRequest:request externalUserAgent:externalUserAgent callback:callback]; +} + @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h b/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h index d98d4413c..910d0bb86 100644 --- a/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h +++ b/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h @@ -45,6 +45,15 @@ API_AVAILABLE(macCatalyst(13)) API_UNAVAILABLE(ios) (UIViewController *)presentingViewController NS_DESIGNATED_INITIALIZER; +/*! @brief Create an external user-agent which optionally uses a private authentication session. + @param presentingViewController The view controller from which to present the browser. + @param prefersEphemeralSession Whether the caller prefers to use a private authentication + session. See @c ASWebAuthenticationSession.prefersEphemeralWebBrowserSession for more. + */ +- (nullable instancetype)initWithPresentingViewController: + (UIViewController *)presentingViewController + prefersEphemeralSession:(BOOL)prefersEphemeralSession; + @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m b/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m index fc9cef5c0..d6771b3e9 100644 --- a/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m +++ b/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m @@ -38,6 +38,7 @@ @interface OIDExternalUserAgentCatalyst () _session; @@ -53,6 +54,16 @@ - (nullable instancetype)initWithPresentingViewController: return self; } +- (nullable instancetype)initWithPresentingViewController: + (UIViewController *)presentingViewController + prefersEphemeralSession:(BOOL)prefersEphemeralSession { + self = [self initWithPresentingViewController:presentingViewController]; + if (self) { + _prefersEphemeralSession = prefersEphemeralSession; + } + return self; +} + - (BOOL)presentExternalUserAgentRequest:(id)request session:(id)session { if (_externalUserAgentFlowInProgress) { @@ -89,6 +100,7 @@ - (BOOL)presentExternalUserAgentRequest:(id)request }]; authenticationVC.presentationContextProvider = self; + authenticationVC.prefersEphemeralWebBrowserSession = _prefersEphemeralSession; _webAuthenticationVC = authenticationVC; openedUserAgent = [authenticationVC start]; diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h index 7261c050d..ae0773c69 100644 --- a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h +++ b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h @@ -39,13 +39,29 @@ API_UNAVAILABLE(macCatalyst) "initWithPresentingViewController:presentingViewController"); /*! @brief The designated initializer. - @param presentingViewController The view controller from which to present the - \SFSafariViewController. + @param presentingViewController The view controller from which to present the authentication UI. + @discussion The specific authentication UI used depends on the iOS version and accessibility + options. iOS 8 uses the system browser, iOS 9-10 use @c SFSafariViewController, iOS 11 uses + @c SFAuthenticationSession + (unless Guided Access is on which does not work) or uses @c SFSafariViewController, and iOS + 12+ uses @c ASWebAuthenticationSession (unless Guided Access is on). */ - (nullable instancetype)initWithPresentingViewController: (UIViewController *)presentingViewController NS_DESIGNATED_INITIALIZER; +/*! @brief Create an external user-agent which optionally uses a private authentication session. + @param presentingViewController The view controller from which to present the browser. + @param prefersEphemeralSession Whether the caller prefers to use a private authentication + session. See @c ASWebAuthenticationSession.prefersEphemeralWebBrowserSession for more. + @discussion Authentication is performed with @c ASWebAuthenticationSession (unless Guided Access + is on), setting the ephemerality based on the argument. + */ +- (nullable instancetype)initWithPresentingViewController: + (UIViewController *)presentingViewController + prefersEphemeralSession:(BOOL)prefersEphemeralSession + API_AVAILABLE(ios(13)); + @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m index 728f0868d..eab7aa3cb 100644 --- a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m +++ b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m @@ -43,6 +43,7 @@ @interface OIDExternalUserAgentIOS () @implementation OIDExternalUserAgentIOS { UIViewController *_presentingViewController; + BOOL _prefersEphemeralSession; BOOL _externalUserAgentFlowInProgress; __weak id _session; @@ -75,6 +76,16 @@ - (nullable instancetype)initWithPresentingViewController: return self; } +- (nullable instancetype)initWithPresentingViewController: + (UIViewController *)presentingViewController + prefersEphemeralSession:(BOOL)prefersEphemeralSession { + self = [self initWithPresentingViewController:presentingViewController]; + if (self) { + _prefersEphemeralSession = prefersEphemeralSession; + } + return self; +} + - (BOOL)presentExternalUserAgentRequest:(id)request session:(id)session { if (_externalUserAgentFlowInProgress) { @@ -115,7 +126,8 @@ - (BOOL)presentExternalUserAgentRequest:(id)request }]; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 if (@available(iOS 13.0, *)) { - authenticationVC.presentationContextProvider = self; + authenticationVC.presentationContextProvider = self; + authenticationVC.prefersEphemeralWebBrowserSession = _prefersEphemeralSession; } #endif _webAuthenticationVC = authenticationVC; diff --git a/Source/AppAuth/macOS/OIDAuthState+Mac.h b/Source/AppAuth/macOS/OIDAuthState+Mac.h index debde1318..71e56f22a 100644 --- a/Source/AppAuth/macOS/OIDAuthState+Mac.h +++ b/Source/AppAuth/macOS/OIDAuthState+Mac.h @@ -41,13 +41,37 @@ NS_ASSUME_NONNULL_BEGIN @return A @c OIDExternalUserAgentSession instance which will terminate when it receives a @c OIDExternalUserAgentSession.cancel message, or after processing a @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message. - @discussion This method adopts ASWebAuthenticationSession for macOS 10.15 and above or the default browser otherwise. + @discussion This method adopts @c ASWebAuthenticationSession for macOS 10.15 and above or the + default browser otherwise. */ + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest presentingWindow:(NSWindow *)presentingWindow callback:(OIDAuthStateAuthorizationCallback)callback; +/*! @brief Convenience method to create a @c OIDAuthState by presenting an authorization request + (optionally using an emphemeral browser session that shares no cookies or data with the + normal browser session) 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 using + @c OIDAuthState.updateWithTokenResponse:error:. + @param authorizationRequest The authorization request to present. + @param presentingWindow The window to present the @c ASWebAuthenticationSession UI. + @param prefersEphemeralSession Whether the caller prefers to use a private authentication + session. See @c ASWebAuthenticationSession.prefersEphemeralWebBrowserSession for more. + @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) + authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest + presentingWindow:(NSWindow *)presentingWindow + prefersEphemeralSession:(BOOL)prefersEphemeralSession + callback:(OIDAuthStateAuthorizationCallback)callback + API_AVAILABLE(macos(10.15)); + /*! @param authorizationRequest The authorization request to present. @param callback The method called when the request has completed or failed. @return A @c OIDExternalUserAgentSession instance which will terminate when it diff --git a/Source/AppAuth/macOS/OIDAuthState+Mac.m b/Source/AppAuth/macOS/OIDAuthState+Mac.m index 2796223c1..f2894daaf 100644 --- a/Source/AppAuth/macOS/OIDAuthState+Mac.m +++ b/Source/AppAuth/macOS/OIDAuthState+Mac.m @@ -35,6 +35,18 @@ @implementation OIDAuthState (Mac) externalUserAgent:externalUserAgent callback:callback]; } ++ (id) + authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest + presentingWindow:(NSWindow *)presentingWindow + prefersEphemeralSession:(BOOL)prefersEphemeralSession + callback:(OIDAuthStateAuthorizationCallback)callback { + OIDExternalUserAgentMac *externalUserAgent = + [[OIDExternalUserAgentMac alloc] initWithPresentingWindow:presentingWindow + prefersEphemeralSession:prefersEphemeralSession]; + return [self authStateByPresentingAuthorizationRequest:authorizationRequest + externalUserAgent:externalUserAgent + callback:callback]; +} + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest diff --git a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h index ec3c9c890..0a34faf28 100644 --- a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h +++ b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h @@ -36,12 +36,30 @@ NS_ASSUME_NONNULL_BEGIN @return A @c OIDExternalUserAgentSession instance which will terminate when it receives a @c OIDExternalUserAgentSession.cancel message, or after processing a @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message. - @discussion This method adopts ASWebAuthenticationSession for macOS 10.15 and above or the default browser otherwise. + @discussion This method adopts @c ASWebAuthenticationSession for macOS 10.15 and above or the + default browser otherwise. */ + (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request presentingWindow:(NSWindow *)presentingWindow callback:(OIDAuthorizationCallback)callback; +/*! @brief Perform an authorization flow using the @c ASWebAuthenticationSession optionally using an + emphemeral browser session that shares no cookies or data with the normal browser session. + @param request The authorization request. + @param presentingWindow The window to present the authentication flow. + @param prefersEphemeralSession Whether the caller prefers to use a private authentication + session. See @c ASWebAuthenticationSession.prefersEphemeralWebBrowserSession for more. + @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) presentAuthorizationRequest:(OIDAuthorizationRequest *)request + presentingWindow:(NSWindow *)presentingWindow + prefersEphemeralSession:(BOOL)prefersEphemeralSession + callback:(OIDAuthorizationCallback)callback + API_AVAILABLE(macos(10.15)); + /*! @brief Perform an authorization flow using the default browser. @param request The authorization request. @param callback The method called when the request has completed or failed. diff --git a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m index af93186f9..4d3d9a038 100644 --- a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m +++ b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m @@ -37,6 +37,18 @@ @implementation OIDAuthorizationService (Mac) callback:callback]; } ++ (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request + presentingWindow:(NSWindow *)presentingWindow + prefersEphemeralSession:(BOOL)prefersEphemeralSession + callback:(OIDAuthorizationCallback)callback { + OIDExternalUserAgentMac *externalUserAgent = + [[OIDExternalUserAgentMac alloc] initWithPresentingWindow:presentingWindow + prefersEphemeralSession:prefersEphemeralSession]; + return [self presentAuthorizationRequest:request + externalUserAgent:externalUserAgent + callback:callback]; +} + + (id) presentAuthorizationRequest:(OIDAuthorizationRequest *)request callback:(OIDAuthorizationCallback)callback { OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] init]; diff --git a/Source/AppAuth/macOS/OIDExternalUserAgentMac.h b/Source/AppAuth/macOS/OIDExternalUserAgentMac.h index cb864e1cc..360c44ad6 100644 --- a/Source/AppAuth/macOS/OIDExternalUserAgentMac.h +++ b/Source/AppAuth/macOS/OIDExternalUserAgentMac.h @@ -31,10 +31,20 @@ NS_ASSUME_NONNULL_BEGIN @interface OIDExternalUserAgentMac : NSObject /*! @brief The designated initializer. - @param presentingWindow The window from which to present the ASWebAuthenticationSession. + @param presentingWindow The window from which to present the @c ASWebAuthenticationSession on + macOS 10.15 and above. Older macOS versions use the system browser. */ - (instancetype)initWithPresentingWindow:(NSWindow *)presentingWindow NS_DESIGNATED_INITIALIZER; +/*! @brief Create an external user-agent which optionally uses a private authentication session. + @param presentingWindow The window from which to present the @c ASWebAuthenticationSession. + @param prefersEphemeralSession Whether the caller prefers to use a private authentication + session. See @c ASWebAuthenticationSession.prefersEphemeralWebBrowserSession for more. + */ +- (nullable instancetype)initWithPresentingWindow:(NSWindow *)presentingWindow + prefersEphemeralSession:(BOOL)prefersEphemeralSession + API_AVAILABLE(macos(10.15)); + - (instancetype)init __deprecated_msg("Use initWithPresentingWindow for macOS 10.15 and above."); @end diff --git a/Source/AppAuth/macOS/OIDExternalUserAgentMac.m b/Source/AppAuth/macOS/OIDExternalUserAgentMac.m index 301a24af7..d1e08f902 100644 --- a/Source/AppAuth/macOS/OIDExternalUserAgentMac.m +++ b/Source/AppAuth/macOS/OIDExternalUserAgentMac.m @@ -38,6 +38,7 @@ @interface OIDExternalUserAgentMac () _session; + BOOL _prefersEphemeralSession; NSWindow *_presentingWindow; #pragma clang diagnostic push @@ -54,6 +55,15 @@ - (instancetype)initWithPresentingWindow:(NSWindow *)presentingWindow { return self; } +- (nullable instancetype)initWithPresentingWindow:(NSWindow *)presentingWindow + prefersEphemeralSession:(BOOL)prefersEphemeralSession { + self = [self initWithPresentingWindow:presentingWindow]; + if (self) { + _prefersEphemeralSession = prefersEphemeralSession; + } + return self; +} + - (instancetype)init { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" @@ -100,6 +110,7 @@ - (BOOL)presentExternalUserAgentRequest:(id)request authenticationSession.presentationContextProvider = self; _webAuthenticationSession = authenticationSession; + _webAuthenticationSession.prefersEphemeralWebBrowserSession = _prefersEphemeralSession; return [authenticationSession start]; } } From d35a616cdb4139314395c7dc00e210b1989d9a5f Mon Sep 17 00:00:00 2001 From: Yinli ZHU Date: Fri, 9 Sep 2022 21:03:21 +0200 Subject: [PATCH 164/204] Update OIDServiceConfiguration.m (#656) Fix for Unarchive with unarchivedObjectOfClass:fromData:error: --- Source/AppAuthCore/OIDServiceConfiguration.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/AppAuthCore/OIDServiceConfiguration.m b/Source/AppAuthCore/OIDServiceConfiguration.m index a5aa43e7a..2cffbf73a 100644 --- a/Source/AppAuthCore/OIDServiceConfiguration.m +++ b/Source/AppAuthCore/OIDServiceConfiguration.m @@ -185,7 +185,7 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { return nil; } - OIDServiceDiscovery *discoveryDocument = [aDecoder decodeObjectOfClass:[OIDServiceDiscovery class] + OIDServiceDiscovery *discoveryDocument = [aDecoder decodeObjectOfClasses:[NSSet setWithObjects:[OIDServiceDiscovery class], [NSArray class], nil] forKey:kDiscoveryDocumentKey]; return [self initWithAuthorizationEndpoint:authorizationEndpoint From c6879807e503b3154c07d03eb0e14372e0b7e8e6 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Fri, 9 Sep 2022 15:41:11 -0700 Subject: [PATCH 165/204] Correct OIDServiceDiscovery's NSCoding implementation (#721) * Improve NSCoding implementation. * Test new and old encodings. * Ensure that we're handling the old encoding without errors. * No need to supply an empty failure string. * Provide forward compatibility. --- Source/AppAuthCore/OIDServiceConfiguration.m | 13 +- Source/AppAuthCore/OIDServiceDiscovery.m | 28 +++- UnitTests/OIDServiceDiscoveryTests.m | 131 ++++++++++++++++++- 3 files changed, 165 insertions(+), 7 deletions(-) diff --git a/Source/AppAuthCore/OIDServiceConfiguration.m b/Source/AppAuthCore/OIDServiceConfiguration.m index 2cffbf73a..0e5c1197a 100644 --- a/Source/AppAuthCore/OIDServiceConfiguration.m +++ b/Source/AppAuthCore/OIDServiceConfiguration.m @@ -185,8 +185,17 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { return nil; } - OIDServiceDiscovery *discoveryDocument = [aDecoder decodeObjectOfClasses:[NSSet setWithObjects:[OIDServiceDiscovery class], [NSArray class], nil] - forKey:kDiscoveryDocumentKey]; + NSSet *allowedClasses = [NSSet setWithArray:@[[OIDServiceDiscovery class], + // The following classes are required in + // order to support secure decoding of the + // old OIDServiceDiscovery encoding. + [NSDictionary class], + [NSArray class], + [NSString class], + [NSNumber class], + [NSNull class]]]; + OIDServiceDiscovery *discoveryDocument = [aDecoder decodeObjectOfClasses:allowedClasses + forKey:kDiscoveryDocumentKey]; return [self initWithAuthorizationEndpoint:authorizationEndpoint tokenEndpoint:tokenEndpoint diff --git a/Source/AppAuthCore/OIDServiceDiscovery.m b/Source/AppAuthCore/OIDServiceDiscovery.m index 739275251..4d96f9db3 100644 --- a/Source/AppAuthCore/OIDServiceDiscovery.m +++ b/Source/AppAuthCore/OIDServiceDiscovery.m @@ -23,6 +23,10 @@ NS_ASSUME_NONNULL_BEGIN +/*! @brief The key for the @c discoveryDictionary property. + */ +static NSString *const kDiscoveryDictionaryKey = @"discoveryDictionary"; + /*! Field keys associated with an OpenID Connect Discovery Document. */ static NSString *const kIssuerKey = @"issuer"; static NSString *const kAuthorizationEndpointKey = @"authorization_endpoint"; @@ -192,7 +196,27 @@ + (BOOL)supportsSecureCoding { - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { NSError *error; - NSDictionary *dictionary = [[NSDictionary alloc] initWithCoder:aDecoder]; + NSDictionary *dictionary; + if ([aDecoder containsValueForKey:kDiscoveryDictionaryKey]) { + // We're decoding a collection type (NSDictionary) from NSJSONSerialization's + // +JSONObjectWithData, so we need to include all classes that could potentially be contained + // within. + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], + [NSArray class], + [NSString class], + [NSNumber class], + [NSNull class]]]; + dictionary = [aDecoder decodeObjectOfClasses:allowedClasses + forKey:kDiscoveryDictionaryKey]; + } else { + // Decode using the old encoding which delegated to NSDictionary's encodeWithCoder: + // implementation: + // + // - (void)encodeWithCoder:(NSCoder *)aCoder { + // [_discoveryDictionary encodeWithCoder:aCoder]; + // } + dictionary = [[NSDictionary alloc] initWithCoder:aDecoder]; + } self = [self initWithDictionary:dictionary error:&error]; if (error) { return nil; @@ -201,6 +225,8 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { } - (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_discoveryDictionary forKey:kDiscoveryDictionaryKey]; + // Provide forward compatibilty by continuing to add the old encoding. [_discoveryDictionary encodeWithCoder:aCoder]; } diff --git a/UnitTests/OIDServiceDiscoveryTests.m b/UnitTests/OIDServiceDiscoveryTests.m index 17d6cd19e..6329feab3 100644 --- a/UnitTests/OIDServiceDiscoveryTests.m +++ b/UnitTests/OIDServiceDiscoveryTests.m @@ -30,6 +30,42 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wgnu" +// Define a subclass of @c OIDServiceDiscovery that provides the old NSCoding decoding +// implementation. +@interface OIDServiceDiscoveryOldDecoding : OIDServiceDiscovery +@end + +@implementation OIDServiceDiscoveryOldDecoding + ++ (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; +} + +@end + +// Define a subclass of @c OIDServiceDiscovery that provides the old NSCoding encoding +// implementation. +@interface OIDServiceDiscoveryOldEncoding : OIDServiceDiscovery +@end + +@implementation OIDServiceDiscoveryOldEncoding + +- (void)encodeWithCoder:(NSCoder *)coder { + [self.discoveryDictionary encodeWithCoder:coder]; +} + +@end + /*! Testing URL used when testing URL conversions. */ static NSString *const kTestURL = @"http://www.google.com/"; @@ -110,7 +146,7 @@ + (NSDictionary *)completeServiceDiscoveryDictionary { kJWKSURLKey : @"http://www.example.com/jwks", kRegistrationEndpointKey : @"Registration Endpoint", kEndSessionEndpointKey : @"https://www.example.com/logout", - kScopesSupportedKey : @"Scopes Supported", + kScopesSupportedKey : @[ @"scope1", @"scope2" ], kResponseTypesSupportedKey : @"Response Types Supported", kResponseModesSupportedKey : @"Response Modes Supported", kGrantTypesSupportedKey : @"Grant Types Supported", @@ -411,10 +447,97 @@ - (void)testSecureCoding { OIDServiceDiscovery *discovery = [[OIDServiceDiscovery alloc] initWithDictionary:serviceDiscoveryDictionary error:&error]; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:discovery]; - OIDServiceDiscovery *unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + NSData *data; + if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:discovery + requiringSecureCoding:YES + error:&error]; + } else { + data = [NSKeyedArchiver archivedDataWithRootObject:discovery]; + } + + OIDServiceDiscovery *unarchived; + if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + unarchived = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDServiceDiscovery class] + fromData:data + error:&error]; + } else { + unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + } + + XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary); +} - XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary, @""); +/*! @brief To ensure backward compatibility, test our ability to decode the old encoding of + @c OIDServiceDiscovery. + */ +- (void)testSecureCodingDecodeOld { + NSError *error; + NSDictionary *serviceDiscoveryDictionary = [[self class] completeServiceDiscoveryDictionary]; + OIDServiceDiscoveryOldEncoding *discovery = + [[OIDServiceDiscoveryOldEncoding alloc] initWithDictionary:serviceDiscoveryDictionary + error:&error]; + NSData *data; + if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:discovery + requiringSecureCoding:YES + error:&error]; + } else { + data = [NSKeyedArchiver archivedDataWithRootObject:discovery]; + } + + OIDServiceDiscovery *unarchived; + if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + NSSet *allowedClasses = [NSSet setWithArray:@[[OIDServiceDiscovery class], + [NSDictionary class], + [NSArray class], + [NSString class], + [NSNumber class], + [NSNull class]]]; + unarchived = [NSKeyedUnarchiver unarchivedObjectOfClasses:allowedClasses + fromData:data + error:&error]; + } else { + unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + } + + XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary); +} + +/*! @brief To ensure forward compatibility, test the ability of the old implementation to decode + the new encoding of @c OIDServiceDiscovery. + */ +- (void)testSecureCodingOldDecodeNew { + NSError *error; + NSDictionary *serviceDiscoveryDictionary = [[self class] completeServiceDiscoveryDictionary]; + OIDServiceDiscoveryOldDecoding *discovery = + [[OIDServiceDiscoveryOldDecoding alloc] initWithDictionary:serviceDiscoveryDictionary + error:&error]; + NSData *data; + if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:discovery + requiringSecureCoding:YES + error:&error]; + } else { + data = [NSKeyedArchiver archivedDataWithRootObject:discovery]; + } + + OIDServiceDiscovery *unarchived; + if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + NSSet *allowedClasses = [NSSet setWithArray:@[[OIDServiceDiscoveryOldDecoding class], + [NSDictionary class], + [NSArray class], + [NSString class], + [NSNumber class], + [NSNull class]]]; + unarchived = [NSKeyedUnarchiver unarchivedObjectOfClasses:allowedClasses + fromData:data + error:&error]; + } else { + unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + } + XCTAssertNil(error); + XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary); } /*! @brief Tests the NSCopying implementation by round-tripping an instance through the copying From 3d36a58a2b736f7bc499453e996a704929b25080 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Mon, 12 Sep 2022 11:03:22 -0700 Subject: [PATCH 166/204] Prep for 1.6.0 release (#722) * Reformat changelog. * Update changelog for 1.6.0 release. * Bump the podspec version for the 1.6.0 release. --- AppAuth.podspec | 2 +- CHANGELOG.md | 70 +++++++++++++++++++++++++------------------------ 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index c4b573b05..4aeae48b5 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.5.0" + s.version = "1.6.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ac138f0e..5c086f028 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,51 +1,53 @@ -# AppAuth for iOS and macOS Changelog +# 1.6.0 +- Added a `prefersEphemeralSession` parameter for external user-agents. ([#645](https://github.com/openid/AppAuth-iOS/pull/645)) +- Fixed errors encountered when using secure coding to decode `OIDAuthState`. ([#656](https://github.com/openid/AppAuth-iOS/pull/656), [#721](https://github.com/openid/AppAuth-iOS/pull/721)) -## 1.5.0 +# 1.5.0 - Improved tvOS support. ([#111](https://github.com/openid/AppAuth-iOS/issues/111)) - ASWebAuthenticationSession on macOS. ([#675](https://github.com/openid/AppAuth-iOS/pull/675)) -## 1.4.0 +# 1.4.0 -### Added +## Added 1. Support for Swift Package Manager -## 1.3.1 +# 1.3.1 -### Fixes +## Fixes 1. Removed `UIWebView` reference in comment -## 1.3.0 +# 1.3.0 -### Notable Changes +## Notable Changes 1. Support for Mac Catalyst -## 1.2.0 +# 1.2.0 -### Notable Changes +## Notable Changes 1. Support for iOS 13 -## 1.1.0 +# 1.1.0 -### Notable Changes +## Notable Changes 1. [OpenID Connect RP-Initiated Logout](http://openid.net/specs/openid-connect-session-1_0.html#RPLogout) implemented 2. Added logic for the `azp` claim -### Fixes +## Fixes 1. Scheme comparison for redirects is now case insensitive 2. Improved error handling during discovery when a non-JSON document is encountered. -## 1.0.0 +# 1.0.0 1.0.0! 🎉 -### Notable Changes +## Notable Changes 1. **All deprecated APIs removed.** Please ensure your code builds on version 0.95.0 with no deprecation warnings before upgrading! @@ -62,20 +64,20 @@ 4. `AppAuth/Core` subspec, and AppAuthCore Framework added to support iOS extensions. -## 1.0.0.beta2 (2018-09-27) +# 1.0.0.beta2 (2018-09-27) -### Notable Changes +## Notable Changes 1. `AppAuth/Core` subspec, and AppAuthCore Framework added to support iOS extensions. -## 1.0.0.beta1 (2018-09-27) +# 1.0.0.beta1 (2018-09-27) First 1.0.0 beta! HEAD is now tracking changes for the 1.0.0 release. The `pre-1.0` branch was cut prior to the breaking changes for 1.0.0, bug fixes for critical issues may be backported for a time. -### Notable Changes +## Notable Changes 1. **All deprecated APIs removed.** Please ensure your code builds on version 0.95.0 with no deprecation warnings before upgrading! @@ -90,13 +92,13 @@ bug fixes for critical issues may be backported for a time. 3. macOS 32-bit support removed. If you need this support, stay on the pre-1.0 releases. -### Fixes +## Fixes 1. All fixes in the 0.95.0 release are incorporated in this release. -## 0.95.0 (2018-09-27) +# 0.95.0 (2018-09-27) -### Fixes +## Fixes 1. `x-www-form-urlencoded` encoding and decoding should be 100% spec compliant now, previously the `+` character was not decoded as @@ -105,19 +107,19 @@ bug fixes for critical issues may be backported for a time. 2. `scope` no longer sent during token refresh (was redundant) https://github.com/openid/AppAuth-iOS/pull/301 -## 0.94.0 (2018-07-13) +# 0.94.0 (2018-07-13) -### Fixes +## Fixes 1. `form-urlencode` client ID and client secret in Authorization header -### Added +## Added 1. Samples have icons now! 2. Output trace logs by defining `_APPAUTHTRACE` -## 0.93.0 (2018-06-26) +# 0.93.0 (2018-06-26) -### Notable Changes +## Notable Changes 1. Implements OpenID Connect (ID Token handling) and the OpenID Connect RP Certification test suite. @@ -142,7 +144,7 @@ bug fixes for critical issues may be backported for a time. https://github.com/openid/AppAuth-iOS/issues/200 https://github.com/openid/AppAuth-iOS/pull/201 -### Upgrading to 0.93.0 +## Upgrading to 0.93.0 0.93.0 deprecates several methods. To update your code to avoid the deprecated methods (which will be required for the 1.0.0 release), @@ -156,7 +158,7 @@ for the new methods to use in those cases. Most users who are using the convenience methods of AppAuth will only need to make the following 3 minor changes to their AppDelegate: -#### Import: +### Import: Change ```objc @@ -167,7 +169,7 @@ to @protocol OIDExternalUserAgentSession; ``` -#### Property: +### Property: Change ```objc @@ -178,7 +180,7 @@ to @property(nonatomic, strong, nullable) idcurrentAuthorizationFlow; ``` -#### Implementation of `-(BOOL)application:openURL:options:` +### Implementation of `-(BOOL)application:openURL:options:` Change ```objc if ([_currentAuthorizationFlow resumeAuthorizationFlowWithURL:url]) { @@ -192,14 +194,14 @@ See also the changes made to the sample which you can copy: https://github.com/openid/AppAuth-iOS/commit/619bb7c7d5f83cc2ed19380d425ca8afa279644c?diff=unified -## 0.92.0 (2018-01-05) +# 0.92.0 (2018-01-05) -### Improvements +## Improvements 1. Added an official Swift sample, and included Swift testing in the continuous integration tests. -## Pre 0.92.0 +# Pre 0.92.0 No changelog entries exist for changes prior to 2018, please review the [git history](https://github.com/openid/AppAuth-iOS/commits/0.91.0). From 74239430b5219ba502d34349288cd27db010eca2 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Tue, 13 Sep 2022 13:05:29 -0700 Subject: [PATCH 167/204] Update issue templates (#723) * Update feature_request.md * Update bug_report.md * Update feature_request.md * Update bug_report.md * Update feature_request.md --- .github/ISSUE_TEMPLATE/bug_report.md | 13 ++++++------- .github/ISSUE_TEMPLATE/feature_request.md | 15 +++++++-------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 107a71184..4603ea210 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,10 +1,9 @@ --- -name: Bug report -about: Create a bug report to help us improve -title: '' -labels: '' -assignees: '' - +name: Bug Report +about: Submit a bug report if something isn't working as expected. +title: "" +labels: bug, triage +assignees: "" --- **Describe the bug** @@ -13,7 +12,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' -2. Click on '....' +2. Tap on '....' 3. Scroll down to '....' 4. See error diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d6..4b77f985d 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,14 +1,13 @@ --- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - +name: Feature Request +about: Make a feature request if you have a suggestion for something new. +title: "" +labels: enhancement, triage +assignees: "" --- -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +**Is your feature request related to a problem you're having? Please describe.** +A clear and concise description of what the problem is. Such as: "I'm always frustrated when ..." **Describe the solution you'd like** A clear and concise description of what you want to happen. From f4360e0b636f94379d9a17a004e743846f3e82e3 Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Thu, 15 Sep 2022 13:06:43 -0700 Subject: [PATCH 168/204] Update feature_request.md (#724) --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 4b77f985d..ab9e65fef 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,7 +7,7 @@ assignees: "" --- **Is your feature request related to a problem you're having? Please describe.** -A clear and concise description of what the problem is. Such as: "I'm always frustrated when ..." +A clear and concise description of what the problem is. **Describe the solution you'd like** A clear and concise description of what you want to happen. From ec85862bd118c04c6b247b6987da02a6b4f5742d Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Tue, 20 Sep 2022 11:56:19 -0700 Subject: [PATCH 169/204] Create config.yml (#725) --- .github/ISSUE_TEMPLATE/config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..3ba13e0ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false From b46216ab5c7a71634034cad03fc2ffe1b75005bf Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Wed, 19 Oct 2022 13:24:56 -0700 Subject: [PATCH 170/204] Update ObjC style guide link. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d4fe7b6fc..b319f9ddf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,7 @@ discussing your proposal, or email the ## Coding Standards The AppAuth library follows the -[Google Objective-C Style Guide](https://github.com/google/styleguide/blob/gh-pages/objcguide.md). Please review your own code for adherence to the +[Google Objective-C Style Guide](https://google.github.io/styleguide/objcguide.html). Please review your own code for adherence to the standard. ## Pull Request Reviews From caba19437351238c42c30dee00a260e9bfb865ec Mon Sep 17 00:00:00 2001 From: Peter Andrews Date: Thu, 12 Jan 2023 15:43:00 -0800 Subject: [PATCH 171/204] Use the macos-11 runner (#745) --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 166a260f7..5af5220f3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ on: jobs: xcode-project-test: - runs-on: macOS-latest + runs-on: macos-11 strategy: matrix: flags: [ @@ -32,7 +32,7 @@ jobs: ${{ matrix.flags }} pod-lib-lint: - runs-on: macOS-latest + runs-on: macos-11 strategy: matrix: flags: [ @@ -50,7 +50,7 @@ jobs: run: pod lib lint --verbose ${{ matrix.flags }} spm-build-test: - runs-on: macOS-latest + runs-on: macos-11 steps: - uses: actions/checkout@v2 - name: Build unit test target From ecc52c58742f0eeffa56f627378154b9b6181535 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 7 Apr 2023 13:35:25 -0700 Subject: [PATCH 172/204] Bump iOS minimum version in podspec to support Xcode 14.3 (#761) --- AppAuth.podspec | 10 ++++++---- Package.swift | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 4aeae48b5..f023a4fa3 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -31,8 +31,10 @@ It follows the OAuth 2.0 for Native Apps best current practice # classes of AppAuth with tokens on watchOS and tvOS, but currently the # library won't help you obtain authorization grants on those platforms. - s.ios.deployment_target = "7.0" - s.osx.deployment_target = "10.9" + ios_deployment_target = "9.0" + osx_deployment_target = "10.12" + s.ios.deployment_target = ios_deployment_target + s.osx.deployment_target = osx_deployment_target s.watchos.deployment_target = "2.0" s.tvos.deployment_target = "9.0" @@ -52,13 +54,13 @@ It follows the OAuth 2.0 for Native Apps best current practice # iOS externalUserAgent.ios.source_files = "Source/AppAuth/iOS/**/*.{h,m}" - externalUserAgent.ios.deployment_target = "7.0" + externalUserAgent.ios.deployment_target = ios_deployment_target externalUserAgent.ios.frameworks = "SafariServices" externalUserAgent.ios.weak_frameworks = "AuthenticationServices" # macOS externalUserAgent.osx.source_files = "Source/AppAuth/macOS/**/*.{h,m}" - externalUserAgent.osx.deployment_target = '10.9' + externalUserAgent.osx.deployment_target = osx_deployment_target externalUserAgent.osx.weak_frameworks = "AuthenticationServices" end diff --git a/Package.swift b/Package.swift index b031680df..ae72258ac 100644 --- a/Package.swift +++ b/Package.swift @@ -23,7 +23,7 @@ import PackageDescription let package = Package( name: "AppAuth", platforms: [ - .macOS(.v10_10), + .macOS(.v10_12), .iOS(.v9), .tvOS(.v9), .watchOS(.v2) From 0eadcdec4ddb121865f3d66917549194afce1f2b Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 7 Apr 2023 15:13:50 -0700 Subject: [PATCH 173/204] Update podspec version and added description to changelog (#763) --- AppAuth.podspec | 2 +- CHANGELOG.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index f023a4fa3..30ef456ea 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.6.0" + s.version = "1.6.1" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c086f028..49752c55f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.6.1 +- Increased minimum iOS and macOS versions to fix [build issue](https://github.com/openid/AppAuth-iOS/pull/761) + # 1.6.0 - Added a `prefersEphemeralSession` parameter for external user-agents. ([#645](https://github.com/openid/AppAuth-iOS/pull/645)) - Fixed errors encountered when using secure coding to decode `OIDAuthState`. ([#656](https://github.com/openid/AppAuth-iOS/pull/656), [#721](https://github.com/openid/AppAuth-iOS/pull/721)) From 0b62e41c224bc7ef9b074aac5970da0c16ab8798 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Wed, 19 Apr 2023 13:39:34 -0700 Subject: [PATCH 174/204] Update to macos-12 runner for workflows (#767) --- .github/workflows/tests.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5af5220f3..523bed57d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,19 +12,19 @@ on: jobs: xcode-project-test: - runs-on: macos-11 + runs-on: macos-12 strategy: matrix: flags: [ - "-scheme AppAuth-iOS -destination 'platform=iOS Simulator,name=iPhone 11,OS=15.2' -sdk 'iphonesimulator15.2'", - "-scheme AppAuth-macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx12.1'", - "-scheme AppAuth_macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx12.1'", - "-scheme AppAuth-tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=15.2' -sdk 'appletvsimulator15.2'", - "-scheme AppAuth_tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=15.2' -sdk 'appletvsimulator15.2'", - "-scheme AppAuthTV -destination 'platform=tvOS Simulator,name=Apple TV,OS=15.2' -sdk 'appletvsimulator15.2'" + "-scheme AppAuth-iOS -destination 'platform=iOS Simulator,name=iPhone 11,OS=16.2' -sdk 'iphonesimulator16.2'", + "-scheme AppAuth-macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx13.1'", + "-scheme AppAuth_macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx13.1'", + "-scheme AppAuth-tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=16.1' -sdk 'appletvsimulator16.1'", + "-scheme AppAuth_tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=16.1' -sdk 'appletvsimulator16.1'", + "-scheme AppAuthTV -destination 'platform=tvOS Simulator,name=Apple TV,OS=16.1' -sdk 'appletvsimulator16.1'" ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Run unit test targets run: | xcodebuild test \ @@ -32,7 +32,7 @@ jobs: ${{ matrix.flags }} pod-lib-lint: - runs-on: macos-11 + runs-on: macos-12 strategy: matrix: flags: [ @@ -41,7 +41,7 @@ jobs: '--use-static-frameworks' ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Update Bundler run: bundle update --bundler - name: Install Ruby gems with Bundler From 5ea841fcf9a2bf2f0f0dd6d677a585ac731b2949 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Wed, 19 Apr 2023 16:19:57 -0700 Subject: [PATCH 175/204] Update framework minimum OS versions (#766) --- AppAuth.xcodeproj/project.pbxproj | 44 +++++++++++++++++-------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 1a945644d..08e6f28a1 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -2615,7 +2615,7 @@ PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthTVTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - TVOS_DEPLOYMENT_TARGET = 10.1; + TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Debug; }; @@ -2630,7 +2630,7 @@ PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthTVTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - TVOS_DEPLOYMENT_TARGET = 10.1; + TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Release; }; @@ -2778,11 +2778,13 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + TVOS_DEPLOYMENT_TARGET = 9.0; WARNING_CFLAGS = "-Wno-gnu"; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Debug; }; @@ -2834,12 +2836,14 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; WARNING_CFLAGS = "-Wno-gnu"; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Release; }; @@ -2935,7 +2939,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-tvOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - TVOS_DEPLOYMENT_TARGET = 10.1; + TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Debug; }; @@ -2950,7 +2954,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-tvOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - TVOS_DEPLOYMENT_TARGET = 10.1; + TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Release; }; @@ -2964,7 +2968,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; - TVOS_DEPLOYMENT_TARGET = 10.1; + TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Debug; }; @@ -2978,7 +2982,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; - TVOS_DEPLOYMENT_TARGET = 10.1; + TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Release; }; @@ -2996,7 +3000,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Source/CoreFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthCore; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3022,7 +3026,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Source/CoreFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthCore; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3047,7 +3051,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Source/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOS"; PRODUCT_NAME = AppAuth; @@ -3072,7 +3076,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Source/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOS"; PRODUCT_NAME = AppAuth; @@ -3091,7 +3095,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3105,7 +3109,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3225,7 +3229,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-tvOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - TVOS_DEPLOYMENT_TARGET = 10.1; + TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Debug; }; @@ -3240,7 +3244,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-tvOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - TVOS_DEPLOYMENT_TARGET = 10.1; + TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Release; }; @@ -3335,7 +3339,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; - WATCHOS_DEPLOYMENT_TARGET = 3.1; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Debug; }; @@ -3348,7 +3352,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; - WATCHOS_DEPLOYMENT_TARGET = 3.1; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Release; }; @@ -3359,7 +3363,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-ExtensionTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3373,7 +3377,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-ExtensionTests"; PRODUCT_NAME = "$(TARGET_NAME)"; From 71cde449f13d453227e687458144bde372d30fc7 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 28 Apr 2023 10:27:10 -0700 Subject: [PATCH 176/204] Bump podspec to 1.6.2 and add entry to Changelog (#769) --- AppAuth.podspec | 2 +- CHANGELOG.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 30ef456ea..b175e6282 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.6.1" + s.version = "1.6.2" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index 49752c55f..b682c8a8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.6.2 +- Increased minimum iOS and macOS versions to 9.0 and 10.12 respectively to fix [framework build issue](https://github.com/openid/AppAuth-iOS/issues/765) + # 1.6.1 - Increased minimum iOS and macOS versions to fix [build issue](https://github.com/openid/AppAuth-iOS/pull/761) From 36c1df0c70324bc3a41ef96f9bb568670cc366d0 Mon Sep 17 00:00:00 2001 From: Vitalij Dadaschjanz Date: Mon, 26 Jun 2023 19:39:09 +0200 Subject: [PATCH 177/204] introduce addtional http headers to OIDTokenRequest (#770) Co-authored-by: Vitalij Dadaschjanz --- .../Source/AppAuthExampleViewController.m | 3 +- .../Source/AppAuthExampleViewController.m | 3 +- .../Source/AppAuthExampleViewController.swift | 3 +- .../AppAuthTVExampleViewController.m | 3 +- README.md | 3 +- Source/AppAuthCore/OIDAuthState.h | 31 +++++++++++ Source/AppAuthCore/OIDAuthState.m | 51 +++++++++++++++--- Source/AppAuthCore/OIDAuthorizationResponse.h | 4 +- Source/AppAuthCore/OIDAuthorizationResponse.m | 9 ++-- Source/AppAuthCore/OIDTokenRequest.h | 12 ++++- Source/AppAuthCore/OIDTokenRequest.m | 39 +++++++++++--- Source/AppAuthTV/OIDTVAuthorizationResponse.h | 19 +++++++ Source/AppAuthTV/OIDTVAuthorizationResponse.m | 29 +++++++++- Source/AppAuthTV/OIDTVTokenRequest.h | 13 +++-- Source/AppAuthTV/OIDTVTokenRequest.m | 13 ++++- .../OIDTVAuthorizationResponseTests.h | 6 +-- .../OIDTVAuthorizationResponseTests.m | 20 +++++-- UnitTests/AppAuthTV/OIDTVTokenRequestTests.m | 13 ++++- UnitTests/OIDAuthStateTests.m | 21 ++++++++ UnitTests/OIDTokenRequestTests.m | 54 ++++++++++++++++--- 20 files changed, 303 insertions(+), 46 deletions(-) diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m b/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m index dc76a8c9c..4d58cf9d2 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m +++ b/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m @@ -177,7 +177,8 @@ - (void)doClientRegistration:(OIDServiceConfiguration *)configuration grantTypes:nil subjectType:nil tokenEndpointAuthMethod:@"client_secret_post" - additionalParameters:nil]; + additionalParameters:nil + additionalHeaders:nil]; // performs registration request [self logMessage:@"Initiating registration request"]; diff --git a/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m b/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m index 2c3ebe03e..d67c7b73d 100644 --- a/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m +++ b/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m @@ -179,7 +179,8 @@ - (void)doClientRegistration:(OIDServiceConfiguration *)configuration grantTypes:nil subjectType:nil tokenEndpointAuthMethod:@"client_secret_post" - additionalParameters:nil]; + additionalParameters:nil + additionalHeaders:nil]; // performs registration request [self logMessage:@"Initiating registration request"]; diff --git a/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift index f70540472..91cf79fa4 100644 --- a/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift +++ b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift @@ -349,7 +349,8 @@ extension AppAuthExampleViewController { grantTypes: nil, subjectType: nil, tokenEndpointAuthMethod: "client_secret_post", - additionalParameters: nil) + additionalParameters: nil, + additionalHeaders: nil) // performs registration request self.logMessage("Initiating registration request") diff --git a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m index 3d461619d..e97b0d204 100644 --- a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m +++ b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m @@ -176,7 +176,8 @@ - (void)performAuthorizationWithConfiguration:(OIDTVServiceConfiguration *)confi clientId:kClientID clientSecret:kClientSecret scopes:@[ OIDScopeOpenID, OIDScopeProfile ] - additionalParameters:nil]; + additionalParameters:nil + additionalHeaders:nil]; OIDTVAuthorizationInitialization initBlock = ^(OIDTVAuthorizationResponse *_Nullable response, NSError *_Nullable error) { diff --git a/README.md b/README.md index 78f79959f..53085b3c4 100644 --- a/README.md +++ b/README.md @@ -516,7 +516,8 @@ OIDTVAuthorizationRequest *request = clientId:kClientID clientSecret:kClientSecret scopes:@[ OIDScopeOpenID, OIDScopeProfile ] - additionalParameters:nil]; + additionalParameters:nil + additionalHeaders:nil]; // performs authentication request OIDTVAuthorizationInitialization initBlock = diff --git a/Source/AppAuthCore/OIDAuthState.h b/Source/AppAuthCore/OIDAuthState.h index 68697d2ca..46c78a831 100644 --- a/Source/AppAuthCore/OIDAuthState.h +++ b/Source/AppAuthCore/OIDAuthState.h @@ -48,6 +48,12 @@ typedef void (^OIDAuthStateAction)(NSString *_Nullable accessToken, typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authState, NSError *_Nullable error); +/*! @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 A convenience class that retains the auth state between @c OIDAuthorizationResponse%s and @c OIDTokenResponse%s. */ @@ -267,6 +273,31 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt - (nullable OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: (nullable NSDictionary *)additionalParameters; +/*! @brief Creates a token request suitable for refreshing an access token. + @param additionalParameters Additional parameters for the token request. + @param additionalHeaders Additional headers 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 *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders; + +/*! @brief Creates a token request suitable for refreshing an access token. + @param additionalHeaders 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 *)tokenRefreshRequestWithAdditionalHeaders: + (nullable NSDictionary *)additionalHeaders; + @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthCore/OIDAuthState.m b/Source/AppAuthCore/OIDAuthState.m index fe8a16221..cb5a22a1e 100644 --- a/Source/AppAuthCore/OIDAuthState.m +++ b/Source/AppAuthCore/OIDAuthState.m @@ -55,12 +55,6 @@ */ 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; @@ -427,7 +421,47 @@ - (OIDTokenRequest *)tokenRefreshRequest { - (OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: (NSDictionary *)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 + additionalHeaders:nil]; +} + +- (OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: + (NSDictionary *)additionalParameters + additionalHeaders: + (NSDictionary *)additionalHeaders { + + 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 + additionalHeaders:additionalHeaders]; +} + +- (OIDTokenRequest *)tokenRefreshRequestWithAdditionalHeaders: + (NSDictionary *)additionalHeaders { if (!_refreshToken) { [OIDErrorUtilities raiseException:kRefreshTokenRequestException]; @@ -442,7 +476,8 @@ - (OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: scope:nil refreshToken:_refreshToken codeVerifier:nil - additionalParameters:additionalParameters]; + additionalParameters:nil + additionalHeaders:additionalHeaders]; } #pragma mark - Stateful Actions diff --git a/Source/AppAuthCore/OIDAuthorizationResponse.h b/Source/AppAuthCore/OIDAuthorizationResponse.h index e7552fe59..2a10c81f2 100644 --- a/Source/AppAuthCore/OIDAuthorizationResponse.h +++ b/Source/AppAuthCore/OIDAuthorizationResponse.h @@ -121,7 +121,9 @@ NS_ASSUME_NONNULL_BEGIN @see https://tools.ietf.org/html/rfc6749#section-4.1.3 */ - (nullable OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: - (nullable NSDictionary *)additionalParameters; + (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders; @end diff --git a/Source/AppAuthCore/OIDAuthorizationResponse.m b/Source/AppAuthCore/OIDAuthorizationResponse.m index a8f92c75e..5c998a966 100644 --- a/Source/AppAuthCore/OIDAuthorizationResponse.m +++ b/Source/AppAuthCore/OIDAuthorizationResponse.m @@ -184,11 +184,13 @@ - (NSString *)description { #pragma mark - - (OIDTokenRequest *)tokenExchangeRequest { - return [self tokenExchangeRequestWithAdditionalParameters:nil]; + return [self tokenExchangeRequestWithAdditionalParameters:nil additionalHeaders:nil]; } - (OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: - (NSDictionary *)additionalParameters { + (NSDictionary *)additionalParameters + additionalHeaders: + (NSDictionary *)additionalHeaders { // TODO: add a unit test to confirm exception is thrown when expected and the request is created // with the correct parameters. if (!_authorizationCode) { @@ -204,7 +206,8 @@ - (OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: scope:nil refreshToken:nil codeVerifier:_request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; } @end diff --git a/Source/AppAuthCore/OIDTokenRequest.h b/Source/AppAuthCore/OIDTokenRequest.h index 399294e8c..1d161cd08 100644 --- a/Source/AppAuthCore/OIDTokenRequest.h +++ b/Source/AppAuthCore/OIDTokenRequest.h @@ -95,9 +95,13 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, readonly, nullable) NSDictionary *additionalParameters; +/*! @brief The client's additional token request headers. + */ +@property(nonatomic, readonly, nullable) NSDictionary *additionalHeaders; + /*! @internal @brief Unavailable. Please use - initWithConfiguration:grantType:code:redirectURL:clientID:additionalParameters:. + initWithConfiguration:grantType:code:redirectURL:clientID:additionalParameters:additionalHeaders:. */ - (instancetype)init NS_UNAVAILABLE; @@ -113,6 +117,7 @@ NS_ASSUME_NONNULL_BEGIN @param refreshToken The refresh token. @param codeVerifier The PKCE code verifier. @param additionalParameters The client's additional token request parameters. + @param additionalHeaders The client's additional token request headers. */ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration grantType:(NSString *)grantType @@ -123,7 +128,8 @@ NS_ASSUME_NONNULL_BEGIN scopes:(nullable NSArray *)scopes refreshToken:(nullable NSString *)refreshToken codeVerifier:(nullable NSString *)codeVerifier - additionalParameters:(nullable NSDictionary *)additionalParameters; + additionalParameters:(nullable NSDictionary *)additionalParameters + additionalHeaders:(nullable NSDictionary *)additionalHeaders; /*! @brief Designated initializer. @param configuration The service's configuration. @@ -139,6 +145,7 @@ NS_ASSUME_NONNULL_BEGIN @param refreshToken The refresh token. @param codeVerifier The PKCE code verifier. @param additionalParameters The client's additional token request parameters. + @param additionalHeaders The client's additional token request headers. */ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration grantType:(NSString *)grantType @@ -150,6 +157,7 @@ NS_ASSUME_NONNULL_BEGIN refreshToken:(nullable NSString *)refreshToken codeVerifier:(nullable NSString *)codeVerifier additionalParameters:(nullable NSDictionary *)additionalParameters + additionalHeaders:(nullable NSDictionary *)additionalHeaders NS_DESIGNATED_INITIALIZER; /*! @brief Designated initializer for NSSecureCoding. diff --git a/Source/AppAuthCore/OIDTokenRequest.m b/Source/AppAuthCore/OIDTokenRequest.m index 5ed8a17ef..08b0dafec 100644 --- a/Source/AppAuthCore/OIDTokenRequest.m +++ b/Source/AppAuthCore/OIDTokenRequest.m @@ -67,6 +67,11 @@ */ static NSString *const kAdditionalParametersKey = @"additionalParameters"; +/*! @brief Key used to encode the @c additionalHeaders property for + @c NSSecureCoding + */ +static NSString *const kAdditionalHeadersKey = @"additionalHeaders"; + @implementation OIDTokenRequest - (instancetype)init @@ -80,7 +85,8 @@ - (instancetype)init scope: refreshToken: codeVerifier: - additionalParameters:) + additionalParameters: + additionalHeaders:) ) - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -92,7 +98,8 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration scopes:(nullable NSArray *)scopes refreshToken:(nullable NSString *)refreshToken codeVerifier:(nullable NSString *)codeVerifier - additionalParameters:(nullable NSDictionary *)additionalParameters { + additionalParameters:(nullable NSDictionary *)additionalParameters + additionalHeaders:(nullable NSDictionary *)additionalHeaders { return [self initWithConfiguration:configuration grantType:grantType authorizationCode:code @@ -102,7 +109,8 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration scope:[OIDScopeUtilities scopesWithArray:scopes] refreshToken:refreshToken codeVerifier:(NSString *)codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; } - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -114,7 +122,8 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration scope:(nullable NSString *)scope refreshToken:(nullable NSString *)refreshToken codeVerifier:(nullable NSString *)codeVerifier - additionalParameters:(nullable NSDictionary *)additionalParameters { + additionalParameters:(nullable NSDictionary *)additionalParameters + additionalHeaders:(nullable NSDictionary *)additionalHeaders { self = [super init]; if (self) { _configuration = [configuration copy]; @@ -128,6 +137,8 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration _codeVerifier = [codeVerifier copy]; _additionalParameters = [[NSDictionary alloc] initWithDictionary:additionalParameters copyItems:YES]; + _additionalHeaders = + [[NSDictionary alloc] initWithDictionary:additionalHeaders copyItems:YES]; // Additional validation for the authorization_code grant type if ([_grantType isEqual:OIDGrantTypeAuthorizationCode]) { @@ -174,9 +185,18 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { [NSDictionary class], [NSString class] ]]; + NSDictionary *additionalParameters = - [aDecoder decodeObjectOfClasses:additionalParameterCodingClasses - forKey:kAdditionalParametersKey]; + [aDecoder decodeObjectOfClasses:additionalParameterCodingClasses forKey:kAdditionalParametersKey]; + + + NSSet *additionalHeaderCodingClasses = [NSSet setWithArray:@[ + [NSDictionary class], + [NSString class] + ]]; + + NSDictionary *additionalHeaders = + [aDecoder decodeObjectOfClasses:additionalHeaderCodingClasses forKey:kAdditionalHeadersKey]; self = [super init]; if (self) { @@ -191,6 +211,8 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { _codeVerifier = [codeVerifier copy]; _additionalParameters = [[NSDictionary alloc] initWithDictionary:additionalParameters copyItems:YES]; + _additionalHeaders = + [[NSDictionary alloc] initWithDictionary:additionalHeaders copyItems:YES]; } return self; } @@ -206,6 +228,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:_refreshToken forKey:kRefreshTokenKey]; [aCoder encodeObject:_codeVerifier forKey:kCodeVerifierKey]; [aCoder encodeObject:_additionalParameters forKey:kAdditionalParametersKey]; + [aCoder encodeObject:_additionalHeaders forKey:kAdditionalHeadersKey]; } #pragma mark - NSObject overrides @@ -305,6 +328,10 @@ - (NSURLRequest *)URLRequest { for (id header in httpHeaders) { [URLRequest setValue:httpHeaders[header] forHTTPHeaderField:header]; } + + for (id header in _additionalHeaders) { + [URLRequest setValue:httpHeaders[header] forHTTPHeaderField:header]; + } return URLRequest; } diff --git a/Source/AppAuthTV/OIDTVAuthorizationResponse.h b/Source/AppAuthTV/OIDTVAuthorizationResponse.h index d3bed1e97..c57847c6e 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationResponse.h +++ b/Source/AppAuthTV/OIDTVAuthorizationResponse.h @@ -87,6 +87,25 @@ NS_ASSUME_NONNULL_BEGIN - (nullable OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: (nullable NSDictionary *)additionalParameters; +/*! @brief Creates a token request suitable for polling the token endpoint with the @c deviceCode. + @param additionalHeaders Additional headers for the token request. + @return A @c OIDTVTokenRequest suitable for polling the token endpoint. + @see https://tools.ietf.org/html/rfc8628#section-3.4 + */ +- (nullable OIDTVTokenRequest *)tokenPollRequestWithAdditionalHeaders: + (nullable NSDictionary *)additionalHeaders; + +/*! @brief Creates a token request suitable for polling the token endpoint with the @c deviceCode. + @param additionalParameters Additional parameters for the token request. + @param additionalHeaders Additional headers for the token request. + @return A @c OIDTVTokenRequest suitable for polling the token endpoint. + @see https://tools.ietf.org/html/rfc8628#section-3.4 + */ +- (nullable OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: + (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders; + @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthTV/OIDTVAuthorizationResponse.m b/Source/AppAuthTV/OIDTVAuthorizationResponse.m index 71b9e8f04..b45fc85f3 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationResponse.m +++ b/Source/AppAuthTV/OIDTVAuthorizationResponse.m @@ -149,7 +149,7 @@ - (NSString *)description { #pragma mark - - (OIDTVTokenRequest *)tokenPollRequest { - return [self tokenPollRequestWithAdditionalParameters:nil]; + return [self tokenPollRequestWithAdditionalParameters:nil additionalHeaders:nil]; } - (OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: @@ -159,7 +159,32 @@ - (OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: deviceCode:_deviceCode clientID:self.request.clientID clientSecret:self.request.clientSecret - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:nil]; +} + +- (OIDTVTokenRequest *)tokenPollRequestWithAdditionalHeaders: + (NSDictionary *)additionalHeaders { + return [[OIDTVTokenRequest alloc] + initWithConfiguration:(OIDTVServiceConfiguration *)self.request.configuration + deviceCode:_deviceCode + clientID:self.request.clientID + clientSecret:self.request.clientSecret + additionalParameters:nil + additionalHeaders:additionalHeaders]; +} + +- (OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: + (NSDictionary *)additionalParameters + additionalHeaders: + (NSDictionary *)additionalHeaders { + return [[OIDTVTokenRequest alloc] + initWithConfiguration:(OIDTVServiceConfiguration *)self.request.configuration + deviceCode:_deviceCode + clientID:self.request.clientID + clientSecret:self.request.clientSecret + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; } @end diff --git a/Source/AppAuthTV/OIDTVTokenRequest.h b/Source/AppAuthTV/OIDTVTokenRequest.h index 5a81c7434..021dc9b9d 100644 --- a/Source/AppAuthTV/OIDTVTokenRequest.h +++ b/Source/AppAuthTV/OIDTVTokenRequest.h @@ -35,14 +35,14 @@ NS_ASSUME_NONNULL_BEGIN /*! @internal @brief Unavailable. Please use - @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters: + @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters:additionalHeaders: or @c initWithCoder:. */ - (instancetype)init NS_UNAVAILABLE; /*! @internal @brief Unavailable. Please use - @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters: + @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters:additionalHeaders: or @c initWithCoder:. */ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -56,11 +56,13 @@ NS_ASSUME_NONNULL_BEGIN codeVerifier:(nullable NSString *)codeVerifier additionalParameters: (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders NS_UNAVAILABLE; /*! @internal @brief Unavailable. Please use - @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters: + @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters:additionalHeaders: or @c initWithCoder:. */ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -74,6 +76,8 @@ NS_ASSUME_NONNULL_BEGIN codeVerifier:(nullable NSString *)codeVerifier additionalParameters: (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders NS_UNAVAILABLE; /*! @brief Designated initializer. @@ -82,6 +86,7 @@ NS_ASSUME_NONNULL_BEGIN @param clientID The client identifier. @param clientSecret The client secret (nullable). @param additionalParameters The client's additional token request parameters. + @param additionalHeaders The client's additional token request headers. */ - (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration deviceCode:(NSString *)deviceCode @@ -89,6 +94,8 @@ NS_ASSUME_NONNULL_BEGIN clientSecret:(nullable NSString *)clientSecret additionalParameters: (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders NS_DESIGNATED_INITIALIZER; /*! @brief Designated initializer for NSSecureCoding. diff --git a/Source/AppAuthTV/OIDTVTokenRequest.m b/Source/AppAuthTV/OIDTVTokenRequest.m index 88874a817..ed5e4d3f2 100644 --- a/Source/AppAuthTV/OIDTVTokenRequest.m +++ b/Source/AppAuthTV/OIDTVTokenRequest.m @@ -43,6 +43,7 @@ - (instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector clientID: clientSecret: additionalParameters: + additionalHeaders: )) - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -56,12 +57,15 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration codeVerifier:(nullable NSString *)codeVerifier additionalParameters: (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders OID_UNAVAILABLE_USE_INITIALIZER(@selector (initWithConfiguration: deviceCode: clientID: clientSecret: additionalParameters: + additionalHeaders: )) - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -75,19 +79,23 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration codeVerifier:(nullable NSString *)codeVerifier additionalParameters: (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders OID_UNAVAILABLE_USE_INITIALIZER(@selector (initWithConfiguration: deviceCode: clientID: clientSecret: additionalParameters: + additionalHeaders: )) - (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration deviceCode:(NSString *)deviceCode clientID:(NSString *)clientID clientSecret:(NSString *)clientSecret - additionalParameters:(NSDictionary *)additionalParameters { + additionalParameters:(NSDictionary *)additionalParameters + additionalHeaders:(NSDictionary *)additionalHeaders { self = [super initWithConfiguration:configuration grantType:kOIDTVDeviceTokenGrantType authorizationCode:nil @@ -97,7 +105,8 @@ - (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration scope:nil refreshToken:nil codeVerifier:nil - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; if (self) { _deviceCode = [deviceCode copy]; diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h index 32497c854..2ffbc5775 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h @@ -48,10 +48,10 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)testTokenPollRequest; -/*! @brief Tests the @c tokenPollRequestWithAdditionalParameters method with one additional - parameter. +/*! @brief Tests the @c testTokenPollRequestWithAdditionalParametersAdditionalHeaders method with one additional + parameter and one additional header. */ -- (void)testTokenPollRequestWithAdditionalParameters; +- (void)testTokenPollRequestWithAdditionalParametersAdditionalHeaders; @end diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m index a6d1bb2f5..288228e88 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m @@ -45,6 +45,14 @@ */ static NSString *const kTestAdditionalParameterValue = @"1"; +/*! @brief Test key for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderKey = @"B"; + +/*! @brief Test value for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderValue = @"2"; + /*! @brief Test value for the @c clientID property. */ static NSString *const kTestClientID = @"ClientID"; @@ -262,22 +270,26 @@ - (void)testTokenPollRequest { XCTAssertEqualObjects(pollRequest.additionalParameters, @{}); } -/*! @brief Tests the @c tokenPollRequestWithAdditionalParameters method with one additional - parameter. +/*! @brief Tests the @c testTokenPollRequestWithAdditionalParametersAdditionalHeaders method with one additional + parameter and one additional header. */ -- (void)testTokenPollRequestWithAdditionalParameters { +- (void)testTokenPollRequestWithAdditionalParametersAdditionalHeaders { OIDTVAuthorizationResponse *testResponse = [self testAuthorizationResponse]; NSDictionary *testAdditionalParameters = @{kTestAdditionalParameterKey : kTestAdditionalParameterValue}; + + NSDictionary *testAdditionalHeaders = + @{kTestAdditionalHeaderKey : kTestAdditionalHeaderValue}; OIDTVTokenRequest *pollRequest = - [testResponse tokenPollRequestWithAdditionalParameters:testAdditionalParameters]; + [testResponse tokenPollRequestWithAdditionalParameters:testAdditionalParameters additionalHeaders:testAdditionalHeaders]; XCTAssertEqualObjects(pollRequest.deviceCode, kTestDeviceCode); XCTAssertEqualObjects(pollRequest.clientID, kTestClientID); XCTAssertEqualObjects(pollRequest.clientSecret, kTestClientSecret); XCTAssertEqualObjects(pollRequest.additionalParameters, testAdditionalParameters); + XCTAssertEqualObjects(pollRequest.additionalHeaders, testAdditionalHeaders); } @end diff --git a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m index cf0bf4963..4778a227b 100644 --- a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m +++ b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m @@ -50,6 +50,14 @@ */ static NSString *const kTestAdditionalParameterValue = @"1"; +/*! @brief Test key for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderKey = @"B"; + +/*! @brief Test value for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderValue = @"2"; + /*! @brief Test key for the @c clientID parameter in the HTTP request. */ static NSString *const kTestClientIDKey = @"client_id"; @@ -121,7 +129,8 @@ - (OIDTVTokenRequest *)testTokenRequest { deviceCode:kDeviceCodeValue clientID:kTestClientID clientSecret:kTestClientSecret - additionalParameters:@{kTestAdditionalParameterKey : kTestAdditionalParameterValue}]; + additionalParameters:@{kTestAdditionalParameterKey : kTestAdditionalParameterValue} + additionalHeaders:@{kTestAdditionalHeaderKey : kTestAdditionalHeaderValue}]; } /*! @brief Tests the initializer @@ -139,6 +148,8 @@ - (void)testInitializer { XCTAssertEqualObjects(request.clientSecret, kTestClientSecret); XCTAssertEqualObjects(request.additionalParameters, @{kTestAdditionalParameterKey:kTestAdditionalParameterValue}); + XCTAssertEqualObjects(request.additionalHeaders, + @{kTestAdditionalHeaderKey:kTestAdditionalHeaderValue}); } /*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying diff --git a/UnitTests/OIDAuthStateTests.m b/UnitTests/OIDAuthStateTests.m index 4d7c3a8b7..d12f2a831 100644 --- a/UnitTests/OIDAuthStateTests.m +++ b/UnitTests/OIDAuthStateTests.m @@ -435,6 +435,27 @@ - (void)testIsTokenFreshHandlesTokenWithoutExpirationTime { XCTAssertEqual([authState isTokenFresh], YES, @""); } +- (void)testThatRefreshTokenExceptionWillBeRaisedForTokenRequestWithAdditionalParameters { + OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:nil tokenResponse:nil registrationResponse:nil]; + XCTAssertThrowsSpecificNamed([authState tokenRefreshRequestWithAdditionalParameters:nil], + NSException, + kRefreshTokenRequestException); +} + +- (void)testThatRefreshTokenExceptionWillBeRaisedForTokenRequestWithAdditionalHeaders { + OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:nil tokenResponse:nil registrationResponse:nil]; + XCTAssertThrowsSpecificNamed([authState tokenRefreshRequestWithAdditionalHeaders:nil], + NSException, + kRefreshTokenRequestException); +} + +- (void)testThatRefreshTokenExceptionWillBeRaisedForTokenRequestWithAdditionalParametersAndHeaders { + OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:nil tokenResponse:nil registrationResponse:nil]; + XCTAssertThrowsSpecificNamed([authState tokenRefreshRequestWithAdditionalHeaders:nil], + NSException, + kRefreshTokenRequestException); +} + @end #pragma GCC diagnostic pop diff --git a/UnitTests/OIDTokenRequestTests.m b/UnitTests/OIDTokenRequestTests.m index 4211ef70a..2aa865238 100644 --- a/UnitTests/OIDTokenRequestTests.m +++ b/UnitTests/OIDTokenRequestTests.m @@ -48,6 +48,14 @@ */ static NSString *const kTestAdditionalParameterValue = @"1"; +/*! @brief Test key for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderKey = @"B"; + +/*! @brief Test value for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderValue = @"2"; + @implementation OIDTokenRequestTests + (OIDTokenRequest *)testInstance { @@ -56,6 +64,9 @@ + (OIDTokenRequest *)testInstance { [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; NSDictionary *additionalParameters = @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + NSDictionary *additionalHeaders = + @{ kTestAdditionalHeaderKey : kTestAdditionalHeaderValue }; + OIDTokenRequest *request = [[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration grantType:OIDGrantTypeAuthorizationCode @@ -66,7 +77,8 @@ + (OIDTokenRequest *)testInstance { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; return request; } @@ -76,6 +88,9 @@ + (OIDTokenRequest *)testInstanceCodeExchange { [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; NSDictionary *additionalParameters = @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + NSDictionary *additionalHeaders = + @{ kTestAdditionalHeaderKey : kTestAdditionalHeaderValue }; + OIDTokenRequest *request = [[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration grantType:OIDGrantTypeAuthorizationCode @@ -86,7 +101,8 @@ + (OIDTokenRequest *)testInstanceCodeExchange { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; return request; } @@ -96,6 +112,9 @@ + (OIDTokenRequest *)testInstanceCodeExchangeClientAuth { [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; NSDictionary *additionalParameters = @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + NSDictionary *additionalHeaders = + @{ kTestAdditionalHeaderKey : kTestAdditionalHeaderValue }; + OIDTokenRequest *request = [[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration grantType:OIDGrantTypeAuthorizationCode @@ -106,7 +125,8 @@ + (OIDTokenRequest *)testInstanceCodeExchangeClientAuth { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; return request; } @@ -116,6 +136,9 @@ + (OIDTokenRequest *)testInstanceRefresh { [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; NSDictionary *additionalParameters = @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + NSDictionary *additionalHeaders = + @{ kTestAdditionalHeaderKey : kTestAdditionalHeaderValue }; + OIDTokenRequest *request = [[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration grantType:OIDGrantTypeAuthorizationCode @@ -126,7 +149,8 @@ + (OIDTokenRequest *)testInstanceRefresh { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; return request; } @@ -157,11 +181,17 @@ - (void)testCopying { XCTAssertEqualObjects(request.codeVerifier, authResponse.request.codeVerifier, @"Request and response codeVerifiers should be equal."); XCTAssertNotNil(request.additionalParameters, - @"Request's additionalParameters field should not be nil."); + @"Request's additionalParameters field should not be nil."); XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue, @"The request's kTestAdditionalParameterKey additional parameter should " "be equal to kTestAdditionalParameterValue."); + XCTAssertNotNil(request.additionalHeaders, + @"Request's additionalHeaders field should not be nil."); + XCTAssertEqualObjects(request.additionalHeaders[kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue, + @"The request's kTestAdditionalHeaderKey additional parameter should " + "be equal to kTestAdditionalHeaderValue."); OIDTokenRequest *requestCopy = [request copy]; @@ -181,6 +211,9 @@ - (void)testCopying { XCTAssertNotNil(requestCopy.additionalParameters, @""); XCTAssertEqualObjects(requestCopy.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue, @""); + XCTAssertNotNil(requestCopy.additionalHeaders, @""); + XCTAssertEqualObjects(requestCopy.additionalHeaders[kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue, @""); } /*! @brief Tests the @c NSSecureCoding by round-tripping an instance through the coding process and @@ -203,6 +236,9 @@ - (void)testSecureCoding { XCTAssertNotNil(request.additionalParameters, @""); XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue, @""); + XCTAssertNotNil(request.additionalHeaders, @""); + XCTAssertEqualObjects(request.additionalHeaders[kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue, @""); NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; OIDTokenRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; @@ -224,6 +260,9 @@ - (void)testSecureCoding { XCTAssertNotNil(requestCopy.additionalParameters, @""); XCTAssertEqualObjects(requestCopy.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue, @""); + XCTAssertNotNil(requestCopy.additionalHeaders, @""); + XCTAssertEqualObjects(requestCopy.additionalHeaders[kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue, @""); } - (void)testURLRequestNoClientAuth { @@ -248,6 +287,8 @@ - (void)testAuthorizationCodeNullRedirectURL { [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; NSDictionary *additionalParameters = @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + NSDictionary *additionalHeaders = + @{ kTestAdditionalHeaderKey : kTestAdditionalHeaderValue }; XCTAssertThrows([[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration grantType:OIDGrantTypeAuthorizationCode authorizationCode:authResponse.authorizationCode @@ -257,7 +298,8 @@ - (void)testAuthorizationCodeNullRedirectURL { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters], @""); + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders], @""); } @end From b376a8719dd9e3067439580d3b2c8aded748827f Mon Sep 17 00:00:00 2001 From: Daniel Seither Date: Wed, 4 Oct 2023 17:46:23 +0200 Subject: [PATCH 178/204] Fix nullability annotation for -[OIDExternalUserAgentIOS init] (#727) --- Source/AppAuth/iOS/OIDExternalUserAgentIOS.h | 2 +- Source/AppAuth/iOS/OIDExternalUserAgentIOS.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h index ae0773c69..12abc203c 100644 --- a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h +++ b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h @@ -34,7 +34,7 @@ NS_ASSUME_NONNULL_BEGIN API_UNAVAILABLE(macCatalyst) @interface OIDExternalUserAgentIOS : NSObject -- (nullable instancetype)init API_AVAILABLE(ios(11)) +- (null_unspecified instancetype)init API_AVAILABLE(ios(11)) __deprecated_msg("This method will not work on iOS 13, use " "initWithPresentingViewController:presentingViewController"); diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m index eab7aa3cb..4a8cda0a3 100644 --- a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m +++ b/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m @@ -55,7 +55,7 @@ @implementation OIDExternalUserAgentIOS { #pragma clang diagnostic pop } -- (nullable instancetype)init { +- (null_unspecified instancetype)init { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" return [self initWithPresentingViewController:nil]; From 8b437c47f46d505b7bf9a580626472b2568f38ce Mon Sep 17 00:00:00 2001 From: Vojtech Novak Date: Wed, 18 Oct 2023 20:11:59 +0200 Subject: [PATCH 179/204] feat: allow custom nonce in OIDAuthorizationRequest (#788) --- Source/AppAuthCore/OIDAuthorizationRequest.h | 23 +++++++++++++++++ Source/AppAuthCore/OIDAuthorizationRequest.m | 26 ++++++++++++++++++++ UnitTests/OIDAuthorizationRequestTests.m | 23 +++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/Source/AppAuthCore/OIDAuthorizationRequest.h b/Source/AppAuthCore/OIDAuthorizationRequest.h index 594f01d87..c3a0cc0d8 100644 --- a/Source/AppAuthCore/OIDAuthorizationRequest.h +++ b/Source/AppAuthCore/OIDAuthorizationRequest.h @@ -159,6 +159,29 @@ extern NSString *const OIDOAuthorizationRequestCodeChallengeMethodS256; responseType:(NSString *)responseType additionalParameters:(nullable NSDictionary *)additionalParameters; +/*! @brief Creates an authorization request with custom nonce, 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 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 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 *)scopes + redirectURL:(NSURL *)redirectURL + responseType:(NSString *)responseType + nonce:(nullable NSString *)nonce + additionalParameters:(nullable NSDictionary *)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. diff --git a/Source/AppAuthCore/OIDAuthorizationRequest.m b/Source/AppAuthCore/OIDAuthorizationRequest.m index ccfacda0f..1be1fdfde 100644 --- a/Source/AppAuthCore/OIDAuthorizationRequest.m +++ b/Source/AppAuthCore/OIDAuthorizationRequest.m @@ -202,6 +202,32 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration additionalParameters:additionalParameters]; } +- (instancetype) + initWithConfiguration:(OIDServiceConfiguration *)configuration + clientId:(NSString *)clientID + scopes:(nullable NSArray *)scopes + redirectURL:(NSURL *)redirectURL + responseType:(NSString *)responseType + nonce:(nullable NSString *)nonce + additionalParameters:(nullable NSDictionary *)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:nil + scope:[OIDScopeUtilities scopesWithArray:scopes] + redirectURL:redirectURL + responseType:responseType + state:[[self class] generateState] + nonce:nonce + codeVerifier:codeVerifier + codeChallenge:codeChallenge + codeChallengeMethod:OIDOAuthorizationRequestCodeChallengeMethodS256 + additionalParameters:additionalParameters]; +} + #pragma mark - NSCopying - (instancetype)copyWithZone:(nullable NSZone *)zone { diff --git a/UnitTests/OIDAuthorizationRequestTests.m b/UnitTests/OIDAuthorizationRequestTests.m index 06bfc6c13..92ed8ee05 100644 --- a/UnitTests/OIDAuthorizationRequestTests.m +++ b/UnitTests/OIDAuthorizationRequestTests.m @@ -223,6 +223,29 @@ - (void)testScopeInitializerWithManyScopesAndNoClientSecret { kTestAdditionalParameterValue, @""); } + +/*! @brief Tests the initializer which takes a nonce + */ +- (void)testNonceInitializer { + OIDServiceConfiguration *configuration = [OIDServiceConfigurationTests testInstance]; + OIDAuthorizationRequest *request = + [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:kTestClientID + scopes:@[] + redirectURL:[NSURL URLWithString:kTestRedirectURL] + responseType:OIDResponseTypeCode + nonce:kTestNonce + additionalParameters:nil]; + + XCTAssertEqualObjects(request.nonce, kTestNonce); + XCTAssertEqualObjects(request.responseType, @"code"); + XCTAssertEqualObjects(request.scope, @""); + XCTAssertEqualObjects(request.clientID, kTestClientID); + XCTAssertNil(request.clientSecret); + XCTAssertEqualObjects(request.redirectURL, [NSURL URLWithString:kTestRedirectURL]); + XCTAssertEqualObjects(@(request.additionalParameters.count), @0); +} + - (void)testScopeInitializerWithManyScopesAndClientSecret { NSDictionary *additionalParameters = @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; From aea7b8acd8b3fc261b8a42c998de33d76851f30b Mon Sep 17 00:00:00 2001 From: GravityByte Date: Wed, 25 Oct 2023 19:45:50 +0200 Subject: [PATCH 180/204] fixed wrong variable for additional http headers in OIDTokenRequest (#770) (#798) --- Source/AppAuthCore/OIDTokenRequest.m | 2 +- UnitTests/OIDTokenRequestTests.m | 53 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/Source/AppAuthCore/OIDTokenRequest.m b/Source/AppAuthCore/OIDTokenRequest.m index 08b0dafec..e09e5577d 100644 --- a/Source/AppAuthCore/OIDTokenRequest.m +++ b/Source/AppAuthCore/OIDTokenRequest.m @@ -330,7 +330,7 @@ - (NSURLRequest *)URLRequest { } for (id header in _additionalHeaders) { - [URLRequest setValue:httpHeaders[header] forHTTPHeaderField:header]; + [URLRequest setValue:_additionalHeaders[header] forHTTPHeaderField:header]; } return URLRequest; diff --git a/UnitTests/OIDTokenRequestTests.m b/UnitTests/OIDTokenRequestTests.m index 2aa865238..20ba691ca 100644 --- a/UnitTests/OIDTokenRequestTests.m +++ b/UnitTests/OIDTokenRequestTests.m @@ -56,6 +56,14 @@ */ static NSString *const kTestAdditionalHeaderValue = @"2"; +/*! @brief Test key for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderKey2 = @"C"; + +/*! @brief Test value for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderValue2 = @"3"; + @implementation OIDTokenRequestTests + (OIDTokenRequest *)testInstance { @@ -154,6 +162,32 @@ + (OIDTokenRequest *)testInstanceRefresh { return request; } ++ (OIDTokenRequest *)testInstanceAdditionalHeaders { + OIDAuthorizationResponse *authResponse = [OIDAuthorizationResponseTests testInstance]; + NSArray *scopesArray = + [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; + NSDictionary *additionalParameters = + @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + NSDictionary *additionalHeaders = @{ + kTestAdditionalHeaderKey : kTestAdditionalHeaderValue, + kTestAdditionalHeaderKey2 : kTestAdditionalHeaderValue2 + }; + + OIDTokenRequest *request = + [[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration + grantType:OIDGrantTypeAuthorizationCode + authorizationCode:authResponse.authorizationCode + redirectURL:authResponse.request.redirectURL + clientID:authResponse.request.clientID + clientSecret:authResponse.request.clientSecret + scopes:scopesArray + refreshToken:kRefreshTokenTestValue + codeVerifier:authResponse.request.codeVerifier + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; + return request; +} + /*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying process and checking to make sure the source and destination instances are equivalent. */ @@ -239,6 +273,10 @@ - (void)testSecureCoding { XCTAssertNotNil(request.additionalHeaders, @""); XCTAssertEqualObjects(request.additionalHeaders[kTestAdditionalHeaderKey], kTestAdditionalHeaderValue, @""); + + NSURLRequest *urlRequest = [request URLRequest]; + XCTAssertEqualObjects([urlRequest.allHTTPHeaderFields objectForKey:kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue); NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; OIDTokenRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; @@ -263,6 +301,10 @@ - (void)testSecureCoding { XCTAssertNotNil(requestCopy.additionalHeaders, @""); XCTAssertEqualObjects(requestCopy.additionalHeaders[kTestAdditionalHeaderKey], kTestAdditionalHeaderValue, @""); + + NSURLRequest *urlrequestCopy = [requestCopy URLRequest]; + XCTAssertEqualObjects([urlrequestCopy.allHTTPHeaderFields objectForKey:kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue); } - (void)testURLRequestNoClientAuth { @@ -302,6 +344,17 @@ - (void)testAuthorizationCodeNullRedirectURL { additionalHeaders:additionalHeaders], @""); } +- (void)testThatAdditionalHeadersAreInTokenRequest { + OIDTokenRequest *request = [[self class] testInstanceAdditionalHeaders]; + NSURLRequest* urlRequest = [request URLRequest]; + + XCTAssertEqualObjects([urlRequest.allHTTPHeaderFields objectForKey:kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue); + + XCTAssertEqualObjects([urlRequest.allHTTPHeaderFields objectForKey:kTestAdditionalHeaderKey2], + kTestAdditionalHeaderValue2); +} + @end #pragma GCC diagnostic pop From 3dc3e93eb165d2a1e18c4bc7e43864f5c9f96f14 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Wed, 6 Mar 2024 12:47:12 -0800 Subject: [PATCH 181/204] Add privacy manifest (#822) --- AppAuth.podspec | 13 ++++- AppAuth.xcodeproj/project.pbxproj | 14 ++++++ .../project.pbxproj | 48 +++++++++++++------ Examples/Example-iOS_ObjC/Podfile | 4 +- .../Source/AppAuthExampleViewController.m | 7 +-- .../Example-macOS.xcodeproj/project.pbxproj | 32 ++++++------- Examples/Example-macOS/Podfile | 2 +- .../Example-tvOS.xcodeproj/project.pbxproj | 26 ++++++++-- .../AppAuthTVExampleViewController.m | 5 +- Examples/Example-tvOS/Podfile | 2 + Package.swift | 5 +- PrivacyInfo.xcprivacy | 18 +++++++ 12 files changed, 129 insertions(+), 47 deletions(-) create mode 100644 PrivacyInfo.xcprivacy diff --git a/AppAuth.podspec b/AppAuth.podspec index b175e6282..325690783 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -44,13 +44,19 @@ It follows the OAuth 2.0 for Native Apps best current practice # Subspec for the core AppAuth library classes only, suitable for extensions. s.subspec 'Core' do |core| core.source_files = "Source/AppAuthCore.h", "Source/AppAuthCore/*.{h,m}" + core.resource_bundles = { + "AppAuthCore_Privacy" => ["PrivacyInfo.xcprivacy"] + } end - # Subspec for the full AppAuth library, including platform-dependant external user agents. + # Subspec for the full AppAuth library, including platform-dependent external user agents. s.subspec 'ExternalUserAgent' do |externalUserAgent| externalUserAgent.dependency 'AppAuth/Core' externalUserAgent.source_files = "Source/AppAuth.h", "Source/AppAuth/*.{h,m}" + externalUserAgent.resource_bundles = { + "AppAuthExternalUserAgent_Privacy" => ["PrivacyInfo.xcprivacy"] + } # iOS externalUserAgent.ios.source_files = "Source/AppAuth/iOS/**/*.{h,m}" @@ -64,10 +70,13 @@ It follows the OAuth 2.0 for Native Apps best current practice externalUserAgent.osx.weak_frameworks = "AuthenticationServices" end - # Subspec for the full AppAuth library, including platform-dependant external user agents. + # Subspec for the full AppAuth library, including platform-dependent external user agents. s.subspec 'TV' do |tv| tv.source_files = "Source/AppAuthTV.h", "Source/AppAuthTV/*.{h,m}" tv.dependency 'AppAuth/Core' + tv.resource_bundles = { + "AppAuthTV" => ["PrivacyInfo.xcprivacy"] + } end s.default_subspecs = 'Core', 'ExternalUserAgent' diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 08e6f28a1..1f4a2a876 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -546,6 +546,12 @@ 60140F801DE4344200DA0DC3 /* OIDRegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */; }; 60140F831DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F821DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m */; }; 60140F861DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */; }; + 73F574342B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */; }; + 73F574352B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */; }; + 73F574362B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */; }; + 73F574372B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */; }; + 73F574382B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */; }; + 73F574392B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */; }; A5EEF29720D821120044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; A5EEF29820D8211A0044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; A5EEF29920D8211B0044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; @@ -827,6 +833,7 @@ 60140F821DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRegistrationRequestTests.m; sourceTree = ""; }; 60140F841DE43C8C00DA0DC3 /* OIDRegistrationResponseTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRegistrationResponseTests.h; sourceTree = ""; }; 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRegistrationResponseTests.m; sourceTree = ""; }; + 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDTokenUtilitiesTests.m; sourceTree = ""; }; A6CEB1172007E384009D492A /* OIDEndSessionRequestTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionRequestTests.h; sourceTree = ""; }; A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionRequestTests.m; sourceTree = ""; }; @@ -1048,6 +1055,7 @@ 340E73731C5D819B0076B1F6 = { isa = PBXGroup; children = ( + 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */, 341742291C5D84D0000EF209 /* Frameworks */, 341741FB1C5D82D3000EF209 /* UnitTests */, 341741AE1C5D8243000EF209 /* Source */, @@ -1937,6 +1945,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73F574392B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1965,6 +1974,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73F574382B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1972,6 +1982,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73F574342B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1986,6 +1997,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73F574352B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1993,6 +2005,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73F574362B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2007,6 +2020,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73F574372B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj b/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj index eb38b576d..7f3f2de68 100644 --- a/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC.xcodeproj/project.pbxproj @@ -19,8 +19,8 @@ 346E91991C2A245000D3620B /* SafariServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 346E91981C2A245000D3620B /* SafariServices.framework */; }; 34CB09BD1C42007600A54261 /* AppAuthExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CB09BB1C42007600A54261 /* AppAuthExampleViewController.m */; }; 34CB09BE1C42007600A54261 /* AppAuthExampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34CB09BC1C42007600A54261 /* AppAuthExampleViewController.xib */; }; - D0B5FA33D74A6F7924A47471 /* libPods-Example-iOS_ObjC_Extension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 715EDB7EC4271193E47615ED /* libPods-Example-iOS_ObjC_Extension.a */; }; - E46F8589CE9E5DDFA69D835B /* libPods-Example-iOS_ObjC.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B61A918CBE7B8142CD7D8B3 /* libPods-Example-iOS_ObjC.a */; }; + BE28448F8FD76A9C12A30150 /* Pods_Example_iOS_ObjC_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CC004951DF03519C52500EC /* Pods_Example_iOS_ObjC_Extension.framework */; }; + C46B36D00F282572B5747D71 /* Pods_Example_iOS_ObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D84745AE558B974A2E64F7 /* Pods_Example_iOS_ObjC.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -81,9 +81,9 @@ 34CB09BB1C42007600A54261 /* AppAuthExampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppAuthExampleViewController.m; sourceTree = ""; }; 34CB09BC1C42007600A54261 /* AppAuthExampleViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AppAuthExampleViewController.xib; sourceTree = ""; }; 3A18DF082AC2FA0980C9DE57 /* Pods-Example-iOS_ObjC_Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS_ObjC_Extension.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example-iOS_ObjC_Extension/Pods-Example-iOS_ObjC_Extension.release.xcconfig"; sourceTree = ""; }; - 715EDB7EC4271193E47615ED /* libPods-Example-iOS_ObjC_Extension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Example-iOS_ObjC_Extension.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3CC004951DF03519C52500EC /* Pods_Example_iOS_ObjC_Extension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_iOS_ObjC_Extension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 43D84745AE558B974A2E64F7 /* Pods_Example_iOS_ObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_iOS_ObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 86C7660572AE6FF7A4D1592A /* Pods-Example-iOS_ObjC_Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS_ObjC_Extension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Example-iOS_ObjC_Extension/Pods-Example-iOS_ObjC_Extension.debug.xcconfig"; sourceTree = ""; }; - 8B61A918CBE7B8142CD7D8B3 /* libPods-Example-iOS_ObjC.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Example-iOS_ObjC.a"; sourceTree = BUILT_PRODUCTS_DIR; }; C4C31DB4A4928F246AA03805 /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = ""; }; D9867DC6FA9089CD613D4728 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = ""; }; ECBCCC4A1A779C83C72044F2 /* Pods-Example-iOS_ObjC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS_ObjC.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example-iOS_ObjC/Pods-Example-iOS_ObjC.release.xcconfig"; sourceTree = ""; }; @@ -95,7 +95,7 @@ buildActionMask = 2147483647; files = ( 06D4812F2055C3D400D9DC32 /* NotificationCenter.framework in Frameworks */, - D0B5FA33D74A6F7924A47471 /* libPods-Example-iOS_ObjC_Extension.a in Frameworks */, + BE28448F8FD76A9C12A30150 /* Pods_Example_iOS_ObjC_Extension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -111,7 +111,7 @@ buildActionMask = 2147483647; files = ( 346E91991C2A245000D3620B /* SafariServices.framework in Frameworks */, - E46F8589CE9E5DDFA69D835B /* libPods-Example-iOS_ObjC.a in Frameworks */, + C46B36D00F282572B5747D71 /* Pods_Example_iOS_ObjC.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -145,9 +145,9 @@ 3474C8DD1DFCB08E00F22B34 /* libAppAuth-iOS.a */, 346E91981C2A245000D3620B /* SafariServices.framework */, 09795EAF079B07A1781675D9 /* libPods-Example.a */, - 8B61A918CBE7B8142CD7D8B3 /* libPods-Example-iOS_ObjC.a */, 06D4812E2055C3D400D9DC32 /* NotificationCenter.framework */, - 715EDB7EC4271193E47615ED /* libPods-Example-iOS_ObjC_Extension.a */, + 43D84745AE558B974A2E64F7 /* Pods_Example_iOS_ObjC.framework */, + 3CC004951DF03519C52500EC /* Pods_Example_iOS_ObjC_Extension.framework */, ); name = Frameworks; sourceTree = ""; @@ -252,6 +252,7 @@ 346E91661C29D42800D3620B /* Frameworks */, 346E91671C29D42800D3620B /* Resources */, 06D4813E2055C3D400D9DC32 /* Embed App Extensions */, + 0B7CB92A32140E833CA8FF89 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -301,6 +302,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -345,6 +347,24 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 0B7CB92A32140E833CA8FF89 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Example-iOS_ObjC/Pods-Example-iOS_ObjC-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppAuth.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-iOS_ObjC/Pods-Example-iOS_ObjC-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 836219F293F319703A16E68D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -468,7 +488,7 @@ DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "Example-iOS_ObjC_Extension/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-iOS-ObjC-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -500,7 +520,7 @@ DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "Example-iOS_ObjC_Extension/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example.Example-iOS-ObjC-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -520,7 +540,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Tests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthExampleTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -539,7 +559,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Tests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthExampleTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -642,7 +662,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Source/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauth.Example; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -658,7 +678,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Source/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauth.Example; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Examples/Example-iOS_ObjC/Podfile b/Examples/Example-iOS_ObjC/Podfile index a4354d010..600de7d10 100644 --- a/Examples/Example-iOS_ObjC/Podfile +++ b/Examples/Example-iOS_ObjC/Podfile @@ -1,4 +1,6 @@ -platform :ios, '9.0' +platform :ios, '11.0' + +use_frameworks! target 'Example-iOS_ObjC' do # AppAuth Pod diff --git a/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m b/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m index d67c7b73d..a4accb51b 100644 --- a/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m +++ b/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m @@ -179,9 +179,10 @@ - (void)doClientRegistration:(OIDServiceConfiguration *)configuration grantTypes:nil subjectType:nil tokenEndpointAuthMethod:@"client_secret_post" - additionalParameters:nil - additionalHeaders:nil]; - // performs registration request + initialAccessToken:nil + additionalParameters:nil]; + + // performs registration request [self logMessage:@"Initiating registration request"]; [OIDAuthorizationService performRegistrationRequest:request diff --git a/Examples/Example-macOS/Example-macOS.xcodeproj/project.pbxproj b/Examples/Example-macOS/Example-macOS.xcodeproj/project.pbxproj index 8990ba02e..13bb1c957 100644 --- a/Examples/Example-macOS/Example-macOS.xcodeproj/project.pbxproj +++ b/Examples/Example-macOS/Example-macOS.xcodeproj/project.pbxproj @@ -156,7 +156,6 @@ F6016E6C1D2AC11F003497D7 /* Sources */, F6016E6D1D2AC11F003497D7 /* Frameworks */, F6016E6E1D2AC11F003497D7 /* Resources */, - 52872C7E76CB3EA69F4392F7 /* [CP] Embed Pods Frameworks */, 1C5B2EF60536044DE119E500 /* [CP] Copy Pods Resources */, ); buildRules = ( @@ -192,6 +191,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -233,28 +233,18 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Example-macOS/Pods-Example-macOS-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/AppAuth/AppAuthCore_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/AppAuth/AppAuthExternalUserAgent_Privacy.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AppAuthCore_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AppAuthExternalUserAgent_Privacy.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example-macOS/Pods-Example-macOS-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 52872C7E76CB3EA69F4392F7 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example-macOS/Pods-Example-macOS-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-macOS/Pods-Example-macOS-resources.sh\"\n"; showEnvVarsInLog = 0; }; C9395BB900D0A44A3F7EB0BE /* [CP] Check Pods Manifest.lock */ = { @@ -263,13 +253,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Example-macOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -446,6 +439,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example-macOS"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -459,6 +453,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.appauth.Example-macOS"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -474,6 +469,7 @@ 341AA4C81E7F2E5000FCA5C6 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; F6016E6B1D2AC11E003497D7 /* Build configuration list for PBXProject "Example-macOS" */ = { isa = XCConfigurationList; diff --git a/Examples/Example-macOS/Podfile b/Examples/Example-macOS/Podfile index 5b8ce01ab..e0c0aced9 100644 --- a/Examples/Example-macOS/Podfile +++ b/Examples/Example-macOS/Podfile @@ -1,5 +1,5 @@ target 'Example-macOS' do - platform :osx, '10.10' + platform :osx, '10.12' # AppAuth Pod # In production, just use `pod 'AppAuth'` without the path reference. diff --git a/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.pbxproj b/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.pbxproj index c70129f75..b2e8a7d1a 100644 --- a/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.pbxproj +++ b/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.pbxproj @@ -13,7 +13,7 @@ 2D91B7B0248EA17D0005B197 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2D91B7AF248EA17D0005B197 /* Assets.xcassets */; }; 2D91B7B3248EA17D0005B197 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2D91B7B1248EA17D0005B197 /* LaunchScreen.storyboard */; }; 2D91B7B6248EA17E0005B197 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D91B7B5248EA17E0005B197 /* main.m */; }; - 424A8D8BA4EB0A6C9FF4326A /* libPods-Example-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EEBF9489D390F9D0913AE178 /* libPods-Example-tvOS.a */; }; + FEFBCAF105C70E934E5A4975 /* Pods_Example_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F58A1FCA981D60A0EA8A3E5A /* Pods_Example_tvOS.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -29,7 +29,7 @@ 2D91B7B4248EA17D0005B197 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2D91B7B5248EA17E0005B197 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; D33A601E3AA7C8647C857C08 /* Pods-Example-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS.debug.xcconfig"; sourceTree = ""; }; - EEBF9489D390F9D0913AE178 /* libPods-Example-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Example-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + F58A1FCA981D60A0EA8A3E5A /* Pods_Example_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -37,7 +37,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 424A8D8BA4EB0A6C9FF4326A /* libPods-Example-tvOS.a in Frameworks */, + FEFBCAF105C70E934E5A4975 /* Pods_Example_tvOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -81,7 +81,7 @@ 9CCA3BB0D6EA112455518E2C /* Frameworks */ = { isa = PBXGroup; children = ( - EEBF9489D390F9D0913AE178 /* libPods-Example-tvOS.a */, + F58A1FCA981D60A0EA8A3E5A /* Pods_Example_tvOS.framework */, ); name = Frameworks; sourceTree = ""; @@ -106,6 +106,7 @@ 2D91B79F248EA17C0005B197 /* Sources */, 2D91B7A0248EA17C0005B197 /* Frameworks */, 2D91B7A1248EA17C0005B197 /* Resources */, + C86108A8D245915451E09E53 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -184,6 +185,23 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + C86108A8D245915451E09E53 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m index e97b0d204..1cdfb5ccf 100644 --- a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m +++ b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m @@ -175,9 +175,8 @@ - (void)performAuthorizationWithConfiguration:(OIDTVServiceConfiguration *)confi [[OIDTVAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kClientID clientSecret:kClientSecret - scopes:@[ OIDScopeOpenID, OIDScopeProfile ] - additionalParameters:nil - additionalHeaders:nil]; + scopes:@[ OIDScopeOpenID, OIDScopeProfile ] + additionalParameters:nil]; OIDTVAuthorizationInitialization initBlock = ^(OIDTVAuthorizationResponse *_Nullable response, NSError *_Nullable error) { diff --git a/Examples/Example-tvOS/Podfile b/Examples/Example-tvOS/Podfile index 979341635..3e7ac1135 100644 --- a/Examples/Example-tvOS/Podfile +++ b/Examples/Example-tvOS/Podfile @@ -1,5 +1,7 @@ platform :tvos, '9.0' +use_frameworks! + target 'Example-tvOS' do # AppAuth Pod, TV subspec # In production, just use `pod 'AppAuth/TV'` without the path reference. diff --git a/Package.swift b/Package.swift index ae72258ac..820e74a6e 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.1 +// swift-tools-version:5.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -44,6 +44,7 @@ let package = Package( .target( name: "AppAuthCore", path: "Source/AppAuthCore", + resources: [.copy("../../PrivacyInfo.xcprivacy")], publicHeadersPath: "" ), .target( @@ -51,6 +52,7 @@ let package = Package( dependencies: ["AppAuthCore"], path: "Source/AppAuth", sources: ["iOS", "macOS"], + resources: [.copy("../../PrivacyInfo.xcprivacy")], publicHeadersPath: "", cSettings: [ .headerSearchPath("iOS"), @@ -62,6 +64,7 @@ let package = Package( name: "AppAuthTV", dependencies: ["AppAuthCore"], path: "Source/AppAuthTV", + resources: [.copy("../../PrivacyInfo.xcprivacy")], publicHeadersPath: "" ), .testTarget( diff --git a/PrivacyInfo.xcprivacy b/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..33b390120 --- /dev/null +++ b/PrivacyInfo.xcprivacy @@ -0,0 +1,18 @@ + + + + + NSPrivacyAccessedAPITypes + + + + NSPrivacyCollectedDataTypes + + + + NSPrivacyTrackingDomains + + NSPrivacyTracking + + + From 4625d9d5e084958d2af9bebf7387da72dbf3385f Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 8 Mar 2024 13:02:13 -0800 Subject: [PATCH 182/204] Prepare for 1.7.0 release (#823) --- AppAuth.podspec | 2 +- CHANGELOG.md | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 325690783..c9efaffff 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.6.2" + s.version = "1.7.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index b682c8a8b..71c7e3bbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.7.0 +- Introduce addtional http headers to OIDTokenRequest ([#770](https://github.com/openid/AppAuth-iOS/pull/770)) +- Fix nullability annotation for -[OIDExternalUserAgentIOS init] ([#727](https://github.com/openid/AppAuth-iOS/pull/727)) +- Feat: allow custom nonce in OIDAuthorizationRequest ([#788](https://github.com/openid/AppAuth-iOS/pull/788)) +- Introduce addtional http headers to OIDTokenRequest ([#770](https://github.com/openid/AppAuth-iOS/pull/770)) +- Add privacy manifest ([#822](https://github.com/openid/AppAuth-iOS/pull/822)) + # 1.6.2 - Increased minimum iOS and macOS versions to 9.0 and 10.12 respectively to fix [framework build issue](https://github.com/openid/AppAuth-iOS/issues/765) From 4b6948f8a60b1ea72a179632cfef7ab99dc527db Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 8 Mar 2024 16:08:05 -0800 Subject: [PATCH 183/204] Add back missing method to OIDAuthorizationResponse (#825) --- .../xcschemes/AppAuth-iOS.xcscheme | 22 ++++++++----------- .../xcschemes/AppAuth-tvOS.xcscheme | 22 ++++++++----------- Source/AppAuthCore/OIDAuthorizationResponse.h | 11 ++++++++++ Source/AppAuthCore/OIDAuthorizationResponse.m | 6 +++++ 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-iOS.xcscheme b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-iOS.xcscheme index 5c03fe8e1..d2886e5ea 100644 --- a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-iOS.xcscheme +++ b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-iOS.xcscheme @@ -27,6 +27,15 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + @@ -39,17 +48,6 @@ - - - - - - - - + + + + @@ -39,17 +48,6 @@ - - - - - - - - *)additionalParameters; + +/*! @brief Creates a token request suitable for exchanging an authorization code for an access + token. + @param additionalParameters Additional parameters for the token request. + @param additionalHeaders Additional headers 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 *)additionalParameters additionalHeaders: diff --git a/Source/AppAuthCore/OIDAuthorizationResponse.m b/Source/AppAuthCore/OIDAuthorizationResponse.m index 5c998a966..957f81d3a 100644 --- a/Source/AppAuthCore/OIDAuthorizationResponse.m +++ b/Source/AppAuthCore/OIDAuthorizationResponse.m @@ -187,6 +187,12 @@ - (OIDTokenRequest *)tokenExchangeRequest { return [self tokenExchangeRequestWithAdditionalParameters:nil additionalHeaders:nil]; } +- (OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: + (NSDictionary *)additionalParameters { + return [self tokenExchangeRequestWithAdditionalParameters:additionalParameters + additionalHeaders:nil]; +} + - (OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: (NSDictionary *)additionalParameters additionalHeaders: From 2bd00a29745d3baaef75d918b48db045bcdc94d1 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 8 Mar 2024 17:08:03 -0800 Subject: [PATCH 184/204] Fix OIDTokenRequest for AppAuthCore and AppAuthTV (#826) --- .../xcschemes/AppAuth-macOS.xcscheme | 22 ++++----- Source/AppAuthCore/OIDTokenRequest.h | 49 +++++++++++++++++++ Source/AppAuthCore/OIDTokenRequest.m | 46 +++++++++++++++++ Source/AppAuthTV/OIDTVTokenRequest.h | 14 ++++++ Source/AppAuthTV/OIDTVTokenRequest.m | 13 +++++ 5 files changed, 131 insertions(+), 13 deletions(-) diff --git a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-macOS.xcscheme b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-macOS.xcscheme index aa261aca6..74b33a822 100644 --- a/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-macOS.xcscheme +++ b/AppAuth.xcodeproj/xcshareddata/xcschemes/AppAuth-macOS.xcscheme @@ -27,6 +27,15 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + @@ -39,17 +48,6 @@ - - - - - - - - *)scopes + refreshToken:(nullable NSString *)refreshToken + codeVerifier:(nullable NSString *)codeVerifier + additionalParameters:(nullable NSDictionary *)additionalParameters; + +/*! @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 *)additionalParameters; + /*! @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. diff --git a/Source/AppAuthCore/OIDTokenRequest.m b/Source/AppAuthCore/OIDTokenRequest.m index e09e5577d..6d30b6d6b 100644 --- a/Source/AppAuthCore/OIDTokenRequest.m +++ b/Source/AppAuthCore/OIDTokenRequest.m @@ -89,6 +89,52 @@ - (instancetype)init additionalHeaders:) ) +- (instancetype)initWithConfiguration:(nonnull OIDServiceConfiguration *)configuration + grantType:(nonnull NSString *)grantType + authorizationCode:(nullable NSString *)code + redirectURL:(nullable NSURL *)redirectURL + clientID:(nonnull NSString *)clientID + clientSecret:(nullable NSString *)clientSecret + scopes:(nullable NSArray *)scopes + refreshToken:(nullable NSString *)refreshToken + codeVerifier:(nullable NSString *)codeVerifier + additionalParameters:(nullable NSDictionary *)additionalParameters { + return [self initWithConfiguration:configuration + grantType:grantType + authorizationCode:code + redirectURL:redirectURL + clientID:clientID + clientSecret:clientSecret + scopes:scopes + refreshToken:refreshToken + codeVerifier:codeVerifier + additionalParameters:additionalParameters + additionalHeaders:_additionalHeaders]; +} + +- (instancetype)initWithConfiguration:(nonnull OIDServiceConfiguration *)configuration + grantType:(nonnull NSString *)grantType + authorizationCode:(nullable NSString *)code + redirectURL:(nullable NSURL *)redirectURL + clientID:(nonnull NSString *)clientID + clientSecret:(nullable NSString *)clientSecret + scope:(nullable NSString *)scope + refreshToken:(nullable NSString *)refreshToken + codeVerifier:(nullable NSString *)codeVerifier + additionalParameters:(nullable NSDictionary *)additionalParameters { + return [self initWithConfiguration:configuration + grantType:grantType + authorizationCode:code + redirectURL:redirectURL + clientID:clientID + clientSecret:clientSecret + scope:scope + refreshToken:refreshToken + codeVerifier:codeVerifier + additionalParameters:additionalParameters + additionalHeaders:_additionalHeaders]; +} + - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration grantType:(NSString *)grantType authorizationCode:(nullable NSString *)code diff --git a/Source/AppAuthTV/OIDTVTokenRequest.h b/Source/AppAuthTV/OIDTVTokenRequest.h index 021dc9b9d..8643b4cc5 100644 --- a/Source/AppAuthTV/OIDTVTokenRequest.h +++ b/Source/AppAuthTV/OIDTVTokenRequest.h @@ -80,6 +80,20 @@ NS_ASSUME_NONNULL_BEGIN (nullable NSDictionary *)additionalHeaders NS_UNAVAILABLE; +/*! @brief Designated initializer. + @param configuration The service's configuration. + @param deviceCode The device verification code received from the authorization server. + @param clientID The client identifier. + @param clientSecret The client secret (nullable). + @param additionalParameters The client's additional token request parameters. +*/ +- (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration + deviceCode:(NSString *)deviceCode + clientID:(NSString *)clientID + clientSecret:(nullable NSString *)clientSecret + additionalParameters: + (nullable NSDictionary *)additionalParameters; + /*! @brief Designated initializer. @param configuration The service's configuration. @param deviceCode The device verification code received from the authorization server. diff --git a/Source/AppAuthTV/OIDTVTokenRequest.m b/Source/AppAuthTV/OIDTVTokenRequest.m index ed5e4d3f2..0878c7934 100644 --- a/Source/AppAuthTV/OIDTVTokenRequest.m +++ b/Source/AppAuthTV/OIDTVTokenRequest.m @@ -90,6 +90,19 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration additionalHeaders: )) +- (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration + deviceCode:(NSString *)deviceCode + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret + additionalParameters:(NSDictionary *)additionalParameters { + return [self initWithConfiguration:configuration + deviceCode:deviceCode + clientID:clientID + clientSecret:clientSecret + additionalParameters:additionalParameters + additionalHeaders:nil]; +} + - (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration deviceCode:(NSString *)deviceCode clientID:(NSString *)clientID From 059f77eb1ccf015a6f5b88cb450376dc0707e1b6 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 8 Mar 2024 17:33:53 -0800 Subject: [PATCH 185/204] Prep for patch release fixing broken API change (#827) --- AppAuth.podspec | 2 +- CHANGELOG.md | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index c9efaffff..5df6b76fe 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.7.0" + s.version = "1.7.1" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index 71c7e3bbc..a2f0e0fca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,11 @@ +# 1.7.1 +- Add back missing method to OIDAuthorizationResponse ([#825](https://github.com/openid/AppAuth-iOS/pull/825)) +- Fix OIDTokenRequest for AppAuthCore and AppAuthTV ([#826](https://github.com/openid/AppAuth-iOS/pull/826)) + # 1.7.0 - Introduce addtional http headers to OIDTokenRequest ([#770](https://github.com/openid/AppAuth-iOS/pull/770)) - Fix nullability annotation for -[OIDExternalUserAgentIOS init] ([#727](https://github.com/openid/AppAuth-iOS/pull/727)) - Feat: allow custom nonce in OIDAuthorizationRequest ([#788](https://github.com/openid/AppAuth-iOS/pull/788)) -- Introduce addtional http headers to OIDTokenRequest ([#770](https://github.com/openid/AppAuth-iOS/pull/770)) - Add privacy manifest ([#822](https://github.com/openid/AppAuth-iOS/pull/822)) # 1.6.2 From 97b19039d34d07e313fd7f68317c81d501003722 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Mon, 11 Mar 2024 10:46:37 -0700 Subject: [PATCH 186/204] Streamline copying of privacy manifest (#830) --- AppAuth.podspec | 6 ------ PrivacyInfo.xcprivacy | 2 -- 2 files changed, 8 deletions(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 5df6b76fe..27f2fac3a 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -54,9 +54,6 @@ It follows the OAuth 2.0 for Native Apps best current practice externalUserAgent.dependency 'AppAuth/Core' externalUserAgent.source_files = "Source/AppAuth.h", "Source/AppAuth/*.{h,m}" - externalUserAgent.resource_bundles = { - "AppAuthExternalUserAgent_Privacy" => ["PrivacyInfo.xcprivacy"] - } # iOS externalUserAgent.ios.source_files = "Source/AppAuth/iOS/**/*.{h,m}" @@ -74,9 +71,6 @@ It follows the OAuth 2.0 for Native Apps best current practice s.subspec 'TV' do |tv| tv.source_files = "Source/AppAuthTV.h", "Source/AppAuthTV/*.{h,m}" tv.dependency 'AppAuth/Core' - tv.resource_bundles = { - "AppAuthTV" => ["PrivacyInfo.xcprivacy"] - } end s.default_subspecs = 'Core', 'ExternalUserAgent' diff --git a/PrivacyInfo.xcprivacy b/PrivacyInfo.xcprivacy index 33b390120..cc6746dcb 100644 --- a/PrivacyInfo.xcprivacy +++ b/PrivacyInfo.xcprivacy @@ -4,11 +4,9 @@ NSPrivacyAccessedAPITypes - NSPrivacyCollectedDataTypes - NSPrivacyTrackingDomains From 269c90515328c24f90b2ed17a67c8a796b485448 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Mon, 11 Mar 2024 11:16:35 -0700 Subject: [PATCH 187/204] Prep patch release fixing errors in privacy reports (#831) --- AppAuth.podspec | 2 +- CHANGELOG.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 27f2fac3a..a328e6be5 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.7.1" + s.version = "1.7.2" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index a2f0e0fca..12e915e1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.7.2 + - Streamline copying of privacy manifest ([#830](https://github.com/openid/AppAuth-iOS/pull/830)) + # 1.7.1 - Add back missing method to OIDAuthorizationResponse ([#825](https://github.com/openid/AppAuth-iOS/pull/825)) - Fix OIDTokenRequest for AppAuthCore and AppAuthTV ([#826](https://github.com/openid/AppAuth-iOS/pull/826)) From 5563d068aac2bbcadd081a1e26e9cf0dbe8af8ba Mon Sep 17 00:00:00 2001 From: mdmathias Date: Wed, 13 Mar 2024 13:26:42 -0700 Subject: [PATCH 188/204] Fix missing manifest in bundle using SPM (#833) --- AppAuth.podspec | 12 ++-- AppAuth.xcodeproj/project.pbxproj | 56 +++++++++++++------ Package.swift | 12 ++-- {Source => Sources}/AppAuth.h | 0 .../AppAuth/Resources/PrivacyInfo.xcprivacy | 0 .../AppAuth/iOS/OIDAuthState+IOS.h | 0 .../AppAuth/iOS/OIDAuthState+IOS.m | 0 .../AppAuth/iOS/OIDAuthorizationService+IOS.h | 0 .../AppAuth/iOS/OIDAuthorizationService+IOS.m | 0 .../iOS/OIDExternalUserAgentCatalyst.h | 0 .../iOS/OIDExternalUserAgentCatalyst.m | 0 .../AppAuth/iOS/OIDExternalUserAgentIOS.h | 0 .../AppAuth/iOS/OIDExternalUserAgentIOS.m | 0 .../OIDExternalUserAgentIOSCustomBrowser.h | 0 .../OIDExternalUserAgentIOSCustomBrowser.m | 0 .../OIDLoopbackHTTPServer.h | 0 .../OIDLoopbackHTTPServer.m | 0 .../AppAuth/macOS/OIDAuthState+Mac.h | 0 .../AppAuth/macOS/OIDAuthState+Mac.m | 0 .../macOS/OIDAuthorizationService+Mac.h | 0 .../macOS/OIDAuthorizationService+Mac.m | 0 .../AppAuth/macOS/OIDExternalUserAgentMac.h | 0 .../AppAuth/macOS/OIDExternalUserAgentMac.m | 0 .../AppAuth/macOS/OIDRedirectHTTPHandler.h | 0 .../AppAuth/macOS/OIDRedirectHTTPHandler.m | 0 {Source => Sources}/AppAuthCore.h | 0 .../AppAuthCore/OIDAuthState.h | 0 .../AppAuthCore/OIDAuthState.m | 0 .../AppAuthCore/OIDAuthStateChangeDelegate.h | 0 .../AppAuthCore/OIDAuthStateErrorDelegate.h | 0 .../AppAuthCore/OIDAuthorizationRequest.h | 0 .../AppAuthCore/OIDAuthorizationRequest.m | 0 .../AppAuthCore/OIDAuthorizationResponse.h | 0 .../AppAuthCore/OIDAuthorizationResponse.m | 0 .../AppAuthCore/OIDAuthorizationService.h | 0 .../AppAuthCore/OIDAuthorizationService.m | 0 .../AppAuthCore/OIDClientMetadataParameters.h | 0 .../AppAuthCore/OIDClientMetadataParameters.m | 0 {Source => Sources}/AppAuthCore/OIDDefines.h | 0 .../AppAuthCore/OIDEndSessionRequest.h | 0 .../AppAuthCore/OIDEndSessionRequest.m | 0 .../AppAuthCore/OIDEndSessionResponse.h | 0 .../AppAuthCore/OIDEndSessionResponse.m | 0 {Source => Sources}/AppAuthCore/OIDError.h | 0 {Source => Sources}/AppAuthCore/OIDError.m | 0 .../AppAuthCore/OIDErrorUtilities.h | 0 .../AppAuthCore/OIDErrorUtilities.m | 0 .../AppAuthCore/OIDExternalUserAgent.h | 0 .../AppAuthCore/OIDExternalUserAgentRequest.h | 0 .../AppAuthCore/OIDExternalUserAgentSession.h | 0 .../AppAuthCore/OIDFieldMapping.h | 0 .../AppAuthCore/OIDFieldMapping.m | 0 .../AppAuthCore/OIDGrantTypes.h | 0 .../AppAuthCore/OIDGrantTypes.m | 0 {Source => Sources}/AppAuthCore/OIDIDToken.h | 0 {Source => Sources}/AppAuthCore/OIDIDToken.m | 0 .../AppAuthCore/OIDRegistrationRequest.h | 0 .../AppAuthCore/OIDRegistrationRequest.m | 0 .../AppAuthCore/OIDRegistrationResponse.h | 0 .../AppAuthCore/OIDRegistrationResponse.m | 0 .../AppAuthCore/OIDResponseTypes.h | 0 .../AppAuthCore/OIDResponseTypes.m | 0 .../AppAuthCore/OIDScopeUtilities.h | 0 .../AppAuthCore/OIDScopeUtilities.m | 0 {Source => Sources}/AppAuthCore/OIDScopes.h | 0 {Source => Sources}/AppAuthCore/OIDScopes.m | 0 .../AppAuthCore/OIDServiceConfiguration.h | 0 .../AppAuthCore/OIDServiceConfiguration.m | 0 .../AppAuthCore/OIDServiceDiscovery.h | 0 .../AppAuthCore/OIDServiceDiscovery.m | 0 .../AppAuthCore/OIDTokenRequest.h | 0 .../AppAuthCore/OIDTokenRequest.m | 0 .../AppAuthCore/OIDTokenResponse.h | 0 .../AppAuthCore/OIDTokenResponse.m | 0 .../AppAuthCore/OIDTokenUtilities.h | 0 .../AppAuthCore/OIDTokenUtilities.m | 0 .../AppAuthCore/OIDURLQueryComponent.h | 0 .../AppAuthCore/OIDURLQueryComponent.m | 0 .../AppAuthCore/OIDURLSessionProvider.h | 0 .../AppAuthCore/OIDURLSessionProvider.m | 0 .../Resources/PrivacyInfo.xcprivacy | 16 ++++++ {Source => Sources}/AppAuthTV.h | 0 .../AppAuthTV/OIDTVAuthorizationRequest.h | 0 .../AppAuthTV/OIDTVAuthorizationRequest.m | 0 .../AppAuthTV/OIDTVAuthorizationResponse.h | 0 .../AppAuthTV/OIDTVAuthorizationResponse.m | 0 .../AppAuthTV/OIDTVAuthorizationService.h | 0 .../AppAuthTV/OIDTVAuthorizationService.m | 0 .../AppAuthTV/OIDTVServiceConfiguration.h | 0 .../AppAuthTV/OIDTVServiceConfiguration.m | 0 .../AppAuthTV/OIDTVTokenRequest.h | 0 .../AppAuthTV/OIDTVTokenRequest.m | 0 .../AppAuthTV/Resources/PrivacyInfo.xcprivacy | 16 ++++++ .../CoreFramework/AppAuthCore.h | 0 {Source => Sources}/CoreFramework/Info.plist | 0 {Source => Sources}/Framework/AppAuth.h | 0 {Source => Sources}/Framework/Info.plist | 0 {Source => Sources}/TVFramework/AppAuthTV.h | 0 {Source => Sources}/TVFramework/Info.plist | 0 .../OIDTVAuthorizationRequestTests.m | 8 +-- .../OIDTVAuthorizationResponseTests.m | 12 ++-- UnitTests/AppAuthTV/OIDTVTokenRequestTests.m | 10 ++-- UnitTests/OIDAuthStateTests.h | 4 +- UnitTests/OIDAuthStateTests.m | 10 ++-- UnitTests/OIDAuthorizationRequestTests.m | 6 +- UnitTests/OIDAuthorizationResponseTests.m | 6 +- UnitTests/OIDEndSessionRequestTests.m | 6 +- UnitTests/OIDGrantTypesTests.m | 2 +- UnitTests/OIDRegistrationRequestTests.m | 6 +- UnitTests/OIDRegistrationResponseTests.m | 4 +- UnitTests/OIDResponseTypesTests.m | 2 +- UnitTests/OIDScopesTests.m | 2 +- UnitTests/OIDServiceConfigurationTests.m | 8 +-- UnitTests/OIDServiceDiscoveryTests.m | 4 +- UnitTests/OIDTokenRequestTests.m | 10 ++-- UnitTests/OIDTokenResponseTests.m | 4 +- UnitTests/OIDTokenUtilitiesTests.m | 2 +- UnitTests/OIDURLQueryComponentTests.m | 2 +- UnitTests/OIDURLQueryComponentTestsIOS7.m | 2 +- 119 files changed, 139 insertions(+), 83 deletions(-) rename {Source => Sources}/AppAuth.h (100%) rename PrivacyInfo.xcprivacy => Sources/AppAuth/Resources/PrivacyInfo.xcprivacy (100%) rename {Source => Sources}/AppAuth/iOS/OIDAuthState+IOS.h (100%) rename {Source => Sources}/AppAuth/iOS/OIDAuthState+IOS.m (100%) rename {Source => Sources}/AppAuth/iOS/OIDAuthorizationService+IOS.h (100%) rename {Source => Sources}/AppAuth/iOS/OIDAuthorizationService+IOS.m (100%) rename {Source => Sources}/AppAuth/iOS/OIDExternalUserAgentCatalyst.h (100%) rename {Source => Sources}/AppAuth/iOS/OIDExternalUserAgentCatalyst.m (100%) rename {Source => Sources}/AppAuth/iOS/OIDExternalUserAgentIOS.h (100%) rename {Source => Sources}/AppAuth/iOS/OIDExternalUserAgentIOS.m (100%) rename {Source => Sources}/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h (100%) rename {Source => Sources}/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m (100%) rename {Source => Sources}/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h (100%) rename {Source => Sources}/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m (100%) rename {Source => Sources}/AppAuth/macOS/OIDAuthState+Mac.h (100%) rename {Source => Sources}/AppAuth/macOS/OIDAuthState+Mac.m (100%) rename {Source => Sources}/AppAuth/macOS/OIDAuthorizationService+Mac.h (100%) rename {Source => Sources}/AppAuth/macOS/OIDAuthorizationService+Mac.m (100%) rename {Source => Sources}/AppAuth/macOS/OIDExternalUserAgentMac.h (100%) rename {Source => Sources}/AppAuth/macOS/OIDExternalUserAgentMac.m (100%) rename {Source => Sources}/AppAuth/macOS/OIDRedirectHTTPHandler.h (100%) rename {Source => Sources}/AppAuth/macOS/OIDRedirectHTTPHandler.m (100%) rename {Source => Sources}/AppAuthCore.h (100%) rename {Source => Sources}/AppAuthCore/OIDAuthState.h (100%) rename {Source => Sources}/AppAuthCore/OIDAuthState.m (100%) rename {Source => Sources}/AppAuthCore/OIDAuthStateChangeDelegate.h (100%) rename {Source => Sources}/AppAuthCore/OIDAuthStateErrorDelegate.h (100%) rename {Source => Sources}/AppAuthCore/OIDAuthorizationRequest.h (100%) rename {Source => Sources}/AppAuthCore/OIDAuthorizationRequest.m (100%) rename {Source => Sources}/AppAuthCore/OIDAuthorizationResponse.h (100%) rename {Source => Sources}/AppAuthCore/OIDAuthorizationResponse.m (100%) rename {Source => Sources}/AppAuthCore/OIDAuthorizationService.h (100%) rename {Source => Sources}/AppAuthCore/OIDAuthorizationService.m (100%) rename {Source => Sources}/AppAuthCore/OIDClientMetadataParameters.h (100%) rename {Source => Sources}/AppAuthCore/OIDClientMetadataParameters.m (100%) rename {Source => Sources}/AppAuthCore/OIDDefines.h (100%) rename {Source => Sources}/AppAuthCore/OIDEndSessionRequest.h (100%) rename {Source => Sources}/AppAuthCore/OIDEndSessionRequest.m (100%) rename {Source => Sources}/AppAuthCore/OIDEndSessionResponse.h (100%) rename {Source => Sources}/AppAuthCore/OIDEndSessionResponse.m (100%) rename {Source => Sources}/AppAuthCore/OIDError.h (100%) rename {Source => Sources}/AppAuthCore/OIDError.m (100%) rename {Source => Sources}/AppAuthCore/OIDErrorUtilities.h (100%) rename {Source => Sources}/AppAuthCore/OIDErrorUtilities.m (100%) rename {Source => Sources}/AppAuthCore/OIDExternalUserAgent.h (100%) rename {Source => Sources}/AppAuthCore/OIDExternalUserAgentRequest.h (100%) rename {Source => Sources}/AppAuthCore/OIDExternalUserAgentSession.h (100%) rename {Source => Sources}/AppAuthCore/OIDFieldMapping.h (100%) rename {Source => Sources}/AppAuthCore/OIDFieldMapping.m (100%) rename {Source => Sources}/AppAuthCore/OIDGrantTypes.h (100%) rename {Source => Sources}/AppAuthCore/OIDGrantTypes.m (100%) rename {Source => Sources}/AppAuthCore/OIDIDToken.h (100%) rename {Source => Sources}/AppAuthCore/OIDIDToken.m (100%) rename {Source => Sources}/AppAuthCore/OIDRegistrationRequest.h (100%) rename {Source => Sources}/AppAuthCore/OIDRegistrationRequest.m (100%) rename {Source => Sources}/AppAuthCore/OIDRegistrationResponse.h (100%) rename {Source => Sources}/AppAuthCore/OIDRegistrationResponse.m (100%) rename {Source => Sources}/AppAuthCore/OIDResponseTypes.h (100%) rename {Source => Sources}/AppAuthCore/OIDResponseTypes.m (100%) rename {Source => Sources}/AppAuthCore/OIDScopeUtilities.h (100%) rename {Source => Sources}/AppAuthCore/OIDScopeUtilities.m (100%) rename {Source => Sources}/AppAuthCore/OIDScopes.h (100%) rename {Source => Sources}/AppAuthCore/OIDScopes.m (100%) rename {Source => Sources}/AppAuthCore/OIDServiceConfiguration.h (100%) rename {Source => Sources}/AppAuthCore/OIDServiceConfiguration.m (100%) rename {Source => Sources}/AppAuthCore/OIDServiceDiscovery.h (100%) rename {Source => Sources}/AppAuthCore/OIDServiceDiscovery.m (100%) rename {Source => Sources}/AppAuthCore/OIDTokenRequest.h (100%) rename {Source => Sources}/AppAuthCore/OIDTokenRequest.m (100%) rename {Source => Sources}/AppAuthCore/OIDTokenResponse.h (100%) rename {Source => Sources}/AppAuthCore/OIDTokenResponse.m (100%) rename {Source => Sources}/AppAuthCore/OIDTokenUtilities.h (100%) rename {Source => Sources}/AppAuthCore/OIDTokenUtilities.m (100%) rename {Source => Sources}/AppAuthCore/OIDURLQueryComponent.h (100%) rename {Source => Sources}/AppAuthCore/OIDURLQueryComponent.m (100%) rename {Source => Sources}/AppAuthCore/OIDURLSessionProvider.h (100%) rename {Source => Sources}/AppAuthCore/OIDURLSessionProvider.m (100%) create mode 100644 Sources/AppAuthCore/Resources/PrivacyInfo.xcprivacy rename {Source => Sources}/AppAuthTV.h (100%) rename {Source => Sources}/AppAuthTV/OIDTVAuthorizationRequest.h (100%) rename {Source => Sources}/AppAuthTV/OIDTVAuthorizationRequest.m (100%) rename {Source => Sources}/AppAuthTV/OIDTVAuthorizationResponse.h (100%) rename {Source => Sources}/AppAuthTV/OIDTVAuthorizationResponse.m (100%) rename {Source => Sources}/AppAuthTV/OIDTVAuthorizationService.h (100%) rename {Source => Sources}/AppAuthTV/OIDTVAuthorizationService.m (100%) rename {Source => Sources}/AppAuthTV/OIDTVServiceConfiguration.h (100%) rename {Source => Sources}/AppAuthTV/OIDTVServiceConfiguration.m (100%) rename {Source => Sources}/AppAuthTV/OIDTVTokenRequest.h (100%) rename {Source => Sources}/AppAuthTV/OIDTVTokenRequest.m (100%) create mode 100644 Sources/AppAuthTV/Resources/PrivacyInfo.xcprivacy rename {Source => Sources}/CoreFramework/AppAuthCore.h (100%) rename {Source => Sources}/CoreFramework/Info.plist (100%) rename {Source => Sources}/Framework/AppAuth.h (100%) rename {Source => Sources}/Framework/Info.plist (100%) rename {Source => Sources}/TVFramework/AppAuthTV.h (100%) rename {Source => Sources}/TVFramework/Info.plist (100%) diff --git a/AppAuth.podspec b/AppAuth.podspec index a328e6be5..70f3e4168 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -43,9 +43,9 @@ It follows the OAuth 2.0 for Native Apps best current practice # Subspec for the core AppAuth library classes only, suitable for extensions. s.subspec 'Core' do |core| - core.source_files = "Source/AppAuthCore.h", "Source/AppAuthCore/*.{h,m}" + core.source_files = "Sources/AppAuthCore.h", "Sources/AppAuthCore/*.{h,m}" core.resource_bundles = { - "AppAuthCore_Privacy" => ["PrivacyInfo.xcprivacy"] + "AppAuthCore_Privacy" => ["Sources/AppAuthCore/Resources/PrivacyInfo.xcprivacy"] } end @@ -53,23 +53,23 @@ It follows the OAuth 2.0 for Native Apps best current practice s.subspec 'ExternalUserAgent' do |externalUserAgent| externalUserAgent.dependency 'AppAuth/Core' - externalUserAgent.source_files = "Source/AppAuth.h", "Source/AppAuth/*.{h,m}" + externalUserAgent.source_files = "Sources/AppAuth.h", "Sources/AppAuth/*.{h,m}" # iOS - externalUserAgent.ios.source_files = "Source/AppAuth/iOS/**/*.{h,m}" + externalUserAgent.ios.source_files = "Sources/AppAuth/iOS/**/*.{h,m}" externalUserAgent.ios.deployment_target = ios_deployment_target externalUserAgent.ios.frameworks = "SafariServices" externalUserAgent.ios.weak_frameworks = "AuthenticationServices" # macOS - externalUserAgent.osx.source_files = "Source/AppAuth/macOS/**/*.{h,m}" + externalUserAgent.osx.source_files = "Sources/AppAuth/macOS/**/*.{h,m}" externalUserAgent.osx.deployment_target = osx_deployment_target externalUserAgent.osx.weak_frameworks = "AuthenticationServices" end # Subspec for the full AppAuth library, including platform-dependent external user agents. s.subspec 'TV' do |tv| - tv.source_files = "Source/AppAuthTV.h", "Source/AppAuthTV/*.{h,m}" + tv.source_files = "Sources/AppAuthTV.h", "Sources/AppAuthTV/*.{h,m}" tv.dependency 'AppAuth/Core' end diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 1f4a2a876..8d770d93c 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -1000,6 +1000,7 @@ 2D47AAD7249A86E30059B5A4 /* AppAuthTV */ = { isa = PBXGroup; children = ( + 7322C49B2BA20BFA00DF9B2F /* Resources */, 2D47AAD9249A87010059B5A4 /* OIDTVAuthorizationRequest.h */, 2D47AADD249A87010059B5A4 /* OIDTVAuthorizationRequest.m */, 2D47AADC249A87010059B5A4 /* OIDTVAuthorizationResponse.h */, @@ -1055,10 +1056,9 @@ 340E73731C5D819B0076B1F6 = { isa = PBXGroup; children = ( - 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */, 341742291C5D84D0000EF209 /* Frameworks */, 341741FB1C5D82D3000EF209 /* UnitTests */, - 341741AE1C5D8243000EF209 /* Source */, + 341741AE1C5D8243000EF209 /* Sources */, 340E737D1C5D819B0076B1F6 /* Products */, ); indentWidth = 2; @@ -1090,7 +1090,7 @@ name = Products; sourceTree = ""; }; - 341741AE1C5D8243000EF209 /* Source */ = { + 341741AE1C5D8243000EF209 /* Sources */ = { isa = PBXGroup; children = ( 348970992178F40600ABEED4 /* CoreFramework */, @@ -1103,7 +1103,7 @@ 3489709E21791B0C00ABEED4 /* AppAuthCore.h */, 2D47AADB249A87010059B5A4 /* AppAuthTV.h */, ); - path = Source; + path = Sources; sourceTree = ""; }; 341741FB1C5D82D3000EF209 /* UnitTests */ = { @@ -1184,9 +1184,32 @@ path = LoopbackHTTPServer; sourceTree = ""; }; + 7322C4992BA2095100DF9B2F /* Resources */ = { + isa = PBXGroup; + children = ( + 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */, + ); + path = Resources; + sourceTree = ""; + }; + 7322C49A2BA20BEF00DF9B2F /* Resources */ = { + isa = PBXGroup; + children = ( + ); + path = Resources; + sourceTree = ""; + }; + 7322C49B2BA20BFA00DF9B2F /* Resources */ = { + isa = PBXGroup; + children = ( + ); + path = Resources; + sourceTree = ""; + }; 8A9B9D5E24561EC40055353E /* AppAuthCore */ = { isa = PBXGroup; children = ( + 7322C4992BA2095100DF9B2F /* Resources */, 341741B41C5D8243000EF209 /* OIDAuthorizationRequest.h */, 341741B51C5D8243000EF209 /* OIDAuthorizationRequest.m */, 341741B61C5D8243000EF209 /* OIDAuthorizationResponse.h */, @@ -1248,6 +1271,7 @@ 8A9B9D632456227D0055353E /* AppAuth */ = { isa = PBXGroup; children = ( + 7322C49A2BA20BEF00DF9B2F /* Resources */, 340DAE241D581FE700EC285B /* macOS */, F6F60FAF1D2BFEF000325CB3 /* iOS */, ); @@ -2668,7 +2692,7 @@ "DEBUG=1", "$(inherited)", ); - INFOPLIST_FILE = "$(SRCROOT)/Source/TVFramework/Info.plist"; + INFOPLIST_FILE = Sources/TVFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; @@ -2700,7 +2724,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = "$(SRCROOT)/Source/TVFramework/Info.plist"; + INFOPLIST_FILE = Sources/TVFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_FAST_MATH = YES; @@ -3012,7 +3036,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/CoreFramework/Info.plist; + INFOPLIST_FILE = Sources/CoreFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -3038,7 +3062,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/CoreFramework/Info.plist; + INFOPLIST_FILE = Sources/CoreFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -3063,7 +3087,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/Framework/Info.plist; + INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -3088,7 +3112,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/Framework/Info.plist; + INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -3142,7 +3166,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/Framework/Info.plist; + INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-watchOS"; @@ -3168,7 +3192,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/Framework/Info.plist; + INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-watchOS"; @@ -3193,7 +3217,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/Framework/Info.plist; + INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-tvOS"; @@ -3218,7 +3242,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/Framework/Info.plist; + INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-tvOS"; @@ -3275,7 +3299,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; - INFOPLIST_FILE = Source/Framework/Info.plist; + INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-macOS"; @@ -3300,7 +3324,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; - INFOPLIST_FILE = Source/Framework/Info.plist; + INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-macOS"; diff --git a/Package.swift b/Package.swift index 820e74a6e..103c0f761 100644 --- a/Package.swift +++ b/Package.swift @@ -43,16 +43,16 @@ let package = Package( targets: [ .target( name: "AppAuthCore", - path: "Source/AppAuthCore", - resources: [.copy("../../PrivacyInfo.xcprivacy")], + path: "Sources/AppAuthCore", + resources: [.copy("Resources/PrivacyInfo.xcprivacy")], publicHeadersPath: "" ), .target( name: "AppAuth", dependencies: ["AppAuthCore"], - path: "Source/AppAuth", + path: "Sources/AppAuth", sources: ["iOS", "macOS"], - resources: [.copy("../../PrivacyInfo.xcprivacy")], + resources: [.copy("Resources/PrivacyInfo.xcprivacy")], publicHeadersPath: "", cSettings: [ .headerSearchPath("iOS"), @@ -63,8 +63,8 @@ let package = Package( .target( name: "AppAuthTV", dependencies: ["AppAuthCore"], - path: "Source/AppAuthTV", - resources: [.copy("../../PrivacyInfo.xcprivacy")], + path: "Sources/AppAuthTV", + resources: [.copy("Resources/PrivacyInfo.xcprivacy")], publicHeadersPath: "" ), .testTarget( diff --git a/Source/AppAuth.h b/Sources/AppAuth.h similarity index 100% rename from Source/AppAuth.h rename to Sources/AppAuth.h diff --git a/PrivacyInfo.xcprivacy b/Sources/AppAuth/Resources/PrivacyInfo.xcprivacy similarity index 100% rename from PrivacyInfo.xcprivacy rename to Sources/AppAuth/Resources/PrivacyInfo.xcprivacy diff --git a/Source/AppAuth/iOS/OIDAuthState+IOS.h b/Sources/AppAuth/iOS/OIDAuthState+IOS.h similarity index 100% rename from Source/AppAuth/iOS/OIDAuthState+IOS.h rename to Sources/AppAuth/iOS/OIDAuthState+IOS.h diff --git a/Source/AppAuth/iOS/OIDAuthState+IOS.m b/Sources/AppAuth/iOS/OIDAuthState+IOS.m similarity index 100% rename from Source/AppAuth/iOS/OIDAuthState+IOS.m rename to Sources/AppAuth/iOS/OIDAuthState+IOS.m diff --git a/Source/AppAuth/iOS/OIDAuthorizationService+IOS.h b/Sources/AppAuth/iOS/OIDAuthorizationService+IOS.h similarity index 100% rename from Source/AppAuth/iOS/OIDAuthorizationService+IOS.h rename to Sources/AppAuth/iOS/OIDAuthorizationService+IOS.h diff --git a/Source/AppAuth/iOS/OIDAuthorizationService+IOS.m b/Sources/AppAuth/iOS/OIDAuthorizationService+IOS.m similarity index 100% rename from Source/AppAuth/iOS/OIDAuthorizationService+IOS.m rename to Sources/AppAuth/iOS/OIDAuthorizationService+IOS.m diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h b/Sources/AppAuth/iOS/OIDExternalUserAgentCatalyst.h similarity index 100% rename from Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h rename to Sources/AppAuth/iOS/OIDExternalUserAgentCatalyst.h diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m b/Sources/AppAuth/iOS/OIDExternalUserAgentCatalyst.m similarity index 100% rename from Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m rename to Sources/AppAuth/iOS/OIDExternalUserAgentCatalyst.m diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h similarity index 100% rename from Source/AppAuth/iOS/OIDExternalUserAgentIOS.h rename to Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m similarity index 100% rename from Source/AppAuth/iOS/OIDExternalUserAgentIOS.m rename to Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h b/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h similarity index 100% rename from Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h rename to Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h diff --git a/Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m b/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m similarity index 100% rename from Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m rename to Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m diff --git a/Source/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h b/Sources/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h similarity index 100% rename from Source/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h rename to Sources/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.h diff --git a/Source/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m b/Sources/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m similarity index 100% rename from Source/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m rename to Sources/AppAuth/macOS/LoopbackHTTPServer/OIDLoopbackHTTPServer.m diff --git a/Source/AppAuth/macOS/OIDAuthState+Mac.h b/Sources/AppAuth/macOS/OIDAuthState+Mac.h similarity index 100% rename from Source/AppAuth/macOS/OIDAuthState+Mac.h rename to Sources/AppAuth/macOS/OIDAuthState+Mac.h diff --git a/Source/AppAuth/macOS/OIDAuthState+Mac.m b/Sources/AppAuth/macOS/OIDAuthState+Mac.m similarity index 100% rename from Source/AppAuth/macOS/OIDAuthState+Mac.m rename to Sources/AppAuth/macOS/OIDAuthState+Mac.m diff --git a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h b/Sources/AppAuth/macOS/OIDAuthorizationService+Mac.h similarity index 100% rename from Source/AppAuth/macOS/OIDAuthorizationService+Mac.h rename to Sources/AppAuth/macOS/OIDAuthorizationService+Mac.h diff --git a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m b/Sources/AppAuth/macOS/OIDAuthorizationService+Mac.m similarity index 100% rename from Source/AppAuth/macOS/OIDAuthorizationService+Mac.m rename to Sources/AppAuth/macOS/OIDAuthorizationService+Mac.m diff --git a/Source/AppAuth/macOS/OIDExternalUserAgentMac.h b/Sources/AppAuth/macOS/OIDExternalUserAgentMac.h similarity index 100% rename from Source/AppAuth/macOS/OIDExternalUserAgentMac.h rename to Sources/AppAuth/macOS/OIDExternalUserAgentMac.h diff --git a/Source/AppAuth/macOS/OIDExternalUserAgentMac.m b/Sources/AppAuth/macOS/OIDExternalUserAgentMac.m similarity index 100% rename from Source/AppAuth/macOS/OIDExternalUserAgentMac.m rename to Sources/AppAuth/macOS/OIDExternalUserAgentMac.m diff --git a/Source/AppAuth/macOS/OIDRedirectHTTPHandler.h b/Sources/AppAuth/macOS/OIDRedirectHTTPHandler.h similarity index 100% rename from Source/AppAuth/macOS/OIDRedirectHTTPHandler.h rename to Sources/AppAuth/macOS/OIDRedirectHTTPHandler.h diff --git a/Source/AppAuth/macOS/OIDRedirectHTTPHandler.m b/Sources/AppAuth/macOS/OIDRedirectHTTPHandler.m similarity index 100% rename from Source/AppAuth/macOS/OIDRedirectHTTPHandler.m rename to Sources/AppAuth/macOS/OIDRedirectHTTPHandler.m diff --git a/Source/AppAuthCore.h b/Sources/AppAuthCore.h similarity index 100% rename from Source/AppAuthCore.h rename to Sources/AppAuthCore.h diff --git a/Source/AppAuthCore/OIDAuthState.h b/Sources/AppAuthCore/OIDAuthState.h similarity index 100% rename from Source/AppAuthCore/OIDAuthState.h rename to Sources/AppAuthCore/OIDAuthState.h diff --git a/Source/AppAuthCore/OIDAuthState.m b/Sources/AppAuthCore/OIDAuthState.m similarity index 100% rename from Source/AppAuthCore/OIDAuthState.m rename to Sources/AppAuthCore/OIDAuthState.m diff --git a/Source/AppAuthCore/OIDAuthStateChangeDelegate.h b/Sources/AppAuthCore/OIDAuthStateChangeDelegate.h similarity index 100% rename from Source/AppAuthCore/OIDAuthStateChangeDelegate.h rename to Sources/AppAuthCore/OIDAuthStateChangeDelegate.h diff --git a/Source/AppAuthCore/OIDAuthStateErrorDelegate.h b/Sources/AppAuthCore/OIDAuthStateErrorDelegate.h similarity index 100% rename from Source/AppAuthCore/OIDAuthStateErrorDelegate.h rename to Sources/AppAuthCore/OIDAuthStateErrorDelegate.h diff --git a/Source/AppAuthCore/OIDAuthorizationRequest.h b/Sources/AppAuthCore/OIDAuthorizationRequest.h similarity index 100% rename from Source/AppAuthCore/OIDAuthorizationRequest.h rename to Sources/AppAuthCore/OIDAuthorizationRequest.h diff --git a/Source/AppAuthCore/OIDAuthorizationRequest.m b/Sources/AppAuthCore/OIDAuthorizationRequest.m similarity index 100% rename from Source/AppAuthCore/OIDAuthorizationRequest.m rename to Sources/AppAuthCore/OIDAuthorizationRequest.m diff --git a/Source/AppAuthCore/OIDAuthorizationResponse.h b/Sources/AppAuthCore/OIDAuthorizationResponse.h similarity index 100% rename from Source/AppAuthCore/OIDAuthorizationResponse.h rename to Sources/AppAuthCore/OIDAuthorizationResponse.h diff --git a/Source/AppAuthCore/OIDAuthorizationResponse.m b/Sources/AppAuthCore/OIDAuthorizationResponse.m similarity index 100% rename from Source/AppAuthCore/OIDAuthorizationResponse.m rename to Sources/AppAuthCore/OIDAuthorizationResponse.m diff --git a/Source/AppAuthCore/OIDAuthorizationService.h b/Sources/AppAuthCore/OIDAuthorizationService.h similarity index 100% rename from Source/AppAuthCore/OIDAuthorizationService.h rename to Sources/AppAuthCore/OIDAuthorizationService.h diff --git a/Source/AppAuthCore/OIDAuthorizationService.m b/Sources/AppAuthCore/OIDAuthorizationService.m similarity index 100% rename from Source/AppAuthCore/OIDAuthorizationService.m rename to Sources/AppAuthCore/OIDAuthorizationService.m diff --git a/Source/AppAuthCore/OIDClientMetadataParameters.h b/Sources/AppAuthCore/OIDClientMetadataParameters.h similarity index 100% rename from Source/AppAuthCore/OIDClientMetadataParameters.h rename to Sources/AppAuthCore/OIDClientMetadataParameters.h diff --git a/Source/AppAuthCore/OIDClientMetadataParameters.m b/Sources/AppAuthCore/OIDClientMetadataParameters.m similarity index 100% rename from Source/AppAuthCore/OIDClientMetadataParameters.m rename to Sources/AppAuthCore/OIDClientMetadataParameters.m diff --git a/Source/AppAuthCore/OIDDefines.h b/Sources/AppAuthCore/OIDDefines.h similarity index 100% rename from Source/AppAuthCore/OIDDefines.h rename to Sources/AppAuthCore/OIDDefines.h diff --git a/Source/AppAuthCore/OIDEndSessionRequest.h b/Sources/AppAuthCore/OIDEndSessionRequest.h similarity index 100% rename from Source/AppAuthCore/OIDEndSessionRequest.h rename to Sources/AppAuthCore/OIDEndSessionRequest.h diff --git a/Source/AppAuthCore/OIDEndSessionRequest.m b/Sources/AppAuthCore/OIDEndSessionRequest.m similarity index 100% rename from Source/AppAuthCore/OIDEndSessionRequest.m rename to Sources/AppAuthCore/OIDEndSessionRequest.m diff --git a/Source/AppAuthCore/OIDEndSessionResponse.h b/Sources/AppAuthCore/OIDEndSessionResponse.h similarity index 100% rename from Source/AppAuthCore/OIDEndSessionResponse.h rename to Sources/AppAuthCore/OIDEndSessionResponse.h diff --git a/Source/AppAuthCore/OIDEndSessionResponse.m b/Sources/AppAuthCore/OIDEndSessionResponse.m similarity index 100% rename from Source/AppAuthCore/OIDEndSessionResponse.m rename to Sources/AppAuthCore/OIDEndSessionResponse.m diff --git a/Source/AppAuthCore/OIDError.h b/Sources/AppAuthCore/OIDError.h similarity index 100% rename from Source/AppAuthCore/OIDError.h rename to Sources/AppAuthCore/OIDError.h diff --git a/Source/AppAuthCore/OIDError.m b/Sources/AppAuthCore/OIDError.m similarity index 100% rename from Source/AppAuthCore/OIDError.m rename to Sources/AppAuthCore/OIDError.m diff --git a/Source/AppAuthCore/OIDErrorUtilities.h b/Sources/AppAuthCore/OIDErrorUtilities.h similarity index 100% rename from Source/AppAuthCore/OIDErrorUtilities.h rename to Sources/AppAuthCore/OIDErrorUtilities.h diff --git a/Source/AppAuthCore/OIDErrorUtilities.m b/Sources/AppAuthCore/OIDErrorUtilities.m similarity index 100% rename from Source/AppAuthCore/OIDErrorUtilities.m rename to Sources/AppAuthCore/OIDErrorUtilities.m diff --git a/Source/AppAuthCore/OIDExternalUserAgent.h b/Sources/AppAuthCore/OIDExternalUserAgent.h similarity index 100% rename from Source/AppAuthCore/OIDExternalUserAgent.h rename to Sources/AppAuthCore/OIDExternalUserAgent.h diff --git a/Source/AppAuthCore/OIDExternalUserAgentRequest.h b/Sources/AppAuthCore/OIDExternalUserAgentRequest.h similarity index 100% rename from Source/AppAuthCore/OIDExternalUserAgentRequest.h rename to Sources/AppAuthCore/OIDExternalUserAgentRequest.h diff --git a/Source/AppAuthCore/OIDExternalUserAgentSession.h b/Sources/AppAuthCore/OIDExternalUserAgentSession.h similarity index 100% rename from Source/AppAuthCore/OIDExternalUserAgentSession.h rename to Sources/AppAuthCore/OIDExternalUserAgentSession.h diff --git a/Source/AppAuthCore/OIDFieldMapping.h b/Sources/AppAuthCore/OIDFieldMapping.h similarity index 100% rename from Source/AppAuthCore/OIDFieldMapping.h rename to Sources/AppAuthCore/OIDFieldMapping.h diff --git a/Source/AppAuthCore/OIDFieldMapping.m b/Sources/AppAuthCore/OIDFieldMapping.m similarity index 100% rename from Source/AppAuthCore/OIDFieldMapping.m rename to Sources/AppAuthCore/OIDFieldMapping.m diff --git a/Source/AppAuthCore/OIDGrantTypes.h b/Sources/AppAuthCore/OIDGrantTypes.h similarity index 100% rename from Source/AppAuthCore/OIDGrantTypes.h rename to Sources/AppAuthCore/OIDGrantTypes.h diff --git a/Source/AppAuthCore/OIDGrantTypes.m b/Sources/AppAuthCore/OIDGrantTypes.m similarity index 100% rename from Source/AppAuthCore/OIDGrantTypes.m rename to Sources/AppAuthCore/OIDGrantTypes.m diff --git a/Source/AppAuthCore/OIDIDToken.h b/Sources/AppAuthCore/OIDIDToken.h similarity index 100% rename from Source/AppAuthCore/OIDIDToken.h rename to Sources/AppAuthCore/OIDIDToken.h diff --git a/Source/AppAuthCore/OIDIDToken.m b/Sources/AppAuthCore/OIDIDToken.m similarity index 100% rename from Source/AppAuthCore/OIDIDToken.m rename to Sources/AppAuthCore/OIDIDToken.m diff --git a/Source/AppAuthCore/OIDRegistrationRequest.h b/Sources/AppAuthCore/OIDRegistrationRequest.h similarity index 100% rename from Source/AppAuthCore/OIDRegistrationRequest.h rename to Sources/AppAuthCore/OIDRegistrationRequest.h diff --git a/Source/AppAuthCore/OIDRegistrationRequest.m b/Sources/AppAuthCore/OIDRegistrationRequest.m similarity index 100% rename from Source/AppAuthCore/OIDRegistrationRequest.m rename to Sources/AppAuthCore/OIDRegistrationRequest.m diff --git a/Source/AppAuthCore/OIDRegistrationResponse.h b/Sources/AppAuthCore/OIDRegistrationResponse.h similarity index 100% rename from Source/AppAuthCore/OIDRegistrationResponse.h rename to Sources/AppAuthCore/OIDRegistrationResponse.h diff --git a/Source/AppAuthCore/OIDRegistrationResponse.m b/Sources/AppAuthCore/OIDRegistrationResponse.m similarity index 100% rename from Source/AppAuthCore/OIDRegistrationResponse.m rename to Sources/AppAuthCore/OIDRegistrationResponse.m diff --git a/Source/AppAuthCore/OIDResponseTypes.h b/Sources/AppAuthCore/OIDResponseTypes.h similarity index 100% rename from Source/AppAuthCore/OIDResponseTypes.h rename to Sources/AppAuthCore/OIDResponseTypes.h diff --git a/Source/AppAuthCore/OIDResponseTypes.m b/Sources/AppAuthCore/OIDResponseTypes.m similarity index 100% rename from Source/AppAuthCore/OIDResponseTypes.m rename to Sources/AppAuthCore/OIDResponseTypes.m diff --git a/Source/AppAuthCore/OIDScopeUtilities.h b/Sources/AppAuthCore/OIDScopeUtilities.h similarity index 100% rename from Source/AppAuthCore/OIDScopeUtilities.h rename to Sources/AppAuthCore/OIDScopeUtilities.h diff --git a/Source/AppAuthCore/OIDScopeUtilities.m b/Sources/AppAuthCore/OIDScopeUtilities.m similarity index 100% rename from Source/AppAuthCore/OIDScopeUtilities.m rename to Sources/AppAuthCore/OIDScopeUtilities.m diff --git a/Source/AppAuthCore/OIDScopes.h b/Sources/AppAuthCore/OIDScopes.h similarity index 100% rename from Source/AppAuthCore/OIDScopes.h rename to Sources/AppAuthCore/OIDScopes.h diff --git a/Source/AppAuthCore/OIDScopes.m b/Sources/AppAuthCore/OIDScopes.m similarity index 100% rename from Source/AppAuthCore/OIDScopes.m rename to Sources/AppAuthCore/OIDScopes.m diff --git a/Source/AppAuthCore/OIDServiceConfiguration.h b/Sources/AppAuthCore/OIDServiceConfiguration.h similarity index 100% rename from Source/AppAuthCore/OIDServiceConfiguration.h rename to Sources/AppAuthCore/OIDServiceConfiguration.h diff --git a/Source/AppAuthCore/OIDServiceConfiguration.m b/Sources/AppAuthCore/OIDServiceConfiguration.m similarity index 100% rename from Source/AppAuthCore/OIDServiceConfiguration.m rename to Sources/AppAuthCore/OIDServiceConfiguration.m diff --git a/Source/AppAuthCore/OIDServiceDiscovery.h b/Sources/AppAuthCore/OIDServiceDiscovery.h similarity index 100% rename from Source/AppAuthCore/OIDServiceDiscovery.h rename to Sources/AppAuthCore/OIDServiceDiscovery.h diff --git a/Source/AppAuthCore/OIDServiceDiscovery.m b/Sources/AppAuthCore/OIDServiceDiscovery.m similarity index 100% rename from Source/AppAuthCore/OIDServiceDiscovery.m rename to Sources/AppAuthCore/OIDServiceDiscovery.m diff --git a/Source/AppAuthCore/OIDTokenRequest.h b/Sources/AppAuthCore/OIDTokenRequest.h similarity index 100% rename from Source/AppAuthCore/OIDTokenRequest.h rename to Sources/AppAuthCore/OIDTokenRequest.h diff --git a/Source/AppAuthCore/OIDTokenRequest.m b/Sources/AppAuthCore/OIDTokenRequest.m similarity index 100% rename from Source/AppAuthCore/OIDTokenRequest.m rename to Sources/AppAuthCore/OIDTokenRequest.m diff --git a/Source/AppAuthCore/OIDTokenResponse.h b/Sources/AppAuthCore/OIDTokenResponse.h similarity index 100% rename from Source/AppAuthCore/OIDTokenResponse.h rename to Sources/AppAuthCore/OIDTokenResponse.h diff --git a/Source/AppAuthCore/OIDTokenResponse.m b/Sources/AppAuthCore/OIDTokenResponse.m similarity index 100% rename from Source/AppAuthCore/OIDTokenResponse.m rename to Sources/AppAuthCore/OIDTokenResponse.m diff --git a/Source/AppAuthCore/OIDTokenUtilities.h b/Sources/AppAuthCore/OIDTokenUtilities.h similarity index 100% rename from Source/AppAuthCore/OIDTokenUtilities.h rename to Sources/AppAuthCore/OIDTokenUtilities.h diff --git a/Source/AppAuthCore/OIDTokenUtilities.m b/Sources/AppAuthCore/OIDTokenUtilities.m similarity index 100% rename from Source/AppAuthCore/OIDTokenUtilities.m rename to Sources/AppAuthCore/OIDTokenUtilities.m diff --git a/Source/AppAuthCore/OIDURLQueryComponent.h b/Sources/AppAuthCore/OIDURLQueryComponent.h similarity index 100% rename from Source/AppAuthCore/OIDURLQueryComponent.h rename to Sources/AppAuthCore/OIDURLQueryComponent.h diff --git a/Source/AppAuthCore/OIDURLQueryComponent.m b/Sources/AppAuthCore/OIDURLQueryComponent.m similarity index 100% rename from Source/AppAuthCore/OIDURLQueryComponent.m rename to Sources/AppAuthCore/OIDURLQueryComponent.m diff --git a/Source/AppAuthCore/OIDURLSessionProvider.h b/Sources/AppAuthCore/OIDURLSessionProvider.h similarity index 100% rename from Source/AppAuthCore/OIDURLSessionProvider.h rename to Sources/AppAuthCore/OIDURLSessionProvider.h diff --git a/Source/AppAuthCore/OIDURLSessionProvider.m b/Sources/AppAuthCore/OIDURLSessionProvider.m similarity index 100% rename from Source/AppAuthCore/OIDURLSessionProvider.m rename to Sources/AppAuthCore/OIDURLSessionProvider.m diff --git a/Sources/AppAuthCore/Resources/PrivacyInfo.xcprivacy b/Sources/AppAuthCore/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..cc6746dcb --- /dev/null +++ b/Sources/AppAuthCore/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,16 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyCollectedDataTypes + + + NSPrivacyTrackingDomains + + NSPrivacyTracking + + + diff --git a/Source/AppAuthTV.h b/Sources/AppAuthTV.h similarity index 100% rename from Source/AppAuthTV.h rename to Sources/AppAuthTV.h diff --git a/Source/AppAuthTV/OIDTVAuthorizationRequest.h b/Sources/AppAuthTV/OIDTVAuthorizationRequest.h similarity index 100% rename from Source/AppAuthTV/OIDTVAuthorizationRequest.h rename to Sources/AppAuthTV/OIDTVAuthorizationRequest.h diff --git a/Source/AppAuthTV/OIDTVAuthorizationRequest.m b/Sources/AppAuthTV/OIDTVAuthorizationRequest.m similarity index 100% rename from Source/AppAuthTV/OIDTVAuthorizationRequest.m rename to Sources/AppAuthTV/OIDTVAuthorizationRequest.m diff --git a/Source/AppAuthTV/OIDTVAuthorizationResponse.h b/Sources/AppAuthTV/OIDTVAuthorizationResponse.h similarity index 100% rename from Source/AppAuthTV/OIDTVAuthorizationResponse.h rename to Sources/AppAuthTV/OIDTVAuthorizationResponse.h diff --git a/Source/AppAuthTV/OIDTVAuthorizationResponse.m b/Sources/AppAuthTV/OIDTVAuthorizationResponse.m similarity index 100% rename from Source/AppAuthTV/OIDTVAuthorizationResponse.m rename to Sources/AppAuthTV/OIDTVAuthorizationResponse.m diff --git a/Source/AppAuthTV/OIDTVAuthorizationService.h b/Sources/AppAuthTV/OIDTVAuthorizationService.h similarity index 100% rename from Source/AppAuthTV/OIDTVAuthorizationService.h rename to Sources/AppAuthTV/OIDTVAuthorizationService.h diff --git a/Source/AppAuthTV/OIDTVAuthorizationService.m b/Sources/AppAuthTV/OIDTVAuthorizationService.m similarity index 100% rename from Source/AppAuthTV/OIDTVAuthorizationService.m rename to Sources/AppAuthTV/OIDTVAuthorizationService.m diff --git a/Source/AppAuthTV/OIDTVServiceConfiguration.h b/Sources/AppAuthTV/OIDTVServiceConfiguration.h similarity index 100% rename from Source/AppAuthTV/OIDTVServiceConfiguration.h rename to Sources/AppAuthTV/OIDTVServiceConfiguration.h diff --git a/Source/AppAuthTV/OIDTVServiceConfiguration.m b/Sources/AppAuthTV/OIDTVServiceConfiguration.m similarity index 100% rename from Source/AppAuthTV/OIDTVServiceConfiguration.m rename to Sources/AppAuthTV/OIDTVServiceConfiguration.m diff --git a/Source/AppAuthTV/OIDTVTokenRequest.h b/Sources/AppAuthTV/OIDTVTokenRequest.h similarity index 100% rename from Source/AppAuthTV/OIDTVTokenRequest.h rename to Sources/AppAuthTV/OIDTVTokenRequest.h diff --git a/Source/AppAuthTV/OIDTVTokenRequest.m b/Sources/AppAuthTV/OIDTVTokenRequest.m similarity index 100% rename from Source/AppAuthTV/OIDTVTokenRequest.m rename to Sources/AppAuthTV/OIDTVTokenRequest.m diff --git a/Sources/AppAuthTV/Resources/PrivacyInfo.xcprivacy b/Sources/AppAuthTV/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..cc6746dcb --- /dev/null +++ b/Sources/AppAuthTV/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,16 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyCollectedDataTypes + + + NSPrivacyTrackingDomains + + NSPrivacyTracking + + + diff --git a/Source/CoreFramework/AppAuthCore.h b/Sources/CoreFramework/AppAuthCore.h similarity index 100% rename from Source/CoreFramework/AppAuthCore.h rename to Sources/CoreFramework/AppAuthCore.h diff --git a/Source/CoreFramework/Info.plist b/Sources/CoreFramework/Info.plist similarity index 100% rename from Source/CoreFramework/Info.plist rename to Sources/CoreFramework/Info.plist diff --git a/Source/Framework/AppAuth.h b/Sources/Framework/AppAuth.h similarity index 100% rename from Source/Framework/AppAuth.h rename to Sources/Framework/AppAuth.h diff --git a/Source/Framework/Info.plist b/Sources/Framework/Info.plist similarity index 100% rename from Source/Framework/Info.plist rename to Sources/Framework/Info.plist diff --git a/Source/TVFramework/AppAuthTV.h b/Sources/TVFramework/AppAuthTV.h similarity index 100% rename from Source/TVFramework/AppAuthTV.h rename to Sources/TVFramework/AppAuthTV.h diff --git a/Source/TVFramework/Info.plist b/Sources/TVFramework/Info.plist similarity index 100% rename from Source/TVFramework/Info.plist rename to Sources/TVFramework/Info.plist diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m index 7b1d19c95..a5ccd6e1d 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m @@ -21,10 +21,10 @@ #if SWIFT_PACKAGE @import AppAuthTV; #else -#import "Source/AppAuthCore/OIDScopeUtilities.h" -#import "Source/AppAuthCore/OIDURLQueryComponent.h" -#import "Source/AppAuthTV/OIDTVAuthorizationRequest.h" -#import "Source/AppAuthTV/OIDTVServiceConfiguration.h" +#import "Sources/AppAuthCore/OIDScopeUtilities.h" +#import "Sources/AppAuthCore/OIDURLQueryComponent.h" +#import "Sources/AppAuthTV/OIDTVAuthorizationRequest.h" +#import "Sources/AppAuthTV/OIDTVServiceConfiguration.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m index 288228e88..436232322 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m @@ -21,12 +21,12 @@ #if SWIFT_PACKAGE @import AppAuthTV; #else -#import "Source/AppAuthCore/OIDScopeUtilities.h" -#import "Source/AppAuthCore/OIDURLQueryComponent.h" -#import "Source/AppAuthTV/OIDTVAuthorizationRequest.h" -#import "Source/AppAuthTV/OIDTVAuthorizationResponse.h" -#import "Source/AppAuthTV/OIDTVServiceConfiguration.h" -#import "Source/AppAuthTV/OIDTVTokenRequest.h" +#import "Sources/AppAuthCore/OIDScopeUtilities.h" +#import "Sources/AppAuthCore/OIDURLQueryComponent.h" +#import "Sources/AppAuthTV/OIDTVAuthorizationRequest.h" +#import "Sources/AppAuthTV/OIDTVAuthorizationResponse.h" +#import "Sources/AppAuthTV/OIDTVServiceConfiguration.h" +#import "Sources/AppAuthTV/OIDTVTokenRequest.h" #endif /*! @brief Test value for the @c deviceAuthorizationEndpoint property. diff --git a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m index 4778a227b..cd56344fd 100644 --- a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m +++ b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m @@ -21,11 +21,11 @@ #if SWIFT_PACKAGE @import AppAuthTV; #else -#import "Source/AppAuthCore/OIDScopeUtilities.h" -#import "Source/AppAuthTV/OIDTVAuthorizationRequest.h" -#import "Source/AppAuthTV/OIDTVAuthorizationResponse.h" -#import "Source/AppAuthTV/OIDTVServiceConfiguration.h" -#import "Source/AppAuthTV/OIDTVTokenRequest.h" +#import "Sources/AppAuthCore/OIDScopeUtilities.h" +#import "Sources/AppAuthTV/OIDTVAuthorizationRequest.h" +#import "Sources/AppAuthTV/OIDTVAuthorizationResponse.h" +#import "Sources/AppAuthTV/OIDTVServiceConfiguration.h" +#import "Sources/AppAuthTV/OIDTVTokenRequest.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is diff --git a/UnitTests/OIDAuthStateTests.h b/UnitTests/OIDAuthStateTests.h index 2f1a62ab1..3bd1eb6fb 100644 --- a/UnitTests/OIDAuthStateTests.h +++ b/UnitTests/OIDAuthStateTests.h @@ -21,8 +21,8 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDAuthStateChangeDelegate.h" -#import "Source/AppAuthCore/OIDAuthStateErrorDelegate.h" +#import "Sources/AppAuthCore/OIDAuthStateChangeDelegate.h" +#import "Sources/AppAuthCore/OIDAuthStateErrorDelegate.h" #endif @class OIDAuthState; diff --git a/UnitTests/OIDAuthStateTests.m b/UnitTests/OIDAuthStateTests.m index d12f2a831..051902833 100644 --- a/UnitTests/OIDAuthStateTests.m +++ b/UnitTests/OIDAuthStateTests.m @@ -25,11 +25,11 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDAuthState.h" -#import "Source/AppAuthCore/OIDAuthorizationResponse.h" -#import "Source/AppAuthCore/OIDErrorUtilities.h" -#import "Source/AppAuthCore/OIDRegistrationResponse.h" -#import "Source/AppAuthCore/OIDTokenResponse.h" +#import "Sources/AppAuthCore/OIDAuthState.h" +#import "Sources/AppAuthCore/OIDAuthorizationResponse.h" +#import "Sources/AppAuthCore/OIDErrorUtilities.h" +#import "Sources/AppAuthCore/OIDRegistrationResponse.h" +#import "Sources/AppAuthCore/OIDTokenResponse.h" #endif #import "OIDTokenRequestTests.h" diff --git a/UnitTests/OIDAuthorizationRequestTests.m b/UnitTests/OIDAuthorizationRequestTests.m index 92ed8ee05..dd1329383 100644 --- a/UnitTests/OIDAuthorizationRequestTests.m +++ b/UnitTests/OIDAuthorizationRequestTests.m @@ -23,9 +23,9 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDAuthorizationRequest.h" -#import "Source/AppAuthCore/OIDScopeUtilities.h" -#import "Source/AppAuthCore/OIDServiceConfiguration.h" +#import "Sources/AppAuthCore/OIDAuthorizationRequest.h" +#import "Sources/AppAuthCore/OIDScopeUtilities.h" +#import "Sources/AppAuthCore/OIDServiceConfiguration.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/OIDAuthorizationResponseTests.m b/UnitTests/OIDAuthorizationResponseTests.m index 10e48eaa8..3aded7eaa 100644 --- a/UnitTests/OIDAuthorizationResponseTests.m +++ b/UnitTests/OIDAuthorizationResponseTests.m @@ -23,9 +23,9 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDAuthorizationRequest.h" -#import "Source/AppAuthCore/OIDAuthorizationResponse.h" -#import "Source/AppAuthCore/OIDGrantTypes.h" +#import "Sources/AppAuthCore/OIDAuthorizationRequest.h" +#import "Sources/AppAuthCore/OIDAuthorizationResponse.h" +#import "Sources/AppAuthCore/OIDGrantTypes.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/OIDEndSessionRequestTests.m b/UnitTests/OIDEndSessionRequestTests.m index 4620d3b82..38543c8d3 100644 --- a/UnitTests/OIDEndSessionRequestTests.m +++ b/UnitTests/OIDEndSessionRequestTests.m @@ -23,9 +23,9 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDEndSessionRequest.h" -#import "Source/AppAuthCore/OIDServiceConfiguration.h" -#import "Source/AppAuthCore/OIDServiceDiscovery.h" +#import "Sources/AppAuthCore/OIDEndSessionRequest.h" +#import "Sources/AppAuthCore/OIDServiceConfiguration.h" +#import "Sources/AppAuthCore/OIDServiceDiscovery.h" #endif /*! @brief Test value for the @c redirectURL property. diff --git a/UnitTests/OIDGrantTypesTests.m b/UnitTests/OIDGrantTypesTests.m index 8c06d5701..b6efc8ea1 100644 --- a/UnitTests/OIDGrantTypesTests.m +++ b/UnitTests/OIDGrantTypesTests.m @@ -21,7 +21,7 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDGrantTypes.h" +#import "Sources/AppAuthCore/OIDGrantTypes.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/OIDRegistrationRequestTests.m b/UnitTests/OIDRegistrationRequestTests.m index 5fbbd585e..440dab2a5 100644 --- a/UnitTests/OIDRegistrationRequestTests.m +++ b/UnitTests/OIDRegistrationRequestTests.m @@ -23,9 +23,9 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDClientMetadataParameters.h" -#import "Source/AppAuthCore/OIDRegistrationRequest.h" -#import "Source/AppAuthCore/OIDServiceConfiguration.h" +#import "Sources/AppAuthCore/OIDClientMetadataParameters.h" +#import "Sources/AppAuthCore/OIDRegistrationRequest.h" +#import "Sources/AppAuthCore/OIDServiceConfiguration.h" #endif /*! @brief Test key for the @c additionalParameters property. diff --git a/UnitTests/OIDRegistrationResponseTests.m b/UnitTests/OIDRegistrationResponseTests.m index f747cc244..92e57e338 100644 --- a/UnitTests/OIDRegistrationResponseTests.m +++ b/UnitTests/OIDRegistrationResponseTests.m @@ -24,8 +24,8 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDRegistrationRequest.h" -#import "Source/AppAuthCore/OIDRegistrationResponse.h" +#import "Sources/AppAuthCore/OIDRegistrationRequest.h" +#import "Sources/AppAuthCore/OIDRegistrationResponse.h" #endif /*! @brief The test value for the @c clientID property. diff --git a/UnitTests/OIDResponseTypesTests.m b/UnitTests/OIDResponseTypesTests.m index 5402ebbf5..659597735 100644 --- a/UnitTests/OIDResponseTypesTests.m +++ b/UnitTests/OIDResponseTypesTests.m @@ -21,7 +21,7 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDResponseTypes.h" +#import "Sources/AppAuthCore/OIDResponseTypes.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/OIDScopesTests.m b/UnitTests/OIDScopesTests.m index 2d9173748..b5276ebd8 100644 --- a/UnitTests/OIDScopesTests.m +++ b/UnitTests/OIDScopesTests.m @@ -21,7 +21,7 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDScopes.h" +#import "Sources/AppAuthCore/OIDScopes.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/OIDServiceConfigurationTests.m b/UnitTests/OIDServiceConfigurationTests.m index 3be785e60..7f4fcb11c 100644 --- a/UnitTests/OIDServiceConfigurationTests.m +++ b/UnitTests/OIDServiceConfigurationTests.m @@ -25,10 +25,10 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDAuthorizationService.h" -#import "Source/AppAuthCore/OIDError.h" -#import "Source/AppAuthCore/OIDServiceConfiguration.h" -#import "Source/AppAuthCore/OIDServiceDiscovery.h" +#import "Sources/AppAuthCore/OIDAuthorizationService.h" +#import "Sources/AppAuthCore/OIDError.h" +#import "Sources/AppAuthCore/OIDServiceConfiguration.h" +#import "Sources/AppAuthCore/OIDServiceDiscovery.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/OIDServiceDiscoveryTests.m b/UnitTests/OIDServiceDiscoveryTests.m index 6329feab3..6d9faf799 100644 --- a/UnitTests/OIDServiceDiscoveryTests.m +++ b/UnitTests/OIDServiceDiscoveryTests.m @@ -21,8 +21,8 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDError.h" -#import "Source/AppAuthCore/OIDServiceDiscovery.h" +#import "Sources/AppAuthCore/OIDError.h" +#import "Sources/AppAuthCore/OIDServiceDiscovery.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/OIDTokenRequestTests.m b/UnitTests/OIDTokenRequestTests.m index 20ba691ca..c387bd809 100644 --- a/UnitTests/OIDTokenRequestTests.m +++ b/UnitTests/OIDTokenRequestTests.m @@ -24,11 +24,11 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDAuthorizationRequest.h" -#import "Source/AppAuthCore/OIDAuthorizationResponse.h" -#import "Source/AppAuthCore/OIDScopeUtilities.h" -#import "Source/AppAuthCore/OIDServiceConfiguration.h" -#import "Source/AppAuthCore/OIDTokenRequest.h" +#import "Sources/AppAuthCore/OIDAuthorizationRequest.h" +#import "Sources/AppAuthCore/OIDAuthorizationResponse.h" +#import "Sources/AppAuthCore/OIDScopeUtilities.h" +#import "Sources/AppAuthCore/OIDServiceConfiguration.h" +#import "Sources/AppAuthCore/OIDTokenRequest.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/OIDTokenResponseTests.m b/UnitTests/OIDTokenResponseTests.m index 5d0a8f8ae..0eb525ed0 100644 --- a/UnitTests/OIDTokenResponseTests.m +++ b/UnitTests/OIDTokenResponseTests.m @@ -23,8 +23,8 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDTokenRequest.h" -#import "Source/AppAuthCore/OIDTokenResponse.h" +#import "Sources/AppAuthCore/OIDTokenRequest.h" +#import "Sources/AppAuthCore/OIDTokenResponse.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/OIDTokenUtilitiesTests.m b/UnitTests/OIDTokenUtilitiesTests.m index 0a7ff15ec..783e81273 100644 --- a/UnitTests/OIDTokenUtilitiesTests.m +++ b/UnitTests/OIDTokenUtilitiesTests.m @@ -21,7 +21,7 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDTokenUtilities.h" +#import "Sources/AppAuthCore/OIDTokenUtilities.h" #endif @interface OIDTokenUtilitiesTests : XCTestCase diff --git a/UnitTests/OIDURLQueryComponentTests.m b/UnitTests/OIDURLQueryComponentTests.m index d0eec3e5c..eac017c43 100644 --- a/UnitTests/OIDURLQueryComponentTests.m +++ b/UnitTests/OIDURLQueryComponentTests.m @@ -21,7 +21,7 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDURLQueryComponent.h" +#import "Sources/AppAuthCore/OIDURLQueryComponent.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of diff --git a/UnitTests/OIDURLQueryComponentTestsIOS7.m b/UnitTests/OIDURLQueryComponentTestsIOS7.m index af26ef107..571f61bba 100644 --- a/UnitTests/OIDURLQueryComponentTestsIOS7.m +++ b/UnitTests/OIDURLQueryComponentTestsIOS7.m @@ -21,7 +21,7 @@ #if SWIFT_PACKAGE @import AppAuthCore; #else -#import "Source/AppAuthCore/OIDURLQueryComponent.h" +#import "Sources/AppAuthCore/OIDURLQueryComponent.h" #endif // Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of From 7e2c09cbeb3bb799f26c268dbedb26325ea722a9 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Wed, 13 Mar 2024 16:06:30 -0700 Subject: [PATCH 189/204] Prepare for 1.7.3 release (#834) --- AppAuth.podspec | 2 +- CHANGELOG.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 70f3e4168..393dad42a 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.7.2" + s.version = "1.7.3" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index 12e915e1b..6411e5afb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.7.3 +- Fix missing manifest in bundle using SPM ([#833](https://github.com/openid/AppAuth-iOS/pull/833)) + # 1.7.2 - Streamline copying of privacy manifest ([#830](https://github.com/openid/AppAuth-iOS/pull/830)) From eee20d36310e37a26dde368b23c681a7616ec69b Mon Sep 17 00:00:00 2001 From: mdmathias Date: Tue, 2 Apr 2024 10:34:02 -0700 Subject: [PATCH 190/204] Adds defines module to AppAuth.podspec (#845) --- AppAuth.podspec | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AppAuth.podspec b/AppAuth.podspec index 393dad42a..cd11563a6 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -41,6 +41,10 @@ It follows the OAuth 2.0 for Native Apps best current practice s.source = { :git => "https://github.com/openid/AppAuth-iOS.git", :tag => s.version } s.requires_arc = true + s.xcconfig = { + 'DEFINES_MODULE' => 'YES', + } + # Subspec for the core AppAuth library classes only, suitable for extensions. s.subspec 'Core' do |core| core.source_files = "Sources/AppAuthCore.h", "Sources/AppAuthCore/*.{h,m}" From 8c0b028aedf617d6d780d448dc836f7048166d0f Mon Sep 17 00:00:00 2001 From: mdmathias Date: Thu, 11 Apr 2024 15:26:02 -0700 Subject: [PATCH 191/204] Prepare for 1.7.4 release (#846) --- AppAuth.podspec | 2 +- CHANGELOG.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index cd11563a6..cd9f99af3 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.7.3" + s.version = "1.7.4" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index 6411e5afb..73401d806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.7.4 +- Adds defines module to AppAuth.podspec ([#845](https://github.com/openid/AppAuth-iOS/pull/845)) + # 1.7.3 - Fix missing manifest in bundle using SPM ([#833](https://github.com/openid/AppAuth-iOS/pull/833)) From bbc3648ff20e594c3fb60774341ccebe679fa22d Mon Sep 17 00:00:00 2001 From: mdmathias Date: Thu, 18 Apr 2024 14:33:48 -0700 Subject: [PATCH 192/204] Use correct xcconfig syntax for podspec (#851) --- AppAuth.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index cd9f99af3..f1dfdf080 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -41,7 +41,7 @@ It follows the OAuth 2.0 for Native Apps best current practice s.source = { :git => "https://github.com/openid/AppAuth-iOS.git", :tag => s.version } s.requires_arc = true - s.xcconfig = { + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', } From c89ed571ae140f8eb1142735e6e23d7bb8c34cb2 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Mon, 22 Apr 2024 15:53:53 -0700 Subject: [PATCH 193/204] Prepare for 1.7.5 release (#852) --- AppAuth.podspec | 2 +- CHANGELOG.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index f1dfdf080..bfffe6cda 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.7.4" + s.version = "1.7.5" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index 73401d806..3fce4cb8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.7.5 +- Use correct xcconfig syntax for podspec ([#851](https://github.com/openid/AppAuth-iOS/pull/851)) + # 1.7.4 - Adds defines module to AppAuth.podspec ([#845](https://github.com/openid/AppAuth-iOS/pull/845)) From d4cbdfae3fff9f5a83d66a2073ae4d6d0d28a21a Mon Sep 17 00:00:00 2001 From: Brianna Morales <74382627+brnnmrls@users.noreply.github.com> Date: Mon, 21 Oct 2024 09:54:54 -0700 Subject: [PATCH 194/204] Use the macos-12 runner. (#879) --- .github/workflows/tests.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 523bed57d..2bcfa7e72 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -50,12 +50,14 @@ jobs: run: pod lib lint --verbose ${{ matrix.flags }} spm-build-test: - runs-on: macos-11 + runs-on: macos-12 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Build unit test target run: swift build - name: Run unit test target run: swift test --enable-code-coverage - name: Upload coverage to Codecov - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 + with: + version: v0.7.3 From b856e4bdf4d3f5063c933bd103b70541751f8437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Sharma?= <737941+loic-sharma@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:55:14 -0700 Subject: [PATCH 195/204] Fix OIDExternalUserAgentIOSCustomBrowser on iOS 10+ (#871) --- .../iOS/OIDExternalUserAgentIOSCustomBrowser.m | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m b/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m index be5dc820c..c49cbbf9c 100644 --- a/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m +++ b/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m @@ -145,7 +145,11 @@ - (BOOL)presentExternalUserAgentRequest:(nonnull id NSString *testURLString = [NSString stringWithFormat:@"%@://example.com", _canOpenURLScheme]; NSURL *testURL = [NSURL URLWithString:testURLString]; if (![[UIApplication sharedApplication] canOpenURL:testURL]) { - [[UIApplication sharedApplication] openURL:_appStoreURL]; + if (@available(iOS 10.0, *)) { + [[UIApplication sharedApplication] openURL:_appStoreURL options:@{} completionHandler:nil]; + } else { + [[UIApplication sharedApplication] openURL:_appStoreURL]; + } return NO; } } @@ -153,8 +157,14 @@ - (BOOL)presentExternalUserAgentRequest:(nonnull id // Transforms the request URL and opens it. NSURL *requestURL = [request externalUserAgentRequestURL]; requestURL = _URLTransformation(requestURL); - BOOL openedInBrowser = [[UIApplication sharedApplication] openURL:requestURL]; - return openedInBrowser; + if (@available(iOS 10.0, *)) { + BOOL willOpen = [[UIApplication sharedApplication] canOpenURL:requestURL]; + [[UIApplication sharedApplication] openURL:requestURL options:@{} completionHandler:nil]; + return willOpen; + } else { + BOOL openedInBrowser = [[UIApplication sharedApplication] openURL:requestURL]; + return openedInBrowser; + } } - (void)dismissExternalUserAgentAnimated:(BOOL)animated From ce416b9eeb9ca8b01dc05ea5b559fceeae67f8a1 Mon Sep 17 00:00:00 2001 From: Brianna Morales <74382627+brnnmrls@users.noreply.github.com> Date: Mon, 25 Nov 2024 14:43:22 -0800 Subject: [PATCH 196/204] Update runner to macos-13. (#886) --- .github/workflows/tests.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2bcfa7e72..0fc7553c3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,16 +12,16 @@ on: jobs: xcode-project-test: - runs-on: macos-12 + runs-on: macos-13 strategy: matrix: flags: [ - "-scheme AppAuth-iOS -destination 'platform=iOS Simulator,name=iPhone 11,OS=16.2' -sdk 'iphonesimulator16.2'", - "-scheme AppAuth-macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx13.1'", - "-scheme AppAuth_macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx13.1'", - "-scheme AppAuth-tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=16.1' -sdk 'appletvsimulator16.1'", - "-scheme AppAuth_tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=16.1' -sdk 'appletvsimulator16.1'", - "-scheme AppAuthTV -destination 'platform=tvOS Simulator,name=Apple TV,OS=16.1' -sdk 'appletvsimulator16.1'" + "-scheme AppAuth-iOS -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2' -sdk 'iphonesimulator17.2'", + "-scheme AppAuth-macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx14.2'", + "-scheme AppAuth_macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx14.2'", + "-scheme AppAuth-tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=17.2' -sdk 'appletvsimulator17.2'", + "-scheme AppAuth_tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=17.2' -sdk 'appletvsimulator17.2'", + "-scheme AppAuthTV -destination 'platform=tvOS Simulator,name=Apple TV,OS=17.2' -sdk 'appletvsimulator17.2'" ] steps: - uses: actions/checkout@v3 @@ -32,7 +32,7 @@ jobs: ${{ matrix.flags }} pod-lib-lint: - runs-on: macos-12 + runs-on: macos-13 strategy: matrix: flags: [ @@ -50,7 +50,7 @@ jobs: run: pod lib lint --verbose ${{ matrix.flags }} spm-build-test: - runs-on: macos-12 + runs-on: macos-13 steps: - uses: actions/checkout@v3 - name: Build unit test target From 2781038865a80e2c425a1da12cc1327bcd56501f Mon Sep 17 00:00:00 2001 From: Brianna Morales <74382627+brnnmrls@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:53:33 -0800 Subject: [PATCH 197/204] Prepare for 1.7.6 release (#885) --- AppAuth.podspec | 2 +- CHANGELOG.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index bfffe6cda..40febc0a8 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.7.5" + s.version = "1.7.6" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fce4cb8d..13beb5467 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.7.6 +- Fix OIDExternalUserAgentIOSCustomBrowser on versions iOS 10+ ([#871](https://github.com/openid/AppAuth-iOS/pull/871)) +- Update runners in tests.yml to macos-13. ([#886](https://github.com/openid/AppAuth-iOS/pull/886)) + # 1.7.5 - Use correct xcconfig syntax for podspec ([#851](https://github.com/openid/AppAuth-iOS/pull/851)) From 1ba9e0f792210cc6d33ea262f69b7ea53d467323 Mon Sep 17 00:00:00 2001 From: dmaclach Date: Wed, 16 Apr 2025 06:52:06 -0700 Subject: [PATCH 198/204] Remove [UIApplication openURL:] (#911) --- AppAuth.podspec | 2 +- AppAuth.xcodeproj/project.pbxproj | 22 ++++++++++--------- CONTRIBUTORS | 1 + Package.swift | 2 +- README.md | 2 +- Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m | 14 +++++------- .../OIDExternalUserAgentIOSCustomBrowser.m | 17 ++++---------- 7 files changed, 25 insertions(+), 35 deletions(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 40febc0a8..46cb8cafa 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -31,7 +31,7 @@ It follows the OAuth 2.0 for Native Apps best current practice # classes of AppAuth with tokens on watchOS and tvOS, but currently the # library won't help you obtain authorization grants on those platforms. - ios_deployment_target = "9.0" + ios_deployment_target = "10.0" osx_deployment_target = "10.12" s.ios.deployment_target = ios_deployment_target s.osx.deployment_target = osx_deployment_target diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 8d770d93c..2d945920a 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -2888,6 +2888,7 @@ 340E73861C5D819B0076B1F6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + IPHONEOS_DEPLOYMENT_TARGET = 10.0; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; @@ -2896,6 +2897,7 @@ 340E73871C5D819B0076B1F6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + IPHONEOS_DEPLOYMENT_TARGET = 10.0; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; @@ -2907,7 +2909,7 @@ CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/"; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauth.AppAuthTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2924,7 +2926,7 @@ CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/"; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauth.AppAuthTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3038,7 +3040,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/CoreFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthCore; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3064,7 +3066,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/CoreFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthCore; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3089,7 +3091,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOS"; PRODUCT_NAME = AppAuth; @@ -3114,7 +3116,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOS"; PRODUCT_NAME = AppAuth; @@ -3133,7 +3135,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3147,7 +3149,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3401,7 +3403,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-ExtensionTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3415,7 +3417,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-ExtensionTests"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/CONTRIBUTORS b/CONTRIBUTORS index bf6f154f9..06dbb5823 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -20,3 +20,4 @@ Hernan Zalazar https://github.com/hzalaz Joseph Heenan https://github.com/jogu Julien Bodet https://github.com/julienbodet Tobias Schröpf https://github.com/schroepf +Dave MacLachlan https://github.com/dmaclach \ No newline at end of file diff --git a/Package.swift b/Package.swift index 103c0f761..baac5cb31 100644 --- a/Package.swift +++ b/Package.swift @@ -24,7 +24,7 @@ let package = Package( name: "AppAuth", platforms: [ .macOS(.v10_12), - .iOS(.v9), + .iOS(.v10), .tvOS(.v9), .watchOS(.v2) ], diff --git a/README.md b/README.md index 53085b3c4..8b55c6417 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ For tvOS, AppAuth implements [OAuth 2.0 Device Authorization Grant #### Supported Versions -AppAuth supports iOS 7 and above. +AppAuth supports iOS 10 and above. iOS 9+ uses the in-app browser tab pattern (via `SFSafariViewController`), and falls back to the system browser (mobile diff --git a/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m index 4a8cda0a3..41d5fa1c0 100644 --- a/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m +++ b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m @@ -175,18 +175,14 @@ - (BOOL)presentExternalUserAgentRequest:(id)request openedUserAgent = YES; } } - // iOS 8 and earlier, use mobile Safari + // If all else failed use the local browser. if (!openedUserAgent){ - openedUserAgent = [[UIApplication sharedApplication] openURL:requestURL]; + [[UIApplication sharedApplication] openURL:requestURL + options:@{} + completionHandler:nil]; + openedUserAgent = YES; } - if (!openedUserAgent) { - [self cleanUp]; - NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeSafariOpenError - underlyingError:nil - description:@"Unable to open Safari."]; - [session failExternalUserAgentFlowWithError:safariError]; - } return openedUserAgent; } diff --git a/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m b/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m index c49cbbf9c..900bfbe19 100644 --- a/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m +++ b/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m @@ -145,11 +145,7 @@ - (BOOL)presentExternalUserAgentRequest:(nonnull id NSString *testURLString = [NSString stringWithFormat:@"%@://example.com", _canOpenURLScheme]; NSURL *testURL = [NSURL URLWithString:testURLString]; if (![[UIApplication sharedApplication] canOpenURL:testURL]) { - if (@available(iOS 10.0, *)) { - [[UIApplication sharedApplication] openURL:_appStoreURL options:@{} completionHandler:nil]; - } else { - [[UIApplication sharedApplication] openURL:_appStoreURL]; - } + [[UIApplication sharedApplication] openURL:_appStoreURL options:@{} completionHandler:nil]; return NO; } } @@ -157,14 +153,9 @@ - (BOOL)presentExternalUserAgentRequest:(nonnull id // Transforms the request URL and opens it. NSURL *requestURL = [request externalUserAgentRequestURL]; requestURL = _URLTransformation(requestURL); - if (@available(iOS 10.0, *)) { - BOOL willOpen = [[UIApplication sharedApplication] canOpenURL:requestURL]; - [[UIApplication sharedApplication] openURL:requestURL options:@{} completionHandler:nil]; - return willOpen; - } else { - BOOL openedInBrowser = [[UIApplication sharedApplication] openURL:requestURL]; - return openedInBrowser; - } + BOOL willOpen = [[UIApplication sharedApplication] canOpenURL:requestURL]; + [[UIApplication sharedApplication] openURL:requestURL options:@{} completionHandler:nil]; + return willOpen; } - (void)dismissExternalUserAgentAnimated:(BOOL)animated From 14097528936dabc23681a6f2e1192a6a688ad1e5 Mon Sep 17 00:00:00 2001 From: Brianna Morales <74382627+brnnmrls@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:45:02 -0700 Subject: [PATCH 199/204] Update minimum iOS version from 10 to 12. (#918) --- AppAuth.podspec | 2 +- AppAuth.xcodeproj/project.pbxproj | 24 ++++----- Package.swift | 2 +- README.md | 2 +- Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h | 6 +-- Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m | 48 ----------------- Sources/AppAuthCore/OIDRegistrationRequest.m | 15 +++--- UnitTests/OIDAuthStateTests.m | 52 +++++++++++++++++-- UnitTests/OIDAuthorizationRequestTests.m | 18 ++++++- UnitTests/OIDAuthorizationResponseTests.m | 18 ++++++- UnitTests/OIDEndSessionRequestTests.m | 18 ++++++- UnitTests/OIDRegistrationRequestTests.m | 18 ++++++- UnitTests/OIDRegistrationResponseTests.m | 18 ++++++- UnitTests/OIDServiceConfigurationTests.m | 18 ++++++- UnitTests/OIDServiceDiscoveryTests.m | 24 ++++++--- UnitTests/OIDTokenRequestTests.m | 18 ++++++- UnitTests/OIDTokenResponseTests.m | 18 ++++++- 17 files changed, 219 insertions(+), 100 deletions(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 46cb8cafa..7ad140d0b 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -31,7 +31,7 @@ It follows the OAuth 2.0 for Native Apps best current practice # classes of AppAuth with tokens on watchOS and tvOS, but currently the # library won't help you obtain authorization grants on those platforms. - ios_deployment_target = "10.0" + ios_deployment_target = "12.0" osx_deployment_target = "10.12" s.ios.deployment_target = ios_deployment_target s.osx.deployment_target = osx_deployment_target diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 2d945920a..e17b22d34 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -2815,7 +2815,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -2873,7 +2873,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; @@ -2909,7 +2909,7 @@ CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/"; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauth.AppAuthTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2926,7 +2926,7 @@ CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/"; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.appauth.AppAuthTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3040,7 +3040,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/CoreFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthCore; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3066,7 +3066,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/CoreFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.openid.AppAuthCore; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3091,7 +3091,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOS"; PRODUCT_NAME = AppAuth; @@ -3116,7 +3116,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOS"; PRODUCT_NAME = AppAuth; @@ -3135,7 +3135,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3149,7 +3149,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-iOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3403,7 +3403,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-ExtensionTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3417,7 +3417,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; HEADER_SEARCH_PATHS = .; INFOPLIST_FILE = UnitTests/UnitTestsInfo.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.openid.AppAuth-ExtensionTests"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Package.swift b/Package.swift index baac5cb31..208eaf467 100644 --- a/Package.swift +++ b/Package.swift @@ -24,7 +24,7 @@ let package = Package( name: "AppAuth", platforms: [ .macOS(.v10_12), - .iOS(.v10), + .iOS(.v12), .tvOS(.v9), .watchOS(.v2) ], diff --git a/README.md b/README.md index 8b55c6417..540feda72 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ For tvOS, AppAuth implements [OAuth 2.0 Device Authorization Grant #### Supported Versions -AppAuth supports iOS 10 and above. +AppAuth supports iOS 12 and above. iOS 9+ uses the in-app browser tab pattern (via `SFSafariViewController`), and falls back to the system browser (mobile diff --git a/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h index 12abc203c..4ab6c7452 100644 --- a/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h +++ b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h @@ -41,10 +41,8 @@ API_UNAVAILABLE(macCatalyst) /*! @brief The designated initializer. @param presentingViewController The view controller from which to present the authentication UI. @discussion The specific authentication UI used depends on the iOS version and accessibility - options. iOS 8 uses the system browser, iOS 9-10 use @c SFSafariViewController, iOS 11 uses - @c SFAuthenticationSession - (unless Guided Access is on which does not work) or uses @c SFSafariViewController, and iOS - 12+ uses @c ASWebAuthenticationSession (unless Guided Access is on). + options. iOS 12+ uses @c ASWebAuthenticationSession (unless Guided Access is on), + otherwise local browser is used. */ - (nullable instancetype)initWithPresentingViewController: (UIViewController *)presentingViewController diff --git a/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m index 41d5fa1c0..7a3fa2278 100644 --- a/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m +++ b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m @@ -50,7 +50,6 @@ @implementation OIDExternalUserAgentIOS { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpartial-availability" __weak SFSafariViewController *_safariVC; - SFAuthenticationSession *_authenticationVC; ASWebAuthenticationSession *_webAuthenticationVC; #pragma clang diagnostic pop } @@ -134,47 +133,6 @@ - (BOOL)presentExternalUserAgentRequest:(id)request openedUserAgent = [authenticationVC start]; } } - // iOS 11, use SFAuthenticationSession - if (@available(iOS 11.0, *)) { - // SFAuthenticationSession doesn't work with guided access (rdar://40809553) - if (!openedUserAgent && !UIAccessibilityIsGuidedAccessEnabled()) { - __weak OIDExternalUserAgentIOS *weakSelf = self; - NSString *redirectScheme = request.redirectScheme; - SFAuthenticationSession *authenticationVC = - [[SFAuthenticationSession alloc] initWithURL:requestURL - callbackURLScheme:redirectScheme - completionHandler:^(NSURL * _Nullable callbackURL, - NSError * _Nullable error) { - __strong OIDExternalUserAgentIOS *strongSelf = weakSelf; - if (!strongSelf) { - return; - } - strongSelf->_authenticationVC = nil; - if (callbackURL) { - [strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL]; - } else { - NSError *safariError = - [OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow - underlyingError:error - description:@"User cancelled."]; - [strongSelf->_session failExternalUserAgentFlowWithError:safariError]; - } - }]; - _authenticationVC = authenticationVC; - openedUserAgent = [authenticationVC start]; - } - } - // iOS 9 and 10, use SFSafariViewController - if (@available(iOS 9.0, *)) { - if (!openedUserAgent && _presentingViewController) { - SFSafariViewController *safariVC = - [[SFSafariViewController alloc] initWithURL:requestURL]; - safariVC.delegate = self; - _safariVC = safariVC; - [_presentingViewController presentViewController:safariVC animated:YES completion:nil]; - openedUserAgent = YES; - } - } // If all else failed use the local browser. if (!openedUserAgent){ [[UIApplication sharedApplication] openURL:requestURL @@ -196,7 +154,6 @@ - (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(voi #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpartial-availability" SFSafariViewController *safariVC = _safariVC; - SFAuthenticationSession *authenticationVC = _authenticationVC; ASWebAuthenticationSession *webAuthenticationVC = _webAuthenticationVC; #pragma clang diagnostic pop @@ -206,10 +163,6 @@ - (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(voi // dismiss the ASWebAuthenticationSession [webAuthenticationVC cancel]; if (completion) completion(); - } else if (authenticationVC) { - // dismiss the SFAuthenticationSession - [authenticationVC cancel]; - if (completion) completion(); } else if (safariVC) { // dismiss the SFSafariViewController [safariVC dismissViewControllerAnimated:YES completion:completion]; @@ -222,7 +175,6 @@ - (void)cleanUp { // The weak references to |_safariVC| and |_session| are set to nil to avoid accidentally using // them while not in an authorization flow. _safariVC = nil; - _authenticationVC = nil; _webAuthenticationVC = nil; _session = nil; _externalUserAgentFlowInProgress = NO; diff --git a/Sources/AppAuthCore/OIDRegistrationRequest.m b/Sources/AppAuthCore/OIDRegistrationRequest.m index 9efd18fdd..2b3435f85 100644 --- a/Sources/AppAuthCore/OIDRegistrationRequest.m +++ b/Sources/AppAuthCore/OIDRegistrationRequest.m @@ -130,12 +130,15 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { forKey:kConfigurationKey]; NSString *initialAccessToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:kInitialAccessToken]; - NSArray *redirectURIs = [aDecoder decodeObjectOfClass:[NSArray class] - forKey:kRedirectURIsKey]; - NSArray *responseTypes = [aDecoder decodeObjectOfClass:[NSArray class] - forKey:kResponseTypesKey]; - NSArray *grantTypes = [aDecoder decodeObjectOfClass:[NSArray class] - forKey:kGrantTypesKey]; + NSArray *redirectURIs = + [aDecoder decodeObjectOfClasses:[NSSet setWithArray:@[[NSArray class], [NSURL class]]] + forKey:kRedirectURIsKey]; + NSArray *responseTypes = + [aDecoder decodeObjectOfClasses:[NSSet setWithArray:@[[NSArray class], [NSString class]]] + forKey:kResponseTypesKey]; + NSArray *grantTypes = + [aDecoder decodeObjectOfClasses:[NSSet setWithArray:@[[NSArray class], [NSString class]]] + forKey:kGrantTypesKey]; NSString *subjectType = [aDecoder decodeObjectOfClass:[NSString class] forKey:kSubjectTypeKey]; NSString *tokenEndpointAuthenticationMethod = diff --git a/UnitTests/OIDAuthStateTests.m b/UnitTests/OIDAuthStateTests.m index 051902833..d18ab3405 100644 --- a/UnitTests/OIDAuthStateTests.m +++ b/UnitTests/OIDAuthStateTests.m @@ -204,7 +204,18 @@ - (void)testNonCompliantNSCodingNSErrors { NSError *oauthError = [[self class] OAuthTokenInvalidGrantErrorWithUnderlyingError:nonCompliantError]; [authstate updateWithAuthorizationError:oauthError]; - XCTAssertNoThrow([NSKeyedArchiver archivedDataWithRootObject:authstate], @""); + NSError *error; + NSData *data; + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:authstate + requiringSecureCoding:YES + error:&error]; + XCTAssertNoThrow(data, @""); + } else { +#if !TARGET_OS_IOS + XCTAssertNoThrow([NSKeyedArchiver archivedDataWithRootObject:authstate], @""); +#endif + } } /*! @brief Tests @c OIDAuthState.updateWithAuthorizationResponse:error: with a success response. @@ -358,8 +369,22 @@ - (void)testSecureCoding { XCTAssert([OIDAuthState supportsSecureCoding], @""); OIDAuthState *authState = [[self class] testInstance]; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:authState]; - OIDAuthState *authStateCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + OIDAuthState *authStateCopy; + NSError *error; + NSData *data; + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:authState + requiringSecureCoding:YES + error:&error]; + authStateCopy = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDAuthState class] + fromData:data + error:&error]; + } else { +#if !TARGET_OS_IOS + data = [NSKeyedArchiver archivedDataWithRootObject:authState]; + authStateCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif + } XCTAssertEqualObjects(authStateCopy.refreshToken, authState.refreshToken, @""); XCTAssertEqualObjects(authStateCopy.scope, authState.scope, @""); @@ -375,9 +400,26 @@ - (void)testSecureCoding { // Verify the error object is indeed restored. NSError *oauthError = [[self class] OAuthTokenInvalidGrantErrorWithUnderlyingError:nil]; [authState updateWithTokenResponse:nil error:oauthError]; - data = [NSKeyedArchiver archivedDataWithRootObject:authState]; + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:authState + requiringSecureCoding:YES + error:&error]; + } else { +#if !TARGET_OS_IOS + data = [NSKeyedArchiver archivedDataWithRootObject:authState]; +#endif + } XCTAssertNotNil(authState.authorizationError, @""); - authStateCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + authStateCopy = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDAuthState class] + fromData:data + error:&error]; + } else { +#if !TARGET_OS_IOS + authStateCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif + } XCTAssertEqualObjects(authStateCopy.authorizationError.domain, authState.authorizationError.domain, @""); XCTAssertEqual(authStateCopy.authorizationError.code, authState.authorizationError.code, @""); diff --git a/UnitTests/OIDAuthorizationRequestTests.m b/UnitTests/OIDAuthorizationRequestTests.m index dd1329383..dcefc6ff2 100644 --- a/UnitTests/OIDAuthorizationRequestTests.m +++ b/UnitTests/OIDAuthorizationRequestTests.m @@ -322,8 +322,22 @@ - (void)testSecureCoding { XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue, @""); - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; - OIDAuthorizationRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + OIDAuthorizationRequest *requestCopy; + NSError *error; + NSData *data; + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:request + requiringSecureCoding:YES + error:&error]; + requestCopy = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDAuthorizationRequest class] + fromData:data + error:&error]; + } else { +#if !TARGET_OS_IOS + data = [NSKeyedArchiver archivedDataWithRootObject:request]; + requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif + } // Not a full test of the configuration deserialization, but should be sufficient as a smoke test // to make sure the configuration IS actually getting serialized and deserialized in the diff --git a/UnitTests/OIDAuthorizationResponseTests.m b/UnitTests/OIDAuthorizationResponseTests.m index 3aded7eaa..6bfb5fce2 100644 --- a/UnitTests/OIDAuthorizationResponseTests.m +++ b/UnitTests/OIDAuthorizationResponseTests.m @@ -158,8 +158,22 @@ - (void)testCopying { */ - (void)testSecureCoding { OIDAuthorizationResponse *response = [[self class] testInstance]; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:response]; - OIDAuthorizationResponse *responseCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + OIDAuthorizationResponse *responseCopy; + NSError *error; + NSData *data; + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:response + requiringSecureCoding:YES + error:&error]; + responseCopy = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDAuthorizationResponse class] + fromData:data + error:&error]; + } else { +#if !TARGET_OS_IOS + data = [NSKeyedArchiver archivedDataWithRootObject:response]; + responseCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif + } // Not a full test of the request deserialization, but should be sufficient as a smoke test // to make sure the request IS actually getting serialized and deserialized in the diff --git a/UnitTests/OIDEndSessionRequestTests.m b/UnitTests/OIDEndSessionRequestTests.m index 38543c8d3..d9ded81be 100644 --- a/UnitTests/OIDEndSessionRequestTests.m +++ b/UnitTests/OIDEndSessionRequestTests.m @@ -97,8 +97,22 @@ - (void)testSecureCoding { XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue); - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; - OIDEndSessionRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + OIDEndSessionRequest *requestCopy; + NSError *error; + NSData *data; + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:request + requiringSecureCoding:YES + error:&error]; + requestCopy = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDEndSessionRequest class] + fromData:data + error:&error]; + } else { +#if !TARGET_OS_IOS + data = [NSKeyedArchiver archivedDataWithRootObject:request]; + requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif + } XCTAssertNotNil(requestCopy.configuration); XCTAssertEqualObjects(requestCopy.configuration.authorizationEndpoint, diff --git a/UnitTests/OIDRegistrationRequestTests.m b/UnitTests/OIDRegistrationRequestTests.m index 440dab2a5..f2a7ba3e1 100644 --- a/UnitTests/OIDRegistrationRequestTests.m +++ b/UnitTests/OIDRegistrationRequestTests.m @@ -142,8 +142,22 @@ - (void)testSecureCoding { XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue); - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; - OIDRegistrationRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + OIDRegistrationRequest *requestCopy; + NSError *error; + NSData *data; + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:request + requiringSecureCoding:YES + error:&error]; + requestCopy = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDRegistrationRequest class] + fromData:data + error:&error]; + } else { +#if !TARGET_OS_IOS + data = [NSKeyedArchiver archivedDataWithRootObject:request]; + requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif + } // Not a full test of the configuration deserialization, but should be sufficient as a smoke test // to make sure the configuration IS actually getting serialized and deserialized in the diff --git a/UnitTests/OIDRegistrationResponseTests.m b/UnitTests/OIDRegistrationResponseTests.m index 92e57e338..f98787e55 100644 --- a/UnitTests/OIDRegistrationResponseTests.m +++ b/UnitTests/OIDRegistrationResponseTests.m @@ -119,8 +119,22 @@ - (void)testCopying { */ - (void)testSecureCoding { OIDRegistrationResponse *response = [[self class] testInstance]; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:response]; - OIDRegistrationResponse *responseCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + OIDRegistrationResponse *responseCopy; + NSError *error; + NSData *data; + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:response + requiringSecureCoding:YES + error:&error]; + responseCopy = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDRegistrationResponse class] + fromData:data + error:&error]; + } else { +#if !TARGET_OS_IOS + data = [NSKeyedArchiver archivedDataWithRootObject:response]; + responseCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif + } // Not a full test of the request deserialization, but should be sufficient as a smoke test // to make sure the request IS actually getting serialized and deserialized in the diff --git a/UnitTests/OIDServiceConfigurationTests.m b/UnitTests/OIDServiceConfigurationTests.m index 7f4fcb11c..bd4c5babb 100644 --- a/UnitTests/OIDServiceConfigurationTests.m +++ b/UnitTests/OIDServiceConfigurationTests.m @@ -363,8 +363,22 @@ - (void)testFetcherWithBadJSON { */ - (void)testSecureCoding { OIDServiceConfiguration *configuration = [[self class] testInstance]; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:configuration]; - OIDServiceConfiguration *unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + OIDServiceConfiguration *unarchived; + NSError *error; + NSData *data; + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:configuration + requiringSecureCoding:YES + error:&error]; + unarchived = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDServiceConfiguration class] + fromData:data + error:&error]; + } else { +#if !TARGET_OS_IOS + data = [NSKeyedArchiver archivedDataWithRootObject:configuration]; + unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif + } XCTAssertEqualObjects(configuration.authorizationEndpoint, unarchived.authorizationEndpoint, @""); XCTAssertEqualObjects(configuration.tokenEndpoint, unarchived.tokenEndpoint, @""); diff --git a/UnitTests/OIDServiceDiscoveryTests.m b/UnitTests/OIDServiceDiscoveryTests.m index 6d9faf799..f07d5e2a0 100644 --- a/UnitTests/OIDServiceDiscoveryTests.m +++ b/UnitTests/OIDServiceDiscoveryTests.m @@ -448,21 +448,25 @@ - (void)testSecureCoding { [[OIDServiceDiscovery alloc] initWithDictionary:serviceDiscoveryDictionary error:&error]; NSData *data; - if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { data = [NSKeyedArchiver archivedDataWithRootObject:discovery requiringSecureCoding:YES error:&error]; } else { +#if !TARGET_OS_IOS data = [NSKeyedArchiver archivedDataWithRootObject:discovery]; +#endif } OIDServiceDiscovery *unarchived; - if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { unarchived = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDServiceDiscovery class] fromData:data error:&error]; } else { +#if !TARGET_OS_IOS unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif } XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary); @@ -478,16 +482,18 @@ - (void)testSecureCodingDecodeOld { [[OIDServiceDiscoveryOldEncoding alloc] initWithDictionary:serviceDiscoveryDictionary error:&error]; NSData *data; - if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { data = [NSKeyedArchiver archivedDataWithRootObject:discovery requiringSecureCoding:YES error:&error]; } else { +#if !TARGET_OS_IOS data = [NSKeyedArchiver archivedDataWithRootObject:discovery]; +#endif } OIDServiceDiscovery *unarchived; - if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { NSSet *allowedClasses = [NSSet setWithArray:@[[OIDServiceDiscovery class], [NSDictionary class], [NSArray class], @@ -498,7 +504,9 @@ - (void)testSecureCodingDecodeOld { fromData:data error:&error]; } else { +#if !TARGET_OS_IOS unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif } XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary); @@ -514,16 +522,18 @@ - (void)testSecureCodingOldDecodeNew { [[OIDServiceDiscoveryOldDecoding alloc] initWithDictionary:serviceDiscoveryDictionary error:&error]; NSData *data; - if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { data = [NSKeyedArchiver archivedDataWithRootObject:discovery requiringSecureCoding:YES error:&error]; } else { +#if !TARGET_OS_IOS data = [NSKeyedArchiver archivedDataWithRootObject:discovery]; +#endif } OIDServiceDiscovery *unarchived; - if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { NSSet *allowedClasses = [NSSet setWithArray:@[[OIDServiceDiscoveryOldDecoding class], [NSDictionary class], [NSArray class], @@ -534,7 +544,9 @@ - (void)testSecureCodingOldDecodeNew { fromData:data error:&error]; } else { +#if !TARGET_OS_IOS unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif } XCTAssertNil(error); XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary); diff --git a/UnitTests/OIDTokenRequestTests.m b/UnitTests/OIDTokenRequestTests.m index c387bd809..59a2f8ae6 100644 --- a/UnitTests/OIDTokenRequestTests.m +++ b/UnitTests/OIDTokenRequestTests.m @@ -278,8 +278,22 @@ - (void)testSecureCoding { XCTAssertEqualObjects([urlRequest.allHTTPHeaderFields objectForKey:kTestAdditionalHeaderKey], kTestAdditionalHeaderValue); - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; - OIDTokenRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + OIDTokenRequest *requestCopy; + NSError *error; + NSData *data; + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:request + requiringSecureCoding:YES + error:&error]; + requestCopy = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDTokenRequest class] + fromData:data + error:&error]; + } else { +#if !TARGET_OS_IOS + data = [NSKeyedArchiver archivedDataWithRootObject:request]; + requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif + } // Not a full test of the configuration deserialization, but should be sufficient as a smoke test // to make sure the configuration IS actually getting serialized and deserialized in the diff --git a/UnitTests/OIDTokenResponseTests.m b/UnitTests/OIDTokenResponseTests.m index 0eb525ed0..cce020b73 100644 --- a/UnitTests/OIDTokenResponseTests.m +++ b/UnitTests/OIDTokenResponseTests.m @@ -175,8 +175,22 @@ - (void)testCopying { */ - (void)testSecureCoding { OIDTokenResponse *response = [[self class] testInstance]; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:response]; - OIDTokenResponse *responseCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + OIDTokenResponse *responseCopy; + NSError *error; + NSData *data; + if (@available(iOS 12.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + data = [NSKeyedArchiver archivedDataWithRootObject:response + requiringSecureCoding:YES + error:&error]; + responseCopy = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDTokenResponse class] + fromData:data + error:&error]; + } else { +#if !TARGET_OS_IOS + data = [NSKeyedArchiver archivedDataWithRootObject:response]; + responseCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; +#endif + } // Not a full test of the request deserialization, but should be sufficient as a smoke test // to make sure the request IS actually getting serialized and deserialized in the From 145104f5ea9d58ae21b60add007c33c1cc0c948e Mon Sep 17 00:00:00 2001 From: Brianna Morales <74382627+brnnmrls@users.noreply.github.com> Date: Tue, 22 Apr 2025 13:11:48 -0700 Subject: [PATCH 200/204] Prepare for 2.0.0 release. (#921) --- AppAuth.podspec | 2 +- CHANGELOG.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/AppAuth.podspec b/AppAuth.podspec index 7ad140d0b..17af8b6ce 100644 --- a/AppAuth.podspec +++ b/AppAuth.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AppAuth" - s.version = "1.7.6" + s.version = "2.0.0" s.summary = "AppAuth for iOS and macOS is a client SDK for communicating with OAuth 2.0 and OpenID Connect providers." s.description = <<-DESC diff --git a/CHANGELOG.md b/CHANGELOG.md index 13beb5467..d86a0f705 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.0.0 +- Raise minimum supported iOS version to iOS 12. ([#918](https://github.com/openid/AppAuth-iOS/pull/918)) +- Remove deprecated `[UIApplication openURL:]` method to compile with Xcode 16. ([#911](https://github.com/openid/AppAuth-iOS/pull/911)) + # 1.7.6 - Fix OIDExternalUserAgentIOSCustomBrowser on versions iOS 10+ ([#871](https://github.com/openid/AppAuth-iOS/pull/871)) - Update runners in tests.yml to macos-13. ([#886](https://github.com/openid/AppAuth-iOS/pull/886)) From cd09d44a559e39c38da17837f9a396ff7c4ba414 Mon Sep 17 00:00:00 2001 From: Worthing <115107835+w-goog@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:10:59 -0700 Subject: [PATCH 201/204] Migrate CI runners from macos-13 to macos-14, pin Xcode 16.2 (#951) --- .github/workflows/tests.yml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0fc7553c3..a14e3b85e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,19 +12,22 @@ on: jobs: xcode-project-test: - runs-on: macos-13 + runs-on: macos-14 strategy: matrix: + # Check the Github runner images when updating this matrix: https://github.com/actions/runner-images/tree/main/images/macos flags: [ - "-scheme AppAuth-iOS -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2' -sdk 'iphonesimulator17.2'", - "-scheme AppAuth-macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx14.2'", - "-scheme AppAuth_macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx14.2'", - "-scheme AppAuth-tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=17.2' -sdk 'appletvsimulator17.2'", - "-scheme AppAuth_tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=17.2' -sdk 'appletvsimulator17.2'", - "-scheme AppAuthTV -destination 'platform=tvOS Simulator,name=Apple TV,OS=17.2' -sdk 'appletvsimulator17.2'" + "-scheme AppAuth-iOS -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.2' -sdk 'iphonesimulator18.2'", + "-scheme AppAuth-macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx15.2'", + "-scheme AppAuth_macOS -destination 'platform=macOS,arch=x86_64' -sdk 'macosx15.2'", + "-scheme AppAuth-tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=18.2' -sdk 'appletvsimulator18.2'", + "-scheme AppAuth_tvOS -destination 'platform=tvOS Simulator,name=Apple TV,OS=18.2' -sdk 'appletvsimulator18.2'", + "-scheme AppAuthTV -destination 'platform=tvOS Simulator,name=Apple TV,OS=18.2' -sdk 'appletvsimulator18.2'" ] steps: - uses: actions/checkout@v3 + - name: Select Xcode + run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer - name: Run unit test targets run: | xcodebuild test \ @@ -32,7 +35,7 @@ jobs: ${{ matrix.flags }} pod-lib-lint: - runs-on: macos-13 + runs-on: macos-14 strategy: matrix: flags: [ @@ -50,7 +53,7 @@ jobs: run: pod lib lint --verbose ${{ matrix.flags }} spm-build-test: - runs-on: macos-13 + runs-on: macos-14 steps: - uses: actions/checkout@v3 - name: Build unit test target From 8510b9aa567553088ab30cdb3ddc45e25c26b9e1 Mon Sep 17 00:00:00 2001 From: Worthing <115107835+w-goog@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:19:31 -0700 Subject: [PATCH 202/204] Add .gemini/ directory to .gitignore. (#949) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9134faf61..5111bb517 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ Docs/ xcuserdata/ Carthage/ +.gemini/ From 6cd02b80c9c83608838500c4c52b285559754de3 Mon Sep 17 00:00:00 2001 From: Worthing <115107835+w-goog@users.noreply.github.com> Date: Mon, 4 May 2026 11:44:02 -0700 Subject: [PATCH 203/204] Create a new sample app that uses SPM (#952) --- CHANGELOG.md | 3 + Examples/Example-iOS_Swift-SPM/.gitignore | 1 + .../Config/Example.xcconfig | 14 + .../Example.xcodeproj/project.pbxproj | 375 ++++++++++++++ .../contents.xcworkspacedata | 7 + .../Example/AppDelegate.swift | 34 ++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ .../Example/Assets.xcassets/Contents.json | 6 + .../Example/AuthManager.swift | 459 ++++++++++++++++++ .../Example/ContentView.swift | 98 ++++ .../Example/ExampleApp.swift | 35 ++ .../Example-iOS_Swift-SPM/Example/Info.plist | 63 +++ Examples/Example-iOS_Swift-SPM/README.md | 48 ++ Examples/README.md | 2 + 15 files changed, 1191 insertions(+) create mode 100644 Examples/Example-iOS_Swift-SPM/.gitignore create mode 100644 Examples/Example-iOS_Swift-SPM/Config/Example.xcconfig create mode 100644 Examples/Example-iOS_Swift-SPM/Example.xcodeproj/project.pbxproj create mode 100644 Examples/Example-iOS_Swift-SPM/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Examples/Example-iOS_Swift-SPM/Example/AppDelegate.swift create mode 100644 Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/Contents.json create mode 100644 Examples/Example-iOS_Swift-SPM/Example/AuthManager.swift create mode 100644 Examples/Example-iOS_Swift-SPM/Example/ContentView.swift create mode 100644 Examples/Example-iOS_Swift-SPM/Example/ExampleApp.swift create mode 100644 Examples/Example-iOS_Swift-SPM/Example/Info.plist create mode 100644 Examples/Example-iOS_Swift-SPM/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index d86a0f705..0d7f6c2a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# Unreleased +- Add SwiftUI + Swift Package Manager sample app under `Examples/Example-iOS_Swift-SPM`. ([#952](https://github.com/openid/AppAuth-iOS/pull/952)) + # 2.0.0 - Raise minimum supported iOS version to iOS 12. ([#918](https://github.com/openid/AppAuth-iOS/pull/918)) - Remove deprecated `[UIApplication openURL:]` method to compile with Xcode 16. ([#911](https://github.com/openid/AppAuth-iOS/pull/911)) diff --git a/Examples/Example-iOS_Swift-SPM/.gitignore b/Examples/Example-iOS_Swift-SPM/.gitignore new file mode 100644 index 000000000..e3680cef6 --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/.gitignore @@ -0,0 +1 @@ +Config/Example.local.xcconfig diff --git a/Examples/Example-iOS_Swift-SPM/Config/Example.xcconfig b/Examples/Example-iOS_Swift-SPM/Config/Example.xcconfig new file mode 100644 index 000000000..0c33ad3b0 --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/Config/Example.xcconfig @@ -0,0 +1,14 @@ +// This file holds public placeholder defaults for OAuth configuration. +// Real OAuth client values belong in the sibling, gitignored Config/Example.local.xcconfig, +// which overrides these defaults via the optional-include directive below. + +OIDC_ISSUER = https:/$()/issuer.example.com +OIDC_CLIENT_ID = YOUR_CLIENT_ID +OIDC_REDIRECT_URI = com.example.app:/oauth2redirect/example-provider +OIDC_REDIRECT_URI_SCHEME = com.example.app + +// Code signing. Defaults to Automatic with no team. Override in +// Example.local.xcconfig if you need Manual signing with a specific provisioning profile. +CODE_SIGN_STYLE = Automatic + +#include? "Example.local.xcconfig" diff --git a/Examples/Example-iOS_Swift-SPM/Example.xcodeproj/project.pbxproj b/Examples/Example-iOS_Swift-SPM/Example.xcodeproj/project.pbxproj new file mode 100644 index 000000000..da3b94da0 --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/Example.xcodeproj/project.pbxproj @@ -0,0 +1,375 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 09BB0F002F91B157004C0D4B /* AppAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 09BB0E002F91B157004C0D4B /* AppAuth */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 09BB0B542F91B157004C0D4B /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 09BB0F012F91B157004C0D4B /* Config/Example.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config/Example.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 09BB0C002F91B157004C0D4B /* Exceptions for "Example" folder in "Example" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 09BB0B532F91B157004C0D4B /* Example */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 09BB0B562F91B157004C0D4B /* Example */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 09BB0C002F91B157004C0D4B /* Exceptions for "Example" folder in "Example" target */, + ); + path = Example; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 09BB0B512F91B157004C0D4B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 09BB0F002F91B157004C0D4B /* AppAuth in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 09BB0B4B2F91B157004C0D4B = { + isa = PBXGroup; + children = ( + 09BB0F022F91B157004C0D4B /* Config */, + 09BB0B562F91B157004C0D4B /* Example */, + 09BB0B552F91B157004C0D4B /* Products */, + ); + sourceTree = ""; + }; + 09BB0B552F91B157004C0D4B /* Products */ = { + isa = PBXGroup; + children = ( + 09BB0B542F91B157004C0D4B /* Example.app */, + ); + name = Products; + sourceTree = ""; + }; + 09BB0F022F91B157004C0D4B /* Config */ = { + isa = PBXGroup; + children = ( + 09BB0F012F91B157004C0D4B /* Config/Example.xcconfig */, + ); + name = Config; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 09BB0B532F91B157004C0D4B /* Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = 09BB0B5F2F91B158004C0D4B /* Build configuration list for PBXNativeTarget "Example" */; + buildPhases = ( + 09BB0B502F91B157004C0D4B /* Sources */, + 09BB0B512F91B157004C0D4B /* Frameworks */, + 09BB0B522F91B157004C0D4B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 09BB0B562F91B157004C0D4B /* Example */, + ); + name = Example; + packageProductDependencies = ( + 09BB0E002F91B157004C0D4B /* AppAuth */, + ); + productName = Example; + productReference = 09BB0B542F91B157004C0D4B /* Example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 09BB0B4C2F91B157004C0D4B /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2620; + LastUpgradeCheck = 2620; + TargetAttributes = { + 09BB0B532F91B157004C0D4B = { + CreatedOnToolsVersion = 26.2; + }; + }; + }; + buildConfigurationList = 09BB0B4F2F91B157004C0D4B /* Build configuration list for PBXProject "Example" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 09BB0B4B2F91B157004C0D4B; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 09BB0D002F91B157004C0D4B /* XCLocalSwiftPackageReference "../.." */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 09BB0B552F91B157004C0D4B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 09BB0B532F91B157004C0D4B /* Example */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 09BB0B522F91B157004C0D4B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 09BB0B502F91B157004C0D4B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 09BB0B5D2F91B158004C0D4B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 09BB0B5E2F91B158004C0D4B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 09BB0B602F91B158004C0D4B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 09BB0F012F91B157004C0D4B /* Config/Example.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = Example/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "AppAuth iOS Demo"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 09BB0B612F91B158004C0D4B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 09BB0F012F91B157004C0D4B /* Config/Example.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = Example/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "AppAuth iOS Demo"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 09BB0B4F2F91B157004C0D4B /* Build configuration list for PBXProject "Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 09BB0B5D2F91B158004C0D4B /* Debug */, + 09BB0B5E2F91B158004C0D4B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 09BB0B5F2F91B158004C0D4B /* Build configuration list for PBXNativeTarget "Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 09BB0B602F91B158004C0D4B /* Debug */, + 09BB0B612F91B158004C0D4B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 09BB0D002F91B157004C0D4B /* XCLocalSwiftPackageReference "../.." */ = { + isa = XCLocalSwiftPackageReference; + relativePath = ../..; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 09BB0E002F91B157004C0D4B /* AppAuth */ = { + isa = XCSwiftPackageProductDependency; + productName = AppAuth; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 09BB0B4C2F91B157004C0D4B /* Project object */; +} diff --git a/Examples/Example-iOS_Swift-SPM/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/Example-iOS_Swift-SPM/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/Example-iOS_Swift-SPM/Example/AppDelegate.swift b/Examples/Example-iOS_Swift-SPM/Example/AppDelegate.swift new file mode 100644 index 000000000..a1838b2cd --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/Example/AppDelegate.swift @@ -0,0 +1,34 @@ +// +// AppDelegate.swift +// +// Copyright (c) 2026 The AppAuth Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import AppAuth + +class AppDelegate: NSObject, UIApplicationDelegate { + + var currentAuthorizationFlow: OIDExternalUserAgentSession? + + func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { + if let authorizationFlow = self.currentAuthorizationFlow, authorizationFlow.resumeExternalUserAgentFlow(with: url) { + self.currentAuthorizationFlow = nil + return true + } + + return false + } +} diff --git a/Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..230588010 --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/Contents.json b/Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/Example/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Example-iOS_Swift-SPM/Example/AuthManager.swift b/Examples/Example-iOS_Swift-SPM/Example/AuthManager.swift new file mode 100644 index 000000000..b0e01ff84 --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/Example/AuthManager.swift @@ -0,0 +1,459 @@ +// +// AuthManager.swift +// +// Copyright (c) 2026 The AppAuth Authors. +// +// 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 AppAuth +import Combine +import SwiftUI +import UIKit + +typealias PostRegistrationCallback = (OIDServiceConfiguration?, OIDRegistrationResponse?) -> Void + +let kIssuer: String = { + guard let issuer = Bundle.main.object(forInfoDictionaryKey: "OIDCIssuer") as? String, + !issuer.isEmpty, + issuer != "https://issuer.example.com" else { + preconditionFailure("Please configure OIDC_ISSUER in Example.local.xcconfig") + } + return issuer +}() +let kClientID: String? = { + let clientID = Bundle.main.object(forInfoDictionaryKey: "OIDCClientID") as? String + if clientID == "YOUR_CLIENT_ID" || clientID?.isEmpty ?? true { + return nil + } + return clientID +}() +let kRedirectURI: String = { + guard let redirectURI = Bundle.main.object(forInfoDictionaryKey: "OIDCRedirectURI") as? String, + !redirectURI.isEmpty, + redirectURI != "com.example.app:/oauth2redirect/example-provider" else { + preconditionFailure("Please configure OIDC_REDIRECT_URI in Example.local.xcconfig") + } + return redirectURI +}() +let kAppAuthExampleAuthStateKey: String = "authState" + +final class AuthManager: NSObject, ObservableObject { + @Published private(set) var authState: OIDAuthState? + @Published private(set) var logText: String = "" + + var isAuthorized: Bool { authState?.isAuthorized ?? false } + var hasAuthorizationCode: Bool { authState?.lastAuthorizationResponse.authorizationCode != nil && authState?.lastTokenResponse == nil } + var hasAuthState: Bool { authState != nil } + + weak var appDelegate: AppDelegate? + + override init() { + super.init() + self.validateOAuthConfiguration() + self.loadState() + } + + // MARK: Public Methods + + func validateOAuthConfiguration() { + guard let urlTypes = Bundle.main.object(forInfoDictionaryKey: "CFBundleURLTypes") as? [[String: Any]], + let urlSchemes = urlTypes.first?["CFBundleURLSchemes"] as? [String], + let urlScheme = urlSchemes.first else { + assertionFailure("CFBundleURLSchemes not configured") + return + } + + assert(urlScheme != "com.example.app", "Register your OIDC Redirect URI scheme in Example.local.xcconfig (OIDC_REDIRECT_URI_SCHEME).") + assert(kIssuer != "https://issuer.example.com", "Register your OIDC Issuer in Example.local.xcconfig (OIDC_ISSUER).") + } + + func authWithAutoCodeExchange() { + guard let issuer = URL(string: kIssuer) else { + self.logMessage("Error creating URL for : \(kIssuer)") + return + } + + self.logMessage("Fetching configuration for issuer: \(issuer)") + + // discovers endpoints + OIDAuthorizationService.discoverConfiguration(forIssuer: issuer) { configuration, error in + guard let config = configuration else { + self.logMessage("Error retrieving discovery document: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + return + } + + self.logMessage("Got configuration: \(config)") + + // If kClientID was provided, use it; otherwise perform RFC 7591 dynamic client registration. + if let clientId = kClientID { + self.doAuthWithAutoCodeExchange(configuration: config, clientID: clientId, clientSecret: nil) + } else { + self.doClientRegistration(configuration: config) { configuration, response in + guard let configuration = configuration, let clientID = response?.clientID else { + self.logMessage("Error retrieving configuration OR clientID") + return + } + + self.doAuthWithAutoCodeExchange(configuration: configuration, + clientID: clientID, + clientSecret: response?.clientSecret) + } + } + } + } + + func authNoCodeExchange() { + guard let issuer = URL(string: kIssuer) else { + self.logMessage("Error creating URL for : \(kIssuer)") + return + } + + self.logMessage("Fetching configuration for issuer: \(issuer)") + + OIDAuthorizationService.discoverConfiguration(forIssuer: issuer) { configuration, error in + if let error = error { + self.logMessage("Error retrieving discovery document: \(error.localizedDescription)") + return + } + + guard let configuration = configuration else { + self.logMessage("Error retrieving discovery document. Error & Configuration both are NIL!") + return + } + + self.logMessage("Got configuration: \(configuration)") + + // If kClientID was provided, use it; otherwise perform RFC 7591 dynamic client registration. + if let clientId = kClientID { + self.doAuthWithoutCodeExchange(configuration: configuration, clientID: clientId, clientSecret: nil) + } else { + self.doClientRegistration(configuration: configuration) { configuration, response in + guard let configuration = configuration, let response = response else { + return + } + + self.doAuthWithoutCodeExchange(configuration: configuration, + clientID: response.clientID, + clientSecret: response.clientSecret) + } + } + } + } + + func codeExchange() { + guard let tokenExchangeRequest = self.authState?.lastAuthorizationResponse.tokenExchangeRequest() else { + self.logMessage("Error creating authorization code exchange request") + return + } + + self.logMessage("Performing authorization code exchange with request \(tokenExchangeRequest)") + + OIDAuthorizationService.perform(tokenExchangeRequest) { response, error in + if let tokenResponse = response { + self.logMessage("Received token response with accessToken: \(tokenResponse.accessToken ?? "DEFAULT_TOKEN")") + } else { + self.logMessage("Token exchange error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + } + self.authState?.update(with: response, error: error) + } + } + + func userinfo() { + guard let userinfoEndpoint = self.authState?.lastAuthorizationResponse.request.configuration.discoveryDocument?.userinfoEndpoint else { + self.logMessage("Userinfo endpoint not declared in discovery document") + return + } + + self.logMessage("Performing userinfo request") + + let currentAccessToken: String? = self.authState?.lastTokenResponse?.accessToken + + self.authState?.performAction() { (accessToken, idToken, error) in + if error != nil { + self.logMessage("Error fetching fresh tokens: \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let accessToken = accessToken else { + self.logMessage("Error getting accessToken") + return + } + + if currentAccessToken != accessToken { + self.logMessage("Access token was refreshed automatically (\(currentAccessToken ?? "CURRENT_ACCESS_TOKEN") to \(accessToken))") + } else { + self.logMessage("Access token was fresh and not updated \(accessToken)") + } + + var urlRequest = URLRequest(url: userinfoEndpoint) + urlRequest.allHTTPHeaderFields = ["Authorization":"Bearer \(accessToken)"] + + let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in + DispatchQueue.main.async { + guard error == nil else { + self.logMessage("HTTP request failed \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let response = response as? HTTPURLResponse else { + self.logMessage("Non-HTTP response") + return + } + + guard let data = data else { + self.logMessage("HTTP response data is empty") + return + } + + var json: [AnyHashable: Any]? + + do { + json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + } catch { + self.logMessage("JSON Serialization Error") + } + + if response.statusCode != 200 { + // server replied with an error + let responseText: String? = String(data: data, encoding: String.Encoding.utf8) + + if response.statusCode == 401 { + // "401 Unauthorized" generally indicates there is an issue with the authorization + // grant. Puts OIDAuthState into an error state. + let oauthError = OIDErrorUtilities.resourceServerAuthorizationError(withCode: 0, + errorResponse: json, + underlyingError: error) + self.authState?.update(withAuthorizationError: oauthError) + self.logMessage("Authorization Error (\(oauthError)). Response: \(responseText ?? "RESPONSE_TEXT")") + } else { + self.logMessage("HTTP: \(response.statusCode), Response: \(responseText ?? "RESPONSE_TEXT")") + } + + return + } + + if let json = json { + self.logMessage("Success: \(json)") + } + } + } + + task.resume() + } + } + + func clearAuthState() { + setAuthState(nil) + } + + func clearLogs() { + DispatchQueue.main.async { + self.logText = "" + } + } + + // MARK: Private Methods + + private func doClientRegistration(configuration: OIDServiceConfiguration, callback: @escaping PostRegistrationCallback) { + guard let redirectURI = URL(string: kRedirectURI) else { + self.logMessage("Error creating URL for : \(kRedirectURI)") + return + } + + let request: OIDRegistrationRequest = OIDRegistrationRequest(configuration: configuration, + redirectURIs: [redirectURI], + responseTypes: nil, + grantTypes: nil, + subjectType: nil, + tokenEndpointAuthMethod: "client_secret_post", + additionalParameters: nil) + + // performs registration request + self.logMessage("Initiating registration request") + + OIDAuthorizationService.perform(request) { response, error in + if let regResponse = response { + self.setAuthState(OIDAuthState(registrationResponse: regResponse)) + self.logMessage("Got registration response: \(regResponse)") + callback(configuration, regResponse) + } else { + self.logMessage("Registration error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + } + } + } + + private func doAuthWithAutoCodeExchange(configuration: OIDServiceConfiguration, clientID: String, clientSecret: String?) { + guard let redirectURI = URL(string: kRedirectURI) else { + self.logMessage("Error creating URL for : \(kRedirectURI)") + return + } + + guard let appDelegate = self.appDelegate else { + self.logMessage("Error accessing AppDelegate") + return + } + + guard let presentingVC = self.presentingViewController() else { + return + } + + // builds authentication request + let request = OIDAuthorizationRequest(configuration: configuration, + clientId: clientID, + clientSecret: clientSecret, + scopes: [OIDScopeOpenID, OIDScopeProfile], + redirectURL: redirectURI, + responseType: OIDResponseTypeCode, + additionalParameters: nil) + + // performs authentication request + logMessage("Initiating authorization request with scope: \(request.scope ?? "DEFAULT_SCOPE")") + + appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: presentingVC) { authState, error in + if let authState = authState { + self.setAuthState(authState) + self.logMessage("Got authorization tokens. Access token: \(authState.lastTokenResponse?.accessToken ?? "DEFAULT_TOKEN")") + } else { + self.logMessage("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + } + } + } + + private func doAuthWithoutCodeExchange(configuration: OIDServiceConfiguration, clientID: String, clientSecret: String?) { + guard let redirectURI = URL(string: kRedirectURI) else { + self.logMessage("Error creating URL for : \(kRedirectURI)") + return + } + + guard let appDelegate = self.appDelegate else { + self.logMessage("Error accessing AppDelegate") + return + } + + guard let presentingVC = self.presentingViewController() else { + return + } + + // builds authentication request + let request = OIDAuthorizationRequest(configuration: configuration, + clientId: clientID, + clientSecret: clientSecret, + scopes: [OIDScopeOpenID, OIDScopeProfile], + redirectURL: redirectURI, + responseType: OIDResponseTypeCode, + additionalParameters: nil) + + // performs authentication request + logMessage("Initiating authorization request with scope: \(request.scope ?? "DEFAULT_SCOPE")") + + appDelegate.currentAuthorizationFlow = OIDAuthorizationService.present(request, presenting: presentingVC) { (response, error) in + if let response = response { + let authState = OIDAuthState(authorizationResponse: response) + self.setAuthState(authState) + self.logMessage("Authorization response with code: \(response.authorizationCode ?? "DEFAULT_CODE")") + // could just call [self tokenExchange:nil] directly, but will let the user initiate it. + } else { + self.logMessage("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + } + } + } + + private func setAuthState(_ authState: OIDAuthState?) { + if (self.authState == authState) { + return + } + self.authState = authState + self.authState?.stateChangeDelegate = self + self.authState?.errorDelegate = self + self.stateChanged() + } + + private func stateChanged() { + self.saveState() + } + + private func saveState() { + var data: Data? = nil + + if let authState = self.authState { + do { + data = try NSKeyedArchiver.archivedData(withRootObject: authState, requiringSecureCoding: true) + } catch { + logMessage("Error archiving authState: \(error.localizedDescription)") + return + } + } + + UserDefaults.standard.set(data, forKey: kAppAuthExampleAuthStateKey) + } + + private func loadState() { + guard let data = UserDefaults.standard.data(forKey: kAppAuthExampleAuthStateKey) else { + return + } + + do { + if let authState = try NSKeyedUnarchiver.unarchivedObject(ofClass: OIDAuthState.self, from: data) { + self.setAuthState(authState) + } + } catch { + logMessage("Error unarchiving authState: \(error.localizedDescription)") + } + } + + private func logMessage(_ message: String?) { + guard let message = message else { + return + } + + print(message) + + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "hh:mm:ss" + let dateString = dateFormatter.string(from: Date()) + + // appends to output log + DispatchQueue.main.async { + let logText = "\(self.logText)\n\(dateString): \(message)" + self.logText = logText + } + } + + private func presentingViewController() -> UIViewController? { + let viewController = UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .first?.windows + .first(where: { $0.isKeyWindow })? + .rootViewController + if viewController == nil { + logMessage("Error: no presenting view controller available") + } + return viewController + } +} + +// MARK: OIDAuthState Delegate + +extension AuthManager: OIDAuthStateChangeDelegate, OIDAuthStateErrorDelegate { + func didChange(_ state: OIDAuthState) { + self.stateChanged() + } + + func authState(_ state: OIDAuthState, didEncounterAuthorizationError error: Error) { + self.logMessage("Received authorization error: \(error)") + } +} diff --git a/Examples/Example-iOS_Swift-SPM/Example/ContentView.swift b/Examples/Example-iOS_Swift-SPM/Example/ContentView.swift new file mode 100644 index 000000000..7ddcbcac4 --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/Example/ContentView.swift @@ -0,0 +1,98 @@ +// +// ContentView.swift +// +// Copyright (c) 2026 The AppAuth Authors. +// +// 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 SwiftUI + +struct ContentView: View { + @EnvironmentObject var authManager: AuthManager + + var body: some View { + NavigationStack { + VStack(spacing: 12) { + HStack { + Button(authManager.hasAuthState ? "Re-Auth (Auto)" : "Auto") { + authManager.authWithAutoCodeExchange() + } + .buttonStyle(.borderedProminent) + .frame(maxWidth: .infinity) + + Button(authManager.hasAuthState ? "Re-Auth (Manual)" : "Manual") { + authManager.authNoCodeExchange() + } + .buttonStyle(.borderedProminent) + .frame(maxWidth: .infinity) + } + + HStack { + Button("Code Exchange") { + authManager.codeExchange() + } + .buttonStyle(.borderedProminent) + .disabled(!authManager.hasAuthorizationCode) + .frame(maxWidth: .infinity) + + Button("User Info") { + authManager.userinfo() + } + .buttonStyle(.borderedProminent) + .disabled(!authManager.isAuthorized) + .frame(maxWidth: .infinity) + } + + ScrollViewReader { proxy in + ScrollView { + Text(authManager.logText) + .frame(maxWidth: .infinity, alignment: .leading) + .font(.system(.caption, design: .monospaced)) + .textSelection(.enabled) + .padding(8) + .id("bottom") + } + .overlay( + RoundedRectangle(cornerRadius: 6) + .stroke(Color.secondary.opacity(0.4)) + ) + .onChange(of: authManager.logText) { _ in + proxy.scrollTo("bottom", anchor: .bottom) + } + } + } + .padding() + .navigationTitle("AppAuth Example") + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + Menu { + Button(role: .destructive) { + authManager.clearAuthState() + } label: { + Text("Clear OAuth State") + } + + Button { + authManager.clearLogs() + } label: { + Text("Clear Logs") + } + } label: { + Image(systemName: "trash") + } + } + } + } + } +} diff --git a/Examples/Example-iOS_Swift-SPM/Example/ExampleApp.swift b/Examples/Example-iOS_Swift-SPM/Example/ExampleApp.swift new file mode 100644 index 000000000..462e81fa2 --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/Example/ExampleApp.swift @@ -0,0 +1,35 @@ +// +// ExampleApp.swift +// +// Copyright (c) 2026 The AppAuth Authors. +// +// 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 SwiftUI + +@main +struct ExampleApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate + @StateObject private var authManager = AuthManager() + + var body: some Scene { + WindowGroup { + ContentView() + .environmentObject(authManager) + .onAppear { + authManager.appDelegate = appDelegate + } + } + } +} diff --git a/Examples/Example-iOS_Swift-SPM/Example/Info.plist b/Examples/Example-iOS_Swift-SPM/Example/Info.plist new file mode 100644 index 000000000..aaf558068 --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/Example/Info.plist @@ -0,0 +1,63 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + $(OIDC_REDIRECT_URI_SCHEME) + + + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSRequiresIPhoneOS + + OIDCClientID + $(OIDC_CLIENT_ID) + OIDCIssuer + $(OIDC_ISSUER) + OIDCRedirectURI + $(OIDC_REDIRECT_URI) + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + + UILaunchScreen + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Examples/Example-iOS_Swift-SPM/README.md b/Examples/Example-iOS_Swift-SPM/README.md new file mode 100644 index 000000000..403a93146 --- /dev/null +++ b/Examples/Example-iOS_Swift-SPM/README.md @@ -0,0 +1,48 @@ +# Example Project (SwiftUI + Swift Package Manager) + +## Setup & Open the Project + +This sample uses the local AppAuth Swift Package via a relative path (`../..`), so Xcode resolves it automatically. + +Just open `Example.xcodeproj` to get started, and complete the configuration. + +## Configuration + +The example doesn't work out of the box — you need to configure it with your own OpenID Connect client. + +### Information You'll Need + +* Issuer +* Client ID +* Redirect URI + +How to get this information varies by IdP, but we have [instructions](../README.md#openid-certified-providers) for some OpenID Certified providers. + +### Configure the Example + +The sample reads these values (Issuer, Client ID, Redirect URI) from an xcconfig file. Create your local override file by copying the committed defaults: + + cp Config/Example.xcconfig Config/Example.local.xcconfig + +Then edit `Config/Example.local.xcconfig` and set: + + OIDC_ISSUER = + OIDC_CLIENT_ID = + OIDC_REDIRECT_URI = + OIDC_REDIRECT_URI_SCHEME = + +The scheme is everything before the colon (:) of your redirect URI. For example, if the redirect URI is `com.example.app:/oauth2redirect/example-provider`, the scheme is `com.example.app`. + +Note that the local version you create, `Config/Example.local.xcconfig`, is gitignored. + +The same file can also override code-signing. By default the committed xcconfig sets CODE_SIGN_STYLE = Automatic, which causes Xcode to prompt for your team on first build — the same experience as the sibling samples. To use Manual signing with a specific provisioning profile, add these lines to Config/Example.local.xcconfig: + + CODE_SIGN_STYLE = Manual + DEVELOPMENT_TEAM = + PROVISIONING_PROFILE_SPECIFIER = + +Note: Xcode may cache Info.plist substitutions — after editing the xcconfig, run **Product > Clean Build Folder**. + +### Running the Example + +Now your example should be ready to run. diff --git a/Examples/README.md b/Examples/README.md index 820c0d5bb..accbc23a8 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -10,6 +10,8 @@ Each example has docs on how to configure: * [Example for iOS (Objective-C)](Example-iOS_ObjC/README.md) * [Example for iOS w/ Carthage (Objective-C)](Example-iOS_ObjC-Carthage/README.md) +* [Example for iOS w/ Carthage (Swift)](Example-iOS_Swift-Carthage/README.md) +* [Example for iOS w/ SPM (SwiftUI)](Example-iOS_Swift-SPM/README.md) * [Example for macOS](Example-macOS/README.md) * [Example for tvOS](Example-tvOS/README.md) From 77e32a8bbfb973966692854c974c7599973a0f59 Mon Sep 17 00:00:00 2001 From: Worthing <115107835+w-goog@users.noreply.github.com> Date: Mon, 4 May 2026 13:43:24 -0700 Subject: [PATCH 204/204] [#574] Remove external browser fallback (#954) --- CHANGELOG.md | 1 + README.md | 4 +--- Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h | 9 +++++---- Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m | 9 +++------ 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d7f6c2a8..8be94c79a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased - Add SwiftUI + Swift Package Manager sample app under `Examples/Example-iOS_Swift-SPM`. ([#952](https://github.com/openid/AppAuth-iOS/pull/952)) +- Removed external browser (Safari) fallback from `OIDExternalUserAgentIOS`. If `ASWebAuthenticationSession` fails to start (e.g., Guided Access is enabled), the authorization flow now fails with an error instead of opening an external browser. # 2.0.0 - Raise minimum supported iOS version to iOS 12. ([#918](https://github.com/openid/AppAuth-iOS/pull/918)) diff --git a/README.md b/README.md index 540feda72..c9f44a41c 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,7 @@ For tvOS, AppAuth implements [OAuth 2.0 Device Authorization Grant AppAuth supports iOS 12 and above. -iOS 9+ uses the in-app browser tab pattern -(via `SFSafariViewController`), and falls back to the system browser (mobile -Safari) on earlier versions. +Authentication is performed using `ASWebAuthenticationSession`. #### Authorization Server Requirements diff --git a/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h index 4ab6c7452..c7916a52e 100644 --- a/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h +++ b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h @@ -41,8 +41,8 @@ API_UNAVAILABLE(macCatalyst) /*! @brief The designated initializer. @param presentingViewController The view controller from which to present the authentication UI. @discussion The specific authentication UI used depends on the iOS version and accessibility - options. iOS 12+ uses @c ASWebAuthenticationSession (unless Guided Access is on), - otherwise local browser is used. + options. Uses @c ASWebAuthenticationSession. If Guided Access is enabled or the session + cannot be started, the method returns NO and the authorization flow fails with an error. */ - (nullable instancetype)initWithPresentingViewController: (UIViewController *)presentingViewController @@ -52,8 +52,9 @@ API_UNAVAILABLE(macCatalyst) @param presentingViewController The view controller from which to present the browser. @param prefersEphemeralSession Whether the caller prefers to use a private authentication session. See @c ASWebAuthenticationSession.prefersEphemeralWebBrowserSession for more. - @discussion Authentication is performed with @c ASWebAuthenticationSession (unless Guided Access - is on), setting the ephemerality based on the argument. + @discussion Authentication is performed with @c ASWebAuthenticationSession, setting the + ephemerality based on the argument. If Guided Access is enabled or the session cannot + be started, the method returns NO and the authorization flow fails with an error. */ - (nullable instancetype)initWithPresentingViewController: (UIViewController *)presentingViewController diff --git a/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m index 7a3fa2278..95acdc16c 100644 --- a/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m +++ b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m @@ -133,12 +133,9 @@ - (BOOL)presentExternalUserAgentRequest:(id)request openedUserAgent = [authenticationVC start]; } } - // If all else failed use the local browser. - if (!openedUserAgent){ - [[UIApplication sharedApplication] openURL:requestURL - options:@{} - completionHandler:nil]; - openedUserAgent = YES; + if (!openedUserAgent) { + [self cleanUp]; + return NO; } return openedUserAgent;