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" diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a598b7fbf..4603ea210 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,19 +1,18 @@ --- -name: Bug report -about: Create a report to help us improve - +name: Bug Report +about: Submit a bug report if something isn't working as expected. +title: "" +labels: bug, triage +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. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' -2. Click on '....' +2. Tap on '....' 3. Scroll down to '....' 4. See error @@ -23,16 +22,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. 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 diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 5ad6b46bf..ab9e65fef 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 - +name: Feature Request +about: Make a feature request if you have a suggestion for something new. +title: "" +labels: enhancement, triage +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 [...] +**Is your feature request related to a problem you're having? Please describe.** +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. diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..523bed57d --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,61 @@ +name: tests + +on: + push: + branches: + - master + pull_request: + branches: + - master + workflow_dispatch: + +jobs: + + xcode-project-test: + runs-on: macos-12 + 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'" + ] + steps: + - uses: actions/checkout@v3 + - name: Run unit test targets + run: | + xcodebuild test \ + -project AppAuth.xcodeproj \ + ${{ matrix.flags }} + + pod-lib-lint: + runs-on: macos-12 + strategy: + matrix: + flags: [ + '', + '--use-libraries', + '--use-static-frameworks' + ] + steps: + - uses: actions/checkout@v3 + - 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-11 + steps: + - uses: actions/checkout@v2 + - 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 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.podspec b/AppAuth.podspec index 819560619..bfffe6cda 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.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 @@ -31,30 +31,51 @@ 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" } + 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" s.source = { :git => "https://github.com/openid/AppAuth-iOS.git", :tag => s.version } s.requires_arc = true + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + } + # 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 = "Sources/AppAuthCore.h", "Sources/AppAuthCore/*.{h,m}" + core.resource_bundles = { + "AppAuthCore_Privacy" => ["Sources/AppAuthCore/Resources/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.source_files = "Source/*.{h,m}", "Source/AppAuthCore/*.{h,m}", "Source/AppAuth/*.{h,m}" + externalUserAgent.dependency 'AppAuth/Core' + + externalUserAgent.source_files = "Sources/AppAuth.h", "Sources/AppAuth/*.{h,m}" # iOS - externalUserAgent.ios.source_files = "Source/AppAuth/iOS/**/*.{h,m}" - externalUserAgent.ios.deployment_target = "7.0" + 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.deployment_target = '10.9' + 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 = "Sources/AppAuthTV.h", "Sources/AppAuthTV/*.{h,m}" + tv.dependency 'AppAuth/Core' + end + + s.default_subspecs = 'Core', 'ExternalUserAgent' end diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index bac5ad53b..8d770d93c 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -13,6 +13,91 @@ 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; @@ -384,9 +469,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 */; }; @@ -464,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 */; }; @@ -510,6 +598,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 */; }; @@ -520,6 +611,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 */; @@ -605,6 +703,27 @@ 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; @@ -690,8 +809,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 = ""; }; @@ -716,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 = ""; }; @@ -724,6 +842,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 = ""; }; @@ -737,6 +857,21 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 2D8111F224C0FD4C00984DA7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D8111FA24C0FD4C00984DA7 /* AppAuthTV.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D9385B424B37CAD009A12D7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 340DAE4B1D58216A00EC285B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -862,6 +997,46 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 2D47AAD7249A86E30059B5A4 /* AppAuthTV */ = { + isa = PBXGroup; + children = ( + 7322C49B2BA20BFA00DF9B2F /* Resources */, + 2D47AAD9249A87010059B5A4 /* OIDTVAuthorizationRequest.h */, + 2D47AADD249A87010059B5A4 /* OIDTVAuthorizationRequest.m */, + 2D47AADC249A87010059B5A4 /* OIDTVAuthorizationResponse.h */, + 2D47AAD8249A87010059B5A4 /* OIDTVAuthorizationResponse.m */, + 2D47AADF249A87020059B5A4 /* OIDTVAuthorizationService.h */, + 2D47AAE0249A87020059B5A4 /* OIDTVAuthorizationService.m */, + 2D47AADE249A87020059B5A4 /* OIDTVServiceConfiguration.h */, + 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */, + 2DEB065424CA1D9300DF47E7 /* OIDTVTokenRequest.h */, + 2DEB065524CA1D9300DF47E7 /* OIDTVTokenRequest.m */, + ); + path = AppAuthTV; + sourceTree = ""; + }; + 2D52A08D24C24C260022E402 /* AppAuthTV */ = { + isa = PBXGroup; + children = ( + 2D81120324C1036700984DA7 /* OIDTVAuthorizationRequestTests.h */, + 2D81120024C1036700984DA7 /* OIDTVAuthorizationRequestTests.m */, + 2DA8D82524C6190400FDFB34 /* OIDTVAuthorizationResponseTests.h */, + 2DA8D82424C6190300FDFB34 /* OIDTVAuthorizationResponseTests.m */, + 2DEB065F24CF5CDF00DF47E7 /* OIDTVTokenRequestTests.h */, + 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */, + ); + path = AppAuthTV; + sourceTree = ""; + }; + 2D9385B824B37CAD009A12D7 /* TVFramework */ = { + isa = PBXGroup; + children = ( + 2D9385B924B37CAD009A12D7 /* AppAuthTV.h */, + 2D9385BA24B37CAD009A12D7 /* Info.plist */, + ); + path = TVFramework; + sourceTree = ""; + }; 340DAE241D581FE700EC285B /* macOS */ = { isa = PBXGroup; children = ( @@ -883,7 +1058,7 @@ children = ( 341742291C5D84D0000EF209 /* Frameworks */, 341741FB1C5D82D3000EF209 /* UnitTests */, - 341741AE1C5D8243000EF209 /* Source */, + 341741AE1C5D8243000EF209 /* Sources */, 340E737D1C5D819B0076B1F6 /* Products */, ); indentWidth = 2; @@ -909,26 +1084,32 @@ 343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */, 342F42C32177B1FC00574F24 /* AppAuthCore.framework */, 348970972177B3B000ABEED4 /* AppAuthCoreTests.xctest */, + 2D9385B724B37CAD009A12D7 /* AppAuthTV.framework */, + 2D8111F524C0FD4C00984DA7 /* AppAuthTVTests.xctest */, ); name = Products; sourceTree = ""; }; - 341741AE1C5D8243000EF209 /* Source */ = { + 341741AE1C5D8243000EF209 /* Sources */ = { isa = PBXGroup; children = ( 348970992178F40600ABEED4 /* CoreFramework */, 343AAA4C1E8345B600F9D36E /* Framework */, + 2D9385B824B37CAD009A12D7 /* TVFramework */, 8A9B9D5E24561EC40055353E /* AppAuthCore */, 8A9B9D632456227D0055353E /* AppAuth */, + 2D47AAD7249A86E30059B5A4 /* AppAuthTV */, 341741AF1C5D8243000EF209 /* AppAuth.h */, 3489709E21791B0C00ABEED4 /* AppAuthCore.h */, + 2D47AADB249A87010059B5A4 /* AppAuthTV.h */, ); - path = Source; + path = Sources; sourceTree = ""; }; 341741FB1C5D82D3000EF209 /* UnitTests */ = { isa = PBXGroup; children = ( + 2D52A08D24C24C260022E402 /* AppAuthTV */, 341742231C5D8317000EF209 /* UnitTestsInfo.plist */, 341742001C5D82D3000EF209 /* OIDAuthorizationRequestTests.h */, 341742011C5D82D3000EF209 /* OIDAuthorizationRequestTests.m */, @@ -1003,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 */, @@ -1067,6 +1271,7 @@ 8A9B9D632456227D0055353E /* AppAuth */ = { isa = PBXGroup; children = ( + 7322C49A2BA20BEF00DF9B2F /* Resources */, 340DAE241D581FE700EC285B /* macOS */, F6F60FAF1D2BFEF000325CB3 /* iOS */, ); @@ -1076,10 +1281,10 @@ F6F60FAF1D2BFEF000325CB3 /* iOS */ = { isa = PBXGroup; children = ( + C14E3B6627E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.h */, + C14E3B6727E3BEFB00CF05A9 /* OIDExternalUserAgentIOSCustomBrowser.m */, 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 +1298,45 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 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 */, + 2DEB065624CA1D9300DF47E7 /* OIDTVTokenRequest.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; @@ -1144,7 +1388,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 */, @@ -1171,6 +1414,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; @@ -1285,6 +1529,42 @@ /* 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"; + }; + 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" */; @@ -1578,6 +1858,14 @@ LastUpgradeCheck = 1000; ORGANIZATIONNAME = "OpenID Foundation"; TargetAttributes = { + 2D8111F424C0FD4C00984DA7 = { + CreatedOnToolsVersion = 11.6; + }; + 2D9385B624B37CAD009A12D7 = { + CreatedOnToolsVersion = 11.5; + DevelopmentTeam = AUX79W8H33; + ProvisioningStyle = Automatic; + }; 340DAE4D1D58216A00EC285B = { CreatedOnToolsVersion = 7.3.1; }; @@ -1663,11 +1951,28 @@ 343AAAC91E8348AA00F9D36E /* AppAuth_macOSTests */, 342F42842177B1FC00574F24 /* AppAuthCore */, 3489707D2177B3B000ABEED4 /* AppAuthCoreTests */, + 2D9385B624B37CAD009A12D7 /* AppAuthTV */, + 2D8111F424C0FD4C00984DA7 /* AppAuthTVTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 2D8111F324C0FD4C00984DA7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D9385B524B37CAD009A12D7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 73F574392B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 341741EE1C5D8283000EF209 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1693,6 +1998,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73F574382B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1700,6 +2006,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73F574342B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1714,6 +2021,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73F574352B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1721,6 +2029,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73F574362B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1735,6 +2044,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73F574372B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1755,6 +2065,68 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 2D8111F124C0FD4C00984DA7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + 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 */, + 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; + }; + 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 */, + 2DEB065724CA1D9300DF47E7 /* OIDTVTokenRequest.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; @@ -1795,6 +2167,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 */, @@ -1812,7 +2185,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 */, @@ -1916,6 +2288,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 */, @@ -1924,8 +2297,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; }; @@ -1968,6 +2344,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 */, @@ -1980,7 +2357,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 */, @@ -2223,6 +2599,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 */; @@ -2261,6 +2642,103 @@ /* 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 = 9.0; + }; + 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 = 9.0; + }; + 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 = 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; + 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 = 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; + 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 = { @@ -2337,12 +2815,14 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + 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; }; @@ -2393,13 +2873,15 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + 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; }; @@ -2425,7 +2907,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)"; @@ -2442,7 +2924,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)"; @@ -2495,7 +2977,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; }; @@ -2510,7 +2992,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; }; @@ -2524,7 +3006,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; - TVOS_DEPLOYMENT_TARGET = 10.1; + TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Debug; }; @@ -2538,7 +3020,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; - TVOS_DEPLOYMENT_TARGET = 10.1; + TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Release; }; @@ -2554,9 +3036,9 @@ 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 = 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)"; @@ -2580,9 +3062,9 @@ 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 = 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)"; @@ -2605,9 +3087,9 @@ 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 = 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; @@ -2630,9 +3112,9 @@ 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 = 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; @@ -2651,7 +3133,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)"; @@ -2665,7 +3147,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)"; @@ -2684,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"; @@ -2710,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"; @@ -2735,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"; @@ -2760,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"; @@ -2785,7 +3267,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; }; @@ -2800,7 +3282,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; }; @@ -2817,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"; @@ -2842,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"; @@ -2895,7 +3377,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; - WATCHOS_DEPLOYMENT_TARGET = 3.1; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Debug; }; @@ -2908,7 +3390,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; - WATCHOS_DEPLOYMENT_TARGET = 3.1; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Release; }; @@ -2919,7 +3401,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)"; @@ -2933,7 +3415,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)"; @@ -2943,6 +3425,24 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 2D8111FF24C0FD4D00984DA7 /* Build configuration list for PBXNativeTarget "AppAuthTVTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D8111FD24C0FD4C00984DA7 /* Debug */, + 2D8111FE24C0FD4C00984DA7 /* Release */, + ); + 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/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 @@ - - - - - - - - + + + + @@ -39,17 +48,6 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ - - - - - - - - currentAuthorizationFlow; ``` -#### Implementation of `-(BOOL)application:openURL:options:` +### Implementation of `-(BOOL)application:openURL:options:` Change ```objc if ([_currentAuthorizationFlow resumeAuthorizationFlowWithURL:url]) { @@ -188,14 +222,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). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 51537d3a0..b319f9ddf 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://google.github.io/styleguide/objcguide.html). Please review your own code for adherence to the standard. ## Pull Request Reviews 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. diff --git a/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj b/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj index 9318b369a..839e13e5b 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj +++ b/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/project.pbxproj @@ -7,6 +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, ); }; }; 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 */; }; @@ -17,7 +22,40 @@ 5FEA25251E75C17E00C2D71B /* AppAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5FEA25241E75C17E00C2D71B /* AppAuth.framework */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 06462460205ED68000072191 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 346E91611C29D42800D3620B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 06462454205ED68000072191; + remoteInfo = Example_Extension; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 06462462205ED68000072191 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 06462463205ED68000072191 /* Example_Extension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* 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 = ""; }; + 0646245A205ED68000072191 /* TodayViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TodayViewController.m; sourceTree = ""; }; + 0646245D205ED68000072191 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + 0646245F205ED68000072191 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 06462467205EDB5400072191 /* Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Example.entitlements; sourceTree = ""; }; + 06462468205EDB9900072191 /* Example_Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Example_Extension.entitlements; sourceTree = ""; }; 346E91691C29D42800D3620B /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 346E916D1C29D42800D3620B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 346E916F1C29D42800D3620B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -33,6 +71,15 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 06462452205ED68000072191 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 06375C8521A4E46500338E3F /* AppAuthCore.framework in Frameworks */, + 06462457205ED68000072191 /* NotificationCenter.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 346E91661C29D42800D3620B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -45,11 +92,25 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 06462458205ED68000072191 /* Example_Extension */ = { + isa = PBXGroup; + children = ( + 06462459205ED68000072191 /* TodayViewController.h */, + 0646245A205ED68000072191 /* TodayViewController.m */, + 0646245C205ED68000072191 /* MainInterface.storyboard */, + 0646245F205ED68000072191 /* Info.plist */, + 06462468205EDB9900072191 /* Example_Extension.entitlements */, + ); + path = Example_Extension; + sourceTree = ""; + }; 341564001C487ABA00ECA3D9 /* Frameworks */ = { isa = PBXGroup; children = ( + 06375C8421A4E46500338E3F /* AppAuthCore.framework */, 5FEA25241E75C17E00C2D71B /* AppAuth.framework */, 346E91981C2A245000D3620B /* SafariServices.framework */, + 06462456205ED68000072191 /* NotificationCenter.framework */, ); name = Frameworks; sourceTree = ""; @@ -58,6 +119,7 @@ isa = PBXGroup; children = ( 346E916B1C29D42800D3620B /* Source */, + 06462458205ED68000072191 /* Example_Extension */, 341564001C487ABA00ECA3D9 /* Frameworks */, 346E916A1C29D42800D3620B /* Products */, ); @@ -67,6 +129,7 @@ isa = PBXGroup; children = ( 346E91691C29D42800D3620B /* Example.app */, + 06462455205ED68000072191 /* Example_Extension.appex */, ); name = Products; sourceTree = ""; @@ -83,6 +146,7 @@ 346E91781C29D42800D3620B /* Assets.xcassets */, 346E917A1C29D42800D3620B /* LaunchScreen.storyboard */, 346E917D1C29D42800D3620B /* Info.plist */, + 06462467205EDB5400072191 /* Example.entitlements */, ); path = Source; sourceTree = ""; @@ -90,6 +154,24 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 06462454205ED68000072191 /* Example_Extension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 06462466205ED68000072191 /* Build configuration list for PBXNativeTarget "Example_Extension" */; + buildPhases = ( + 06462451205ED68000072191 /* Sources */, + 06462452205ED68000072191 /* Frameworks */, + 06462453205ED68000072191 /* Resources */, + 06375C8621A4E48800338E3F /* Carthage */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Example_Extension; + productName = Example_Extension; + productReference = 06462455205ED68000072191 /* Example_Extension.appex */; + productType = "com.apple.product-type.app-extension"; + }; 346E91681C29D42800D3620B /* Example */ = { isa = PBXNativeTarget; buildConfigurationList = 346E91801C29D42800D3620B /* Build configuration list for PBXNativeTarget "Example" */; @@ -98,10 +180,12 @@ 346E91661C29D42800D3620B /* Frameworks */, 346E91671C29D42800D3620B /* Resources */, 5FEA25261E75C1CF00C2D71B /* Carthage */, + 06462462205ED68000072191 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + 06462461205ED68000072191 /* PBXTargetDependency */, ); name = Example; productName = Example; @@ -117,8 +201,22 @@ LastUpgradeCheck = 0720; ORGANIZATIONNAME = "William Denniss"; TargetAttributes = { + 06462454205ED68000072191 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + }; + }; 346E91681C29D42800D3620B = { CreatedOnToolsVersion = 7.2; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + }; }; }; }; @@ -136,11 +234,20 @@ projectRoot = ""; targets = ( 346E91681C29D42800D3620B /* Example */, + 06462454205ED68000072191 /* Example_Extension */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 06462453205ED68000072191 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0646245E205ED68000072191 /* MainInterface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 346E91671C29D42800D3620B /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -154,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; @@ -167,11 +293,19 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/usr/local/bin/carthage copy-frameworks"; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 06462451205ED68000072191 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0646245B205ED68000072191 /* TodayViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 346E91651C29D42800D3620B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -184,7 +318,23 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 06462461205ED68000072191 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 06462454205ED68000072191 /* Example_Extension */; + targetProxy = 06462460205ED68000072191 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ + 0646245C205ED68000072191 /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 0646245D205ED68000072191 /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; 346E917A1C29D42800D3620B /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -196,6 +346,76 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 06462464205ED68000072191 /* Debug */ = { + isa = XCBuildConfiguration; + 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_ENTITLEMENTS = Example_Extension/Example_Extension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = Example_Extension/Info.plist; + 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)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 06462465205ED68000072191 /* Release */ = { + isa = XCBuildConfiguration; + 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_ENTITLEMENTS = Example_Extension/Example_Extension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = Example_Extension/Info.plist; + 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)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 346E917E1C29D42800D3620B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -286,6 +506,9 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Source/Example.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", @@ -303,6 +526,9 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Source/Example.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", @@ -319,6 +545,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 06462466205ED68000072191 /* Build configuration list for PBXNativeTarget "Example_Extension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 06462464205ED68000072191 /* Debug */, + 06462465205ED68000072191 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 346E91641C29D42800D3620B /* Build configuration list for PBXProject "Example" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/xcshareddata/xcschemes/Example_Extension.xcscheme b/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/xcshareddata/xcschemes/Example_Extension.xcscheme new file mode 100644 index 000000000..7dff3414d --- /dev/null +++ b/Examples/Example-iOS_ObjC-Carthage/Example.xcodeproj/xcshareddata/xcschemes/Example_Extension.xcscheme @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 000000000..c2f74e69f --- /dev/null +++ b/Examples/Example-iOS_ObjC-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..82681feb5 --- /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 (@available(iOS 10.0, *)) { + [self.extensionContext setWidgetLargestAvailableDisplayMode:NCWidgetDisplayModeExpanded]; + } else { + self.preferredContentSize = CGSizeMake(0, 400.0); + } + + [self loadState]; +} + +- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode + withMaximumSize:(CGSize)maxSize NS_AVAILABLE_IOS(10.0) { + + if (activeDisplayMode == NCWidgetDisplayModeExpanded) { + self.preferredContentSize = CGSizeMake(maxSize.width, 400.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..4d58cf9d2 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]; } @@ -176,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-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 + + + 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..7f3f2de68 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 */; }; - 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 */ + 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,29 @@ }; /* 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 */ + 06462428205EB35400072191 /* Example-iOS_ObjC.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Example-iOS_ObjC.entitlements"; sourceTree = ""; }; + 06462429205EB37A00072191 /* Example-iOS_ObjC_Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Example-iOS_ObjC_Extension.entitlements"; sourceTree = ""; }; + 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,13 +80,25 @@ 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 = ""; }; - 8B61A918CBE7B8142CD7D8B3 /* libPods-Example-iOS_ObjC.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Example-iOS_ObjC.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; + 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 = ""; }; 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 = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 06D4812A2055C3D400D9DC32 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 06D4812F2055C3D400D9DC32 /* NotificationCenter.framework in Frameworks */, + BE28448F8FD76A9C12A30150 /* Pods_Example_iOS_ObjC_Extension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 341310A41E6DEF7000D5DEE5 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -65,13 +111,25 @@ 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; }; /* 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 */, + 06462429205EB37A00072191 /* Example-iOS_ObjC_Extension.entitlements */, + ); + path = "Example-iOS_ObjC_Extension"; + sourceTree = ""; + }; 341310A81E6DEF7000D5DEE5 /* Tests */ = { isa = PBXGroup; children = ( @@ -87,7 +145,9 @@ 3474C8DD1DFCB08E00F22B34 /* libAppAuth-iOS.a */, 346E91981C2A245000D3620B /* SafariServices.framework */, 09795EAF079B07A1781675D9 /* libPods-Example.a */, - 8B61A918CBE7B8142CD7D8B3 /* libPods-Example-iOS_ObjC.a */, + 06D4812E2055C3D400D9DC32 /* NotificationCenter.framework */, + 43D84745AE558B974A2E64F7 /* Pods_Example_iOS_ObjC.framework */, + 3CC004951DF03519C52500EC /* Pods_Example_iOS_ObjC_Extension.framework */, ); name = Frameworks; sourceTree = ""; @@ -97,6 +157,7 @@ children = ( 346E916B1C29D42800D3620B /* Source */, 341310A81E6DEF7000D5DEE5 /* Tests */, + 06D481302055C3D400D9DC32 /* Example-iOS_ObjC_Extension */, 341564001C487ABA00ECA3D9 /* Frameworks */, 346E916A1C29D42800D3620B /* Products */, 6DB0B0125441549B9E4A3E6C /* Pods */, @@ -108,6 +169,7 @@ children = ( 346E91691C29D42800D3620B /* Example-iOS_ObjC.app */, 341310A71E6DEF7000D5DEE5 /* AppAuthExample-iOS_ObjCTests.xctest */, + 06D4812D2055C3D400D9DC32 /* Example-iOS_ObjC_Extension.appex */, ); name = Products; sourceTree = ""; @@ -124,6 +186,7 @@ 346E91781C29D42800D3620B /* Assets.xcassets */, 346E917A1C29D42800D3620B /* LaunchScreen.storyboard */, 346E917D1C29D42800D3620B /* Info.plist */, + 06462428205EB35400072191 /* Example-iOS_ObjC.entitlements */, ); path = Source; sourceTree = ""; @@ -135,6 +198,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 +207,24 @@ /* 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 */, + ); + 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" */; @@ -168,12 +251,13 @@ 346E91651C29D42800D3620B /* Sources */, 346E91661C29D42800D3620B /* Frameworks */, 346E91671C29D42800D3620B /* Resources */, - 0B0B46F67786AB6E322D5F2B /* [CP] Embed Pods Frameworks */, - 3CB1FD4B438DD277394F39A7 /* [CP] Copy Pods Resources */, + 06D4813E2055C3D400D9DC32 /* Embed App Extensions */, + 0B7CB92A32140E833CA8FF89 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( + 06D481392055C3D400D9DC32 /* PBXTargetDependency */, ); name = "Example-iOS_ObjC"; productName = Example; @@ -189,6 +273,15 @@ LastUpgradeCheck = 0720; ORGANIZATIONNAME = "William Denniss"; TargetAttributes = { + 06D4812C2055C3D400D9DC32 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + }; + }; 341310A61E6DEF7000D5DEE5 = { CreatedOnToolsVersion = 8.2.1; ProvisioningStyle = Automatic; @@ -196,6 +289,11 @@ }; 346E91681C29D42800D3620B = { CreatedOnToolsVersion = 7.2; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + }; }; }; }; @@ -204,6 +302,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -214,11 +313,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; @@ -239,34 +347,40 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0B0B46F67786AB6E322D5F2B /* [CP] Embed Pods Frameworks */ = { + 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 = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example-iOS_ObjC/Pods-Example-iOS_ObjC-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-iOS_ObjC/Pods-Example-iOS_ObjC-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 3CB1FD4B438DD277394F39A7 /* [CP] Copy Pods Resources */ = { + 836219F293F319703A16E68D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Copy Pods Resources"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Example-iOS_ObjC_Extension-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example-iOS_ObjC/Pods-Example-iOS_ObjC-resources.sh\"\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; }; C4DC5E3A0D4C7419380FA8C2 /* [CP] Check Pods Manifest.lock */ = { @@ -275,18 +389,29 @@ 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_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 */ /* 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 +433,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 +446,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 +465,70 @@ /* 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_ENTITLEMENTS = "Example-iOS_ObjC_Extension/Example-iOS_ObjC_Extension.entitlements"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "Example-iOS_ObjC_Extension/Info.plist"; + 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)"; + 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_ENTITLEMENTS = "Example-iOS_ObjC_Extension/Example-iOS_ObjC_Extension.entitlements"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "Example-iOS_ObjC_Extension/Info.plist"; + 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)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 341310AF1E6DEF7000D5DEE5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -338,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)"; @@ -357,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)"; @@ -456,8 +658,11 @@ baseConfigurationReference = 0D29B2A58C931A5D41332144 /* Pods-Example-iOS_ObjC.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "Source/Example-iOS_ObjC.entitlements"; + 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)"; @@ -469,8 +674,11 @@ baseConfigurationReference = ECBCCC4A1A779C83C72044F2 /* Pods-Example-iOS_ObjC.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "Source/Example-iOS_ObjC.entitlements"; + 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)"; @@ -480,6 +688,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.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 new file mode 100644 index 000000000..67a53a714 --- /dev/null +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Base.lproj/MainInterface.storyboard @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Example-iOS_ObjC_Extension.entitlements b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Example-iOS_ObjC_Extension.entitlements new file mode 100644 index 000000000..c250a2746 --- /dev/null +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/Example-iOS_ObjC_Extension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.net.openid.appauth.Example + + + 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..73d2d5e3a --- /dev/null +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.h @@ -0,0 +1,23 @@ +/*! @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/Example-iOS_ObjC_Extension/TodayViewController.m b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m new file mode 100644 index 000000000..1bfbd96ed --- /dev/null +++ b/Examples/Example-iOS_ObjC/Example-iOS_ObjC_Extension/TodayViewController.m @@ -0,0 +1,222 @@ +/*! @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 (@available(iOS 10, *)) { + [self.extensionContext setWidgetLargestAvailableDisplayMode:NCWidgetDisplayModeExpanded]; + } else { + self.preferredContentSize = CGSizeMake(0, 400.0); + } + + [self loadState]; +} + +- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode + withMaximumSize:(CGSize)maxSize NS_AVAILABLE_IOS(10.0) { + + if (activeDisplayMode == NCWidgetDisplayModeExpanded) { + self.preferredContentSize = CGSizeMake(maxSize.width, 400.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/Podfile b/Examples/Example-iOS_ObjC/Podfile index 4d6ab0f6f..600de7d10 100644 --- a/Examples/Example-iOS_ObjC/Podfile +++ b/Examples/Example-iOS_ObjC/Podfile @@ -1,7 +1,15 @@ -target 'Example-iOS_ObjC' do - platform :ios, '9.0' +platform :ios, '11.0' + +use_frameworks! +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/Core Pod + # In production, just use `pod 'AppAuth/Core'` without the path reference. + pod 'AppAuth/Core', :path => '../../' +end diff --git a/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m b/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m index 86a32445e..a4accb51b 100644 --- a/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m +++ b/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m @@ -104,18 +104,19 @@ - (void)verifyConfig { */ - (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]; + NSUserDefaults* userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.net.openid.appauth.Example"]; + NSData *archivedAuthState = [NSKeyedArchiver archivedDataWithRootObject:_authState]; + [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]; } @@ -178,8 +179,10 @@ - (void)doClientRegistration:(OIDServiceConfiguration *)configuration grantTypes:nil subjectType:nil tokenEndpointAuthMethod:@"client_secret_post" + initialAccessToken:nil additionalParameters:nil]; - // performs registration request + + // performs registration request [self logMessage:@"Initiating registration request"]; [OIDAuthorizationService performRegistrationRequest:request diff --git a/Examples/Example-iOS_ObjC/Source/Example-iOS_ObjC.entitlements b/Examples/Example-iOS_ObjC/Source/Example-iOS_ObjC.entitlements new file mode 100644 index 000000000..c250a2746 --- /dev/null +++ b/Examples/Example-iOS_ObjC/Source/Example-iOS_ObjC.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.net.openid.appauth.Example + + + diff --git a/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj b/Examples/Example-iOS_Swift-Carthage/Example.xcodeproj/project.pbxproj index 825c2a30f..33f25f87b 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 */ + 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, ); }; }; 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,39 @@ 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 */ + 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 = ""; }; + 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 +64,15 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 06462496205F026300072191 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 06375C8821A4E50E00338E3F /* AppAuthCore.framework in Frameworks */, + 0646249B205F026300072191 /* NotificationCenter.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9F265B941F9AC5D600DC14BF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -38,10 +84,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 +109,7 @@ isa = PBXGroup; children = ( 9F265B971F9AC5D600DC14BF /* Example.app */, + 06462499205F026300072191 /* Example_Extension.appex */, ); name = Products; sourceTree = ""; @@ -64,6 +123,7 @@ 9F265BCD1F9AC69300DC14BF /* Main.storyboard */, 9F265BC91F9AC69300DC14BF /* Assets.xcassets */, 9F265BD01F9AC69300DC14BF /* Info.plist */, + 064624AD205F034A00072191 /* Example.entitlements */, ); path = Source; sourceTree = ""; @@ -71,7 +131,9 @@ 9F265BD81F9AE4CA00DC14BF /* Frameworks */ = { isa = PBXGroup; children = ( + 06375C8721A4E50E00338E3F /* AppAuthCore.framework */, 9F265BD91F9AE50400DC14BF /* AppAuth.framework */, + 0646249A205F026300072191 /* NotificationCenter.framework */, ); name = Frameworks; sourceTree = ""; @@ -79,6 +141,24 @@ /* 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 */, + 06375C8921A4E56B00338E3F /* Carthage */, + ); + 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" */; @@ -86,11 +166,13 @@ 9F265B931F9AC5D600DC14BF /* Sources */, 9F265B941F9AC5D600DC14BF /* Frameworks */, 9F265B951F9AC5D600DC14BF /* Resources */, - 9F265BDB1F9AE52C00DC14BF /* ShellScript */, + 9F265BDB1F9AE52C00DC14BF /* Carthage */, + 064624A9205F026300072191 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + 064624A4205F026300072191 /* PBXTargetDependency */, ); name = Example; productName = Example; @@ -103,13 +185,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 +223,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; @@ -145,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 = ( @@ -153,15 +277,24 @@ 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 */ /* 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 +306,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 +342,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 = 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)"; + 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 = 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)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 9F265BBD1F9AC5D600DC14BF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -303,8 +494,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 +516,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 +538,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..170af8176 --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Example_Extension/Base.lproj/MainInterface.storyboard @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..fc663d1bd --- /dev/null +++ b/Examples/Example-iOS_Swift-Carthage/Example_Extension/TodayViewController.swift @@ -0,0 +1,224 @@ +// +// 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 AppAuthCore + +/** + 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 #available(iOS 10.0, *) { + self.extensionContext?.widgetLargestAvailableDisplayMode = .expanded + } else { + 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: 400.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..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") @@ -463,13 +464,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 + + + 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-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/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.pbxproj b/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.pbxproj new file mode 100644 index 000000000..b2e8a7d1a --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS.xcodeproj/project.pbxproj @@ -0,0 +1,400 @@ +// !$*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 */; }; + FEFBCAF105C70E934E5A4975 /* Pods_Example_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F58A1FCA981D60A0EA8A3E5A /* Pods_Example_tvOS.framework */; }; +/* 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 = ""; }; + 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 */ + 2D91B7A0248EA17C0005B197 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FEFBCAF105C70E934E5A4975 /* Pods_Example_tvOS.framework 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 = ( + F58A1FCA981D60A0EA8A3E5A /* Pods_Example_tvOS.framework */, + ); + 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 */, + C86108A8D245915451E09E53 /* [CP] Embed Pods Frameworks */, + ); + 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; + }; + 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 */ + 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..1cdfb5ccf --- /dev/null +++ b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m @@ -0,0 +1,404 @@ +/*! @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 Indicates whether YES to discover endpoints from @c kIssuer or NO to use the + @c kDeviceAuthorizationEndpoint, @c kTokenEndpoint, and @c kUserInfoEndpoint values defined + below. + */ +static BOOL const shouldDiscoverEndpoints = YES; + +/*! @brief OAuth client ID. + */ +static NSString *const kClientID = @"YOUR_CLIENT_ID"; + +/*! @brief OAuth client secret. + */ +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 kDeviceAuthorizationEndpoint = @"https://www.example.com/device"; + +/*! @brief Token endpoint. + */ +static NSString *const kTokenEndpoint = @"https://www.example.com/token"; + +/*! @brief User info endpoint. + */ +static NSString *const kUserInfoEndpoint = @"https://www.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) + 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"); + + 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(![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"); + + 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) +} + +- (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]; + } + + 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 *deviceAuthorizationEndpoint = [NSURL URLWithString:kDeviceAuthorizationEndpoint]; + NSURL *tokenEndpoint = [NSURL URLWithString:kTokenEndpoint]; + + OIDTVServiceConfiguration *configuration = [[OIDTVServiceConfiguration alloc] + initWithDeviceAuthorizationEndpoint:deviceAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; + + // Perform authorization flow + [self performAuthorizationWithConfiguration:configuration]; + } +} + +- (void)performAuthorizationWithConfiguration:(OIDTVServiceConfiguration *)configuration { + // builds authentication request + __weak __typeof(self) weakSelf = self; + + 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; + + if (shouldDiscoverEndpoints) { + userinfoEndpoint = _authState.lastAuthorizationResponse.request.configuration.discoveryDocument + .userinfoEndpoint; + } else { + 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/Source/AppAuth/macOS/OIDExternalUserAgentMac.h b/Examples/Example-tvOS/Example-tvOS/AppDelegate.h similarity index 56% rename from Source/AppAuth/macOS/OIDExternalUserAgentMac.h rename to Examples/Example-tvOS/Example-tvOS/AppDelegate.h index b7122aee0..0a665e487 100644 --- a/Source/AppAuth/macOS/OIDExternalUserAgentMac.h +++ b/Examples/Example-tvOS/Example-tvOS/AppDelegate.h @@ -1,14 +1,12 @@ -/*! @file OIDExternalUserAgentMac.h - @brief AppAuth iOS SDK +/*! @file AppDelegate.h + @brief AppAuth tvOS SDK Example @copyright - Copyright 2016 Google Inc. All Rights Reserved. + 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. @@ -16,21 +14,14 @@ limitations under the License. */ -#import - -#if TARGET_OS_OSX - -#import "OIDExternalUserAgent.h" +#import -NS_ASSUME_NONNULL_BEGIN +/*! @brief The example application's delegate. + */ +@interface AppDelegate : UIResponder -/*! @brief A Mac-specific external user-agent UI Coordinator that uses the default browser to - present an external user-agent request. +/*! @brief The example application's @c UIWindow. */ -@interface OIDExternalUserAgentMac : NSObject +@property (strong, nonatomic) UIWindow *window; @end - -NS_ASSUME_NONNULL_END - -#endif // TARGET_OS_OSX 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..3e7ac1135 --- /dev/null +++ b/Examples/Example-tvOS/Podfile @@ -0,0 +1,9 @@ +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. + pod 'AppAuth/TV', :path => '../../' +end 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/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..9ed5f1d66 --- /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.6.3) + 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 diff --git a/Package.swift b/Package.swift index 0e180a383..103c0f761 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 @@ -23,8 +23,8 @@ import PackageDescription let package = Package( name: "AppAuth", platforms: [ - .macOS(.v10_10), - .iOS(.v8), + .macOS(.v10_12), + .iOS(.v9), .tvOS(.v9), .watchOS(.v2) ], @@ -34,20 +34,25 @@ let package = Package( targets: ["AppAuthCore"]), .library( name: "AppAuth", - targets: ["AppAuth"]) + targets: ["AppAuth"]), + .library( + name: "AppAuthTV", + targets: ["AppAuthTV"]) ], dependencies: [], targets: [ .target( name: "AppAuthCore", - path: "Source/AppAuthCore", + 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("Resources/PrivacyInfo.xcprivacy")], publicHeadersPath: "", cSettings: [ .headerSearchPath("iOS"), @@ -55,17 +60,29 @@ let package = Package( .headerSearchPath("macOS/LoopbackHTTPServer"), ] ), + .target( + name: "AppAuthTV", + dependencies: ["AppAuthCore"], + path: "Sources/AppAuthTV", + resources: [.copy("Resources/PrivacyInfo.xcprivacy")], + publicHeadersPath: "" + ), .testTarget( name: "AppAuthCoreTests", dependencies: ["AppAuthCore"], path: "UnitTests", - exclude: ["OIDSwiftTests.swift"] + exclude: ["OIDSwiftTests.swift", "AppAuthTV"] ), .testTarget( name: "AppAuthCoreSwiftTests", dependencies: ["AppAuthCore"], path: "UnitTests", sources: ["OIDSwiftTests.swift"] - ) + ), + .testTarget( + name: "AppAuthTVTests", + dependencies: ["AppAuthTV"], + path: "UnitTests/AppAuthTV" + ), ] ) diff --git a/README.md b/README.md index 5f54c2c39..53085b3c4 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,14 @@ ![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) - -AppAuth for iOS and macOS is a client SDK for communicating with +[![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) +[![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, 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 @@ -24,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 @@ -64,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: @@ -78,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), @@ -89,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 @@ -107,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 @@ -119,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 @@ -160,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 @@ -195,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` @@ -422,6 +490,64 @@ _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 + additionalHeaders: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 @@ -464,7 +590,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 @@ -535,7 +661,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 +675,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). @@ -571,4 +715,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. diff --git a/Source/AppAuth/macOS/OIDAuthState+Mac.h b/Source/AppAuth/macOS/OIDAuthState+Mac.h deleted file mode 100644 index f32d27c08..000000000 --- a/Source/AppAuth/macOS/OIDAuthState+Mac.h +++ /dev/null @@ -1,51 +0,0 @@ -/*! @file OIDAuthState+Mac.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 - -#if TARGET_OS_OSX - -#import "OIDAuthState.h" - -NS_ASSUME_NONNULL_BEGIN - -/*! @brief macOS specific convenience methods for @c OIDAuthState. - */ -@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. 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; - -@end - -NS_ASSUME_NONNULL_END - -#endif // TARGET_OS_OSX diff --git a/Source/AppAuth/macOS/OIDAuthState+Mac.m b/Source/AppAuth/macOS/OIDAuthState+Mac.m deleted file mode 100644 index 16b061724..000000000 --- a/Source/AppAuth/macOS/OIDAuthState+Mac.m +++ /dev/null @@ -1,40 +0,0 @@ -/*! @file OIDAuthState+Mac.m - @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 - -#if TARGET_OS_OSX - -#import "OIDAuthState+Mac.h" - -#import "OIDExternalUserAgentMac.h" - -@implementation OIDAuthState (Mac) - -+ (id) - authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest - callback:(OIDAuthStateAuthorizationCallback)callback { - OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] init]; - return [self authStateByPresentingAuthorizationRequest:authorizationRequest - externalUserAgent:externalUserAgent - callback:callback]; -} - -@end - -#endif // TARGET_OS_OSX diff --git a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h b/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h deleted file mode 100644 index 37de63f16..000000000 --- a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.h +++ /dev/null @@ -1,45 +0,0 @@ -/*! @file OIDAuthorizationService+Mac.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 - -#if TARGET_OS_OSX - -#import "OIDAuthorizationService.h" - -NS_ASSUME_NONNULL_BEGIN - -/*! @brief Provides macOS specific authorization request handling. - */ -@interface OIDAuthorizationService (Mac) - -/*! @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 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; - -@end - -NS_ASSUME_NONNULL_END - -#endif // TARGET_OS_OSX diff --git a/Source/AppAuth/macOS/OIDExternalUserAgentMac.m b/Source/AppAuth/macOS/OIDExternalUserAgentMac.m deleted file mode 100644 index c35a7a327..000000000 --- a/Source/AppAuth/macOS/OIDExternalUserAgentMac.m +++ /dev/null @@ -1,81 +0,0 @@ -/*! @file OIDExternalUserAgentMac.m - @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 - -#if TARGET_OS_OSX - -#import "OIDExternalUserAgentMac.h" - -#import - -#import "OIDErrorUtilities.h" -#import "OIDExternalUserAgentSession.h" -#import "OIDExternalUserAgentRequest.h" - -NS_ASSUME_NONNULL_BEGIN - -@implementation OIDExternalUserAgentMac { - BOOL _externalUserAgentFlowInProgress; - __weak id _session; -} - -- (BOOL)presentExternalUserAgentRequest:(id)request - session:(id)session { - if (_externalUserAgentFlowInProgress) { - // TODO: Handle errors as authorization is already in progress. - return NO; - } - - _externalUserAgentFlowInProgress = YES; - _session = session; - NSURL *requestURL = [request externalUserAgentRequestURL]; - - BOOL openedBrowser = [[NSWorkspace sharedWorkspace] openURL:requestURL]; - if (!openedBrowser) { - [self cleanUp]; - NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeBrowserOpenError - underlyingError:nil - description:@"Unable to open the browser."]; - [session failExternalUserAgentFlowWithError:safariError]; - } - return openedBrowser; -} - -- (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 - // control the browser. - [self cleanUp]; - if (completion) completion(); -} - -- (void)cleanUp { - _session = nil; - _externalUserAgentFlowInProgress = NO; -} - -@end - -NS_ASSUME_NONNULL_END - -#endif // TARGET_OS_MAC diff --git a/Source/AppAuth.h b/Sources/AppAuth.h similarity index 99% rename from Source/AppAuth.h rename to Sources/AppAuth.h index a1ecd35bd..dcb1f5598 100644 --- a/Source/AppAuth.h +++ b/Sources/AppAuth.h @@ -52,7 +52,7 @@ #import "OIDExternalUserAgentIOS.h" #import "OIDExternalUserAgentIOSCustomBrowser.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/Sources/AppAuth/Resources/PrivacyInfo.xcprivacy b/Sources/AppAuth/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..cc6746dcb --- /dev/null +++ b/Sources/AppAuth/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,16 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyCollectedDataTypes + + + NSPrivacyTrackingDomains + + NSPrivacyTracking + + + diff --git a/Source/AppAuth/iOS/OIDAuthState+IOS.h b/Sources/AppAuth/iOS/OIDAuthState+IOS.h similarity index 59% rename from Source/AppAuth/iOS/OIDAuthState+IOS.h rename to Sources/AppAuth/iOS/OIDAuthState+IOS.h index 99ca45734..1a1ee63a0 100644 --- a/Source/AppAuth/iOS/OIDAuthState+IOS.h +++ b/Sources/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/Sources/AppAuth/iOS/OIDAuthState+IOS.m similarity index 68% rename from Source/AppAuth/iOS/OIDAuthState+IOS.m rename to Sources/AppAuth/iOS/OIDAuthState+IOS.m index 9f3a4e8c8..c474a77d1 100644 --- a/Source/AppAuth/iOS/OIDAuthState+IOS.m +++ b/Sources/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/Sources/AppAuth/iOS/OIDAuthorizationService+IOS.h similarity index 57% rename from Source/AppAuth/iOS/OIDAuthorizationService+IOS.h rename to Sources/AppAuth/iOS/OIDAuthorizationService+IOS.h index c6e14f194..c7c685d28 100644 --- a/Source/AppAuth/iOS/OIDAuthorizationService+IOS.h +++ b/Sources/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/Sources/AppAuth/iOS/OIDAuthorizationService+IOS.m similarity index 66% rename from Source/AppAuth/iOS/OIDAuthorizationService+IOS.m rename to Sources/AppAuth/iOS/OIDAuthorizationService+IOS.m index 05ccff5db..4ca07c55e 100644 --- a/Source/AppAuth/iOS/OIDAuthorizationService+IOS.m +++ b/Sources/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/Sources/AppAuth/iOS/OIDExternalUserAgentCatalyst.h similarity index 74% rename from Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h rename to Sources/AppAuth/iOS/OIDExternalUserAgentCatalyst.h index d98d4413c..910d0bb86 100644 --- a/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h +++ b/Sources/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/Sources/AppAuth/iOS/OIDExternalUserAgentCatalyst.m similarity index 91% rename from Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m rename to Sources/AppAuth/iOS/OIDExternalUserAgentCatalyst.m index fc9cef5c0..d6771b3e9 100644 --- a/Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m +++ b/Sources/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/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h similarity index 56% rename from Source/AppAuth/iOS/OIDExternalUserAgentIOS.h rename to Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h index 7261c050d..12abc203c 100644 --- a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.h +++ b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.h @@ -34,18 +34,34 @@ 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"); /*! @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/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m similarity index 94% rename from Source/AppAuth/iOS/OIDExternalUserAgentIOS.m rename to Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m index 728f0868d..4a8cda0a3 100644 --- a/Source/AppAuth/iOS/OIDExternalUserAgentIOS.m +++ b/Sources/AppAuth/iOS/OIDExternalUserAgentIOS.m @@ -43,6 +43,7 @@ @interface OIDExternalUserAgentIOS () @implementation OIDExternalUserAgentIOS { UIViewController *_presentingViewController; + BOOL _prefersEphemeralSession; BOOL _externalUserAgentFlowInProgress; __weak id _session; @@ -54,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]; @@ -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/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 96% rename from Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m rename to Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m index 51d2e56cf..be5dc820c 100644 --- a/Source/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m +++ b/Sources/AppAuth/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]; 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/Sources/AppAuth/macOS/OIDAuthState+Mac.h b/Sources/AppAuth/macOS/OIDAuthState+Mac.h new file mode 100644 index 000000000..71e56f22a --- /dev/null +++ b/Sources/AppAuth/macOS/OIDAuthState+Mac.h @@ -0,0 +1,92 @@ +/*! @file OIDAuthState+Mac.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 + +#if TARGET_OS_OSX + +#import +#import "OIDAuthState.h" + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief macOS specific convenience methods for @c OIDAuthState. + */ +@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. 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 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 @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 + 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 + +#endif // TARGET_OS_OSX diff --git a/Sources/AppAuth/macOS/OIDAuthState+Mac.m b/Sources/AppAuth/macOS/OIDAuthState+Mac.m new file mode 100644 index 000000000..f2894daaf --- /dev/null +++ b/Sources/AppAuth/macOS/OIDAuthState+Mac.m @@ -0,0 +1,62 @@ +/*! @file OIDAuthState+Mac.m + @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 + +#if TARGET_OS_OSX + +#import "OIDAuthState+Mac.h" + +#import "OIDExternalUserAgentMac.h" + +@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 + 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 + callback:(OIDAuthStateAuthorizationCallback)callback { + OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] init]; + return [self authStateByPresentingAuthorizationRequest:authorizationRequest + externalUserAgent:externalUserAgent + callback:callback]; +} + +@end + +#endif // TARGET_OS_OSX diff --git a/Sources/AppAuth/macOS/OIDAuthorizationService+Mac.h b/Sources/AppAuth/macOS/OIDAuthorizationService+Mac.h new file mode 100644 index 000000000..0a34faf28 --- /dev/null +++ b/Sources/AppAuth/macOS/OIDAuthorizationService+Mac.h @@ -0,0 +1,78 @@ +/*! @file OIDAuthorizationService+Mac.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 + +#if TARGET_OS_OSX + +#import +#import "OIDAuthorizationService.h" + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Provides macOS specific authorization request handling. + */ +@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 @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. + @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 + __deprecated_msg("For macOS 10.15 and above please use presentAuthorizationRequest:presentingWindow:callback:"); + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_OSX diff --git a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m b/Sources/AppAuth/macOS/OIDAuthorizationService+Mac.m similarity index 51% rename from Source/AppAuth/macOS/OIDAuthorizationService+Mac.m rename to Sources/AppAuth/macOS/OIDAuthorizationService+Mac.m index c0abec8a4..4d3d9a038 100644 --- a/Source/AppAuth/macOS/OIDAuthorizationService+Mac.m +++ b/Sources/AppAuth/macOS/OIDAuthorizationService+Mac.m @@ -28,6 +28,27 @@ @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 + 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/Sources/AppAuth/macOS/OIDExternalUserAgentMac.h b/Sources/AppAuth/macOS/OIDExternalUserAgentMac.h new file mode 100644 index 000000000..360c44ad6 --- /dev/null +++ b/Sources/AppAuth/macOS/OIDExternalUserAgentMac.h @@ -0,0 +1,54 @@ +/*! @file OIDExternalUserAgentMac.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 + +#if TARGET_OS_OSX + +#import +#import "OIDExternalUserAgent.h" + +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 + +/*! @brief The designated initializer. + @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 + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_OSX diff --git a/Sources/AppAuth/macOS/OIDExternalUserAgentMac.m b/Sources/AppAuth/macOS/OIDExternalUserAgentMac.m new file mode 100644 index 000000000..d1e08f902 --- /dev/null +++ b/Sources/AppAuth/macOS/OIDExternalUserAgentMac.m @@ -0,0 +1,170 @@ +/*! @file OIDExternalUserAgentMac.m + @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 + +#if TARGET_OS_OSX + +#import "OIDExternalUserAgentMac.h" + +#import + +#import "OIDErrorUtilities.h" +#import "OIDExternalUserAgentSession.h" +#import "OIDExternalUserAgentRequest.h" +#import + + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDExternalUserAgentMac () +@end + +@implementation OIDExternalUserAgentMac { + BOOL _externalUserAgentFlowInProgress; + __weak id _session; + BOOL _prefersEphemeralSession; + + 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; +} + +- (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" + return [self initWithPresentingWindow:nil]; +#pragma clang diagnostic pop +} + +- (BOOL)presentExternalUserAgentRequest:(id)request + session:(id)session { + if (_externalUserAgentFlowInProgress) { + // TODO: Handle errors as authorization is already in progress. + return NO; + } + + _externalUserAgentFlowInProgress = YES; + _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; + _webAuthenticationSession.prefersEphemeralWebBrowserSession = _prefersEphemeralSession; + return [authenticationSession start]; + } + } + + BOOL openedBrowser = [[NSWorkspace sharedWorkspace] openURL:requestURL]; + if (!openedBrowser) { + [self cleanUp]; + NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeBrowserOpenError + underlyingError:nil + description:@"Unable to open the browser."]; + [session failExternalUserAgentFlowWithError:safariError]; + } + return openedBrowser; +} + +- (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; + } + +#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 (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 + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_OSX 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 99% rename from Source/AppAuth/macOS/OIDRedirectHTTPHandler.m rename to Sources/AppAuth/macOS/OIDRedirectHTTPHandler.m index 8a3df6cc2..3d0d765d8 100644 --- a/Source/AppAuth/macOS/OIDRedirectHTTPHandler.m +++ b/Sources/AppAuth/macOS/OIDRedirectHTTPHandler.m @@ -174,4 +174,4 @@ - (void)dealloc { @end -#endif // TARGET_OS_MAC +#endif // TARGET_OS_OSX 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 87% rename from Source/AppAuthCore/OIDAuthState.h rename to Sources/AppAuthCore/OIDAuthState.h index 68697d2ca..46c78a831 100644 --- a/Source/AppAuthCore/OIDAuthState.h +++ b/Sources/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/Sources/AppAuthCore/OIDAuthState.m similarity index 92% rename from Source/AppAuthCore/OIDAuthState.m rename to Sources/AppAuthCore/OIDAuthState.m index fe8a16221..cb5a22a1e 100644 --- a/Source/AppAuthCore/OIDAuthState.m +++ b/Sources/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/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 89% rename from Source/AppAuthCore/OIDAuthorizationRequest.h rename to Sources/AppAuthCore/OIDAuthorizationRequest.h index 594f01d87..c3a0cc0d8 100644 --- a/Source/AppAuthCore/OIDAuthorizationRequest.h +++ b/Sources/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/Sources/AppAuthCore/OIDAuthorizationRequest.m similarity index 91% rename from Source/AppAuthCore/OIDAuthorizationRequest.m rename to Sources/AppAuthCore/OIDAuthorizationRequest.m index ccfacda0f..1be1fdfde 100644 --- a/Source/AppAuthCore/OIDAuthorizationRequest.m +++ b/Sources/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/Source/AppAuthCore/OIDAuthorizationResponse.h b/Sources/AppAuthCore/OIDAuthorizationResponse.h similarity index 87% rename from Source/AppAuthCore/OIDAuthorizationResponse.h rename to Sources/AppAuthCore/OIDAuthorizationResponse.h index e7552fe59..a9af1864b 100644 --- a/Source/AppAuthCore/OIDAuthorizationResponse.h +++ b/Sources/AppAuthCore/OIDAuthorizationResponse.h @@ -123,6 +123,19 @@ NS_ASSUME_NONNULL_BEGIN - (nullable OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: (nullable NSDictionary *)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: + (nullable NSDictionary *)additionalHeaders; + @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthCore/OIDAuthorizationResponse.m b/Sources/AppAuthCore/OIDAuthorizationResponse.m similarity index 93% rename from Source/AppAuthCore/OIDAuthorizationResponse.m rename to Sources/AppAuthCore/OIDAuthorizationResponse.m index a8f92c75e..957f81d3a 100644 --- a/Source/AppAuthCore/OIDAuthorizationResponse.m +++ b/Sources/AppAuthCore/OIDAuthorizationResponse.m @@ -184,11 +184,19 @@ - (NSString *)description { #pragma mark - - (OIDTokenRequest *)tokenExchangeRequest { - return [self tokenExchangeRequestWithAdditionalParameters:nil]; + return [self tokenExchangeRequestWithAdditionalParameters:nil additionalHeaders:nil]; } - (OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: (NSDictionary *)additionalParameters { + return [self tokenExchangeRequestWithAdditionalParameters:additionalParameters + additionalHeaders:nil]; +} + +- (OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: + (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 +212,8 @@ - (OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: scope:nil refreshToken:nil codeVerifier:_request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; } @end 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/OIDIDTokenValidator.h b/Sources/AppAuthCore/OIDIDTokenValidator.h similarity index 100% rename from Source/AppAuthCore/OIDIDTokenValidator.h rename to Sources/AppAuthCore/OIDIDTokenValidator.h diff --git a/Source/AppAuthCore/OIDIDTokenValidator.m b/Sources/AppAuthCore/OIDIDTokenValidator.m similarity index 100% rename from Source/AppAuthCore/OIDIDTokenValidator.m rename to Sources/AppAuthCore/OIDIDTokenValidator.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 91% rename from Source/AppAuthCore/OIDServiceConfiguration.m rename to Sources/AppAuthCore/OIDServiceConfiguration.m index ca48a8c33..0e5c1197a 100644 --- a/Source/AppAuthCore/OIDServiceConfiguration.m +++ b/Sources/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]; @@ -185,8 +185,17 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { return nil; } - OIDServiceDiscovery *discoveryDocument = [aDecoder decodeObjectOfClass:[OIDServiceDiscovery class] - 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.h b/Sources/AppAuthCore/OIDServiceDiscovery.h similarity index 98% rename from Source/AppAuthCore/OIDServiceDiscovery.h rename to Sources/AppAuthCore/OIDServiceDiscovery.h index 577700834..1a4929c53 100644 --- a/Source/AppAuthCore/OIDServiceDiscovery.h +++ b/Sources/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 @@ -320,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; diff --git a/Source/AppAuthCore/OIDServiceDiscovery.m b/Sources/AppAuthCore/OIDServiceDiscovery.m similarity index 89% rename from Source/AppAuthCore/OIDServiceDiscovery.m rename to Sources/AppAuthCore/OIDServiceDiscovery.m index ca81108a8..4d96f9db3 100644 --- a/Source/AppAuthCore/OIDServiceDiscovery.m +++ b/Sources/AppAuthCore/OIDServiceDiscovery.m @@ -23,9 +23,14 @@ 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"; +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"; @@ -191,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; @@ -200,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]; } @@ -217,6 +244,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/AppAuthCore/OIDTokenRequest.h b/Sources/AppAuthCore/OIDTokenRequest.h similarity index 66% rename from Source/AppAuthCore/OIDTokenRequest.h rename to Sources/AppAuthCore/OIDTokenRequest.h index 00e0c6e20..53a007378 100644 --- a/Source/AppAuthCore/OIDTokenRequest.h +++ b/Sources/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; @@ -125,6 +129,57 @@ NS_ASSUME_NONNULL_BEGIN 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. + @see OIDGrantTypes.h + @param code The authorization code received from the authorization server. + @param redirectURL The client's redirect URI. + @param clientID The client identifier. + @param clientSecret The client secret. + @param scopes An array of scopes to combine into a single scope string per the OAuth2 spec. + @param refreshToken The refresh token. + @param codeVerifier The PKCE code verifier. + @param additionalParameters The client's additional token request parameters. + @param additionalHeaders The client's additional token request headers. + */ +- (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 + additionalHeaders:(nullable NSDictionary *)additionalHeaders; + /*! @brief Designated initializer. @param configuration The service's configuration. @param grantType the type of token being sent to the token endpoint, i.e. "authorization_code" @@ -139,6 +194,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,8 +206,14 @@ 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. + @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/Sources/AppAuthCore/OIDTokenRequest.m similarity index 75% rename from Source/AppAuthCore/OIDTokenRequest.m rename to Sources/AppAuthCore/OIDTokenRequest.m index bd27dd480..6d30b6d6b 100644 --- a/Source/AppAuthCore/OIDTokenRequest.m +++ b/Sources/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,9 +85,56 @@ - (instancetype)init scope: refreshToken: codeVerifier: - additionalParameters:) + additionalParameters: + 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 @@ -92,7 +144,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 +155,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 +168,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 +183,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,19 +231,35 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { [NSDictionary class], [NSString class] ]]; + NSDictionary *additionalParameters = - [aDecoder decodeObjectOfClasses:additionalParameterCodingClasses - forKey:kAdditionalParametersKey]; - self = [self initWithConfiguration:configuration - grantType:grantType - authorizationCode:code - redirectURL:redirectURL - clientID:clientID - clientSecret:clientSecret - scope:scope - refreshToken:refreshToken - codeVerifier:codeVerifier - additionalParameters:additionalParameters]; + [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) { + _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]; + _additionalHeaders = + [[NSDictionary alloc] initWithDictionary:additionalHeaders copyItems:YES]; + } return self; } @@ -201,6 +274,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 @@ -300,6 +374,10 @@ - (NSURLRequest *)URLRequest { for (id header in httpHeaders) { [URLRequest setValue:httpHeaders[header] forHTTPHeaderField:header]; } + + for (id header in _additionalHeaders) { + [URLRequest setValue:_additionalHeaders[header] forHTTPHeaderField:header]; + } return URLRequest; } 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/Sources/AppAuthTV.h b/Sources/AppAuthTV.h new file mode 100644 index 000000000..3768c6c14 --- /dev/null +++ b/Sources/AppAuthTV.h @@ -0,0 +1,22 @@ +/*! @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 "OIDTVAuthorizationRequest.h" +#import "OIDTVAuthorizationResponse.h" +#import "OIDTVAuthorizationService.h" +#import "OIDTVServiceConfiguration.h" diff --git a/Sources/AppAuthTV/OIDTVAuthorizationRequest.h b/Sources/AppAuthTV/OIDTVAuthorizationRequest.h new file mode 100644 index 000000000..2496948f1 --- /dev/null +++ b/Sources/AppAuthTV/OIDTVAuthorizationRequest.h @@ -0,0 +1,53 @@ +/*! @file OIDTVAuthorizationRequest.h + @brief AppAuth iOS 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 + +#import "OIDAuthorizationRequest.h" + +@class OIDTVServiceConfiguration; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Represents a TV and limited input device authorization request. + @see https://tools.ietf.org/html/rfc8628#section-3.1 + */ +@interface OIDTVAuthorizationRequest : 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:(OIDTVServiceConfiguration *)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/Sources/AppAuthTV/OIDTVAuthorizationRequest.m b/Sources/AppAuthTV/OIDTVAuthorizationRequest.m new file mode 100644 index 000000000..da524d388 --- /dev/null +++ b/Sources/AppAuthTV/OIDTVAuthorizationRequest.m @@ -0,0 +1,117 @@ +/*! @file OIDTVAuthorizationRequest.m + @brief AppAuth iOS 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 "OIDTVAuthorizationRequest.h" +#import "OIDTVServiceConfiguration.h" +#import "OIDURLQueryComponent.h" + +@implementation OIDTVAuthorizationRequest + +- (instancetype) + initWithConfiguration:(OIDTVServiceConfiguration *)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:[OIDTVServiceConfiguration class]]) { + NSAssert([configuration isKindOfClass:[OIDTVServiceConfiguration class]], + @"configuration parameter must be of type OIDTVServiceConfiguration, 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:(OIDTVServiceConfiguration *)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] initWithString:@""] + responseType:OIDResponseTypeCode + additionalParameters:additionalParameters]; +} + +#pragma mark - NSObject overrides + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, request: %@>", + NSStringFromClass([self class]), + (void *)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"; + + OIDTVServiceConfiguration *tvConfiguration = (OIDTVServiceConfiguration *)self.configuration; + + NSMutableURLRequest *URLRequest = + [[NSURLRequest requestWithURL:tvConfiguration.deviceAuthorizationEndpoint] 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/Sources/AppAuthTV/OIDTVAuthorizationResponse.h b/Sources/AppAuthTV/OIDTVAuthorizationResponse.h new file mode 100644 index 000000000..c57847c6e --- /dev/null +++ b/Sources/AppAuthTV/OIDTVAuthorizationResponse.h @@ -0,0 +1,111 @@ +/*! @file OIDTVAuthorizationResponse.h + @brief AppAuth iOS 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 + +#import "OIDAuthorizationResponse.h" + +@class OIDTVAuthorizationRequest; +@class OIDTVTokenRequest; + +NS_ASSUME_NONNULL_BEGIN + +/*! @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 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 *verificationURIComplete; + +/*! @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; + +/*! @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:(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 OIDTVTokenRequest suitable for polling the token endpoint. + @see https://tools.ietf.org/html/rfc8628#section-3.4 + */ +- (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 OIDTVTokenRequest suitable for polling the token endpoint. + @see https://tools.ietf.org/html/rfc8628#section-3.4 + */ +- (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/Sources/AppAuthTV/OIDTVAuthorizationResponse.m b/Sources/AppAuthTV/OIDTVAuthorizationResponse.m new file mode 100644 index 000000000..b45fc85f3 --- /dev/null +++ b/Sources/AppAuthTV/OIDTVAuthorizationResponse.m @@ -0,0 +1,190 @@ +/*! @file OIDTVAuthorizationResponse.m + @brief AppAuth iOS 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 "OIDTVAuthorizationResponse.h" + +#import "OIDDefines.h" +#import "OIDFieldMapping.h" + +#import "OIDTVTokenRequest.h" +#import "OIDTVAuthorizationRequest.h" + +/*! @brief The key for the @c verificationURI property in the incoming parameters and for + @c NSSecureCoding. + */ +static NSString *const kVerificationURIKey = @"verification_uri"; + +/*! @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 kVerificationURICompleteKey = @"verification_uri_complete"; + +/*! @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 OIDTVAuthorizationResponse + +/*! @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[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] = + [[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]]; + + // 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; +} + +#pragma mark - Initializers + +- (instancetype)initWithRequest:(OIDTVAuthorizationRequest *)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, verificationURI: %@, verificationURIComplete: %@, " + "userCode: \"%@\", deviceCode: " + "\"%@\", interval: %@, expirationDate: %@, " + "additionalParameters: %@, " + "request: %@>", + NSStringFromClass([self class]), + (void *)self, + _verificationURI, + _verificationURIComplete, + _userCode, + _deviceCode, + _interval, + _expirationDate, + self.additionalParameters, + self.request]; +} + +#pragma mark - + +- (OIDTVTokenRequest *)tokenPollRequest { + return [self tokenPollRequestWithAdditionalParameters:nil additionalHeaders:nil]; +} + +- (OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: + (NSDictionary *)additionalParameters { + return [[OIDTVTokenRequest alloc] + initWithConfiguration:(OIDTVServiceConfiguration *)self.request.configuration + deviceCode:_deviceCode + clientID:self.request.clientID + clientSecret:self.request.clientSecret + 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/Sources/AppAuthTV/OIDTVAuthorizationService.h b/Sources/AppAuthTV/OIDTVAuthorizationService.h new file mode 100644 index 000000000..93158579c --- /dev/null +++ b/Sources/AppAuthTV/OIDTVAuthorizationService.h @@ -0,0 +1,111 @@ +/*! @file OIDTVAuthorizationService.h + @brief AppAuth iOS 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 OIDAuthState; +@class OIDTVAuthorizationRequest; +@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 + the user so they can action the request. + @param error The error if an error occurred. + */ +typedef void (^OIDTVAuthorizationInitialization)(OIDTVAuthorizationResponse *_Nullable response, + NSError *_Nullable error); + +/*! @brief The block that is called when the TV authorization has completed. + @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 (^OIDTVAuthorizationCompletion) + (OIDAuthState *_Nullable authorization, + NSError *_Nullable error); + +/*! @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 (^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. + @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 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://tools.ietf.org/html/rfc8628 + */ ++ (OIDTVAuthorizationCancelBlock)authorizeTVRequest:(OIDTVAuthorizationRequest *)request + initialization:(OIDTVAuthorizationInitialization)initialization + completion:(OIDTVAuthorizationCompletion)completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/AppAuthTV/OIDTVAuthorizationService.m b/Sources/AppAuthTV/OIDTVAuthorizationService.m new file mode 100644 index 000000000..eaa4d6bfc --- /dev/null +++ b/Sources/AppAuthTV/OIDTVAuthorizationService.m @@ -0,0 +1,285 @@ +/*! @file OIDTVAuthorizationService.m + @brief AppAuth iOS 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 "OIDTVAuthorizationService.h" + +#import "OIDAuthorizationService.h" +#import "OIDAuthState.h" +#import "OIDDefines.h" +#import "OIDErrorUtilities.h" +#import "OIDServiceDiscovery.h" +#import "OIDURLQueryComponent.h" +#import "OIDURLSessionProvider.h" + +#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 + */ +NSString *const kErrorCodeAuthorizationPending = @"authorization_pending"; + +/*! @brief The slow down error code. + @see https://tools.ietf.org/html/rfc8628#section-3.5 + */ +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 + 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. + OIDTVAuthorizationCancelBlock 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. + OIDTVAuthorizationResponse *TVAuthorizationResponse = + [[OIDTVAuthorizationResponse 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. + 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 { + // 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]; + completion(authState, 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/Sources/AppAuthTV/OIDTVServiceConfiguration.h b/Sources/AppAuthTV/OIDTVServiceConfiguration.h new file mode 100644 index 000000000..2afea6cac --- /dev/null +++ b/Sources/AppAuthTV/OIDTVServiceConfiguration.h @@ -0,0 +1,61 @@ +/*! @file OIDTVServiceConfiguration.h + @brief AppAuth iOS 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 "OIDServiceConfiguration.h" + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Configuration for authorizing the user with the @c OIDTVAuthorizationService. + */ +@interface OIDTVServiceConfiguration : OIDServiceConfiguration + +/*! @brief The device authorization endpoint URI. + */ +@property(nonatomic, readonly) NSURL *deviceAuthorizationEndpoint; + +/*! @internal + @brief Unavailable. Please use + @c initWithDeviceAuthorizationEndpoint:tokenEndpoint: + */ +- (instancetype)init NS_UNAVAILABLE; + +/*! @internal + @brief Unavailable. Please use + @c initWithDeviceAuthorizationEndpoint:tokenEndpoint: + */ +- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint 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 deviceAuthorizationEndpoint The device authorization endpoint URI. + @param tokenEndpoint The token exchange and refresh endpoint URI. + */ +- (instancetype)initWithDeviceAuthorizationEndpoint:(NSURL *)deviceAuthorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/AppAuthTV/OIDTVServiceConfiguration.m b/Sources/AppAuthTV/OIDTVServiceConfiguration.m new file mode 100644 index 000000000..202aa9104 --- /dev/null +++ b/Sources/AppAuthTV/OIDTVServiceConfiguration.m @@ -0,0 +1,105 @@ +/*! @file OIDTVServiceConfiguration.m + @brief AppAuth iOS 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 "OIDTVServiceConfiguration.h" + +#import "OIDDefines.h" +#import "OIDServiceDiscovery.h" + +/*! @brief The key for the @c deviceAuthorizationEndpoint property. + */ +static NSString *const kDeviceAuthorizationEndpointKey = @"deviceAuthorizationEndpoint"; + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDTVServiceConfiguration () + +/*! @brief Designated initializer. + @param aDecoder NSCoder to unserialize the object from. + */ +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; + +@end + +@implementation OIDTVServiceConfiguration + +- (instancetype)init + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithDeviceAuthorizationEndpoint:tokenEndpoint:)) + +- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithDeviceAuthorizationEndpoint: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 { + _deviceAuthorizationEndpoint = [discoveryDocument.deviceAuthorizationEndpoint copy]; + } + } + return self; +} + +- (instancetype)initWithDeviceAuthorizationEndpoint:(NSURL *)deviceAuthorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint { + self = [super initWithAuthorizationEndpoint:[[NSURL alloc] initWithString:@""] + tokenEndpoint:tokenEndpoint]; + if (self) { + _deviceAuthorizationEndpoint = [deviceAuthorizationEndpoint copy]; + } + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + NSURL *deviceAuthorizationEndpoint = + [aDecoder decodeObjectOfClass:[NSURL class] forKey:kDeviceAuthorizationEndpointKey]; + _deviceAuthorizationEndpoint = deviceAuthorizationEndpoint; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [super encodeWithCoder:aCoder]; + [aCoder encodeObject:_deviceAuthorizationEndpoint forKey:kDeviceAuthorizationEndpointKey]; +} + +#pragma mark - description + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, deviceAuthorizationEndpoint: %@ tokenEndpoint: %@>", + NSStringFromClass([self class]), + (void *)self, + _deviceAuthorizationEndpoint, + self.tokenEndpoint]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/AppAuthTV/OIDTVTokenRequest.h b/Sources/AppAuthTV/OIDTVTokenRequest.h new file mode 100644 index 000000000..8643b4cc5 --- /dev/null +++ b/Sources/AppAuthTV/OIDTVTokenRequest.h @@ -0,0 +1,122 @@ +/*! @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:additionalHeaders: + or @c initWithCoder:. +*/ +- (instancetype)init NS_UNAVAILABLE; + +/*! @internal + @brief Unavailable. Please use + @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters:additionalHeaders: + 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 + additionalHeaders: + (nullable NSDictionary *)additionalHeaders + NS_UNAVAILABLE; + +/*! @internal + @brief Unavailable. Please use + @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters:additionalHeaders: + 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 + additionalHeaders: + (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. + @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 + clientID:(NSString *)clientID + clientSecret:(nullable NSString *)clientSecret + additionalParameters: + (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders + 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/Sources/AppAuthTV/OIDTVTokenRequest.m b/Sources/AppAuthTV/OIDTVTokenRequest.m new file mode 100644 index 000000000..0878c7934 --- /dev/null +++ b/Sources/AppAuthTV/OIDTVTokenRequest.m @@ -0,0 +1,174 @@ +/*! @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: + additionalHeaders: + )) + +- (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 + additionalHeaders: + (nullable NSDictionary *)additionalHeaders + OID_UNAVAILABLE_USE_INITIALIZER(@selector + (initWithConfiguration: + deviceCode: + clientID: + clientSecret: + additionalParameters: + additionalHeaders: + )) + +- (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 + 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 { + return [self initWithConfiguration:configuration + deviceCode:deviceCode + clientID:clientID + clientSecret:clientSecret + additionalParameters:additionalParameters + additionalHeaders:nil]; +} + +- (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration + deviceCode:(NSString *)deviceCode + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret + additionalParameters:(NSDictionary *)additionalParameters + additionalHeaders:(NSDictionary *)additionalHeaders { + self = [super initWithConfiguration:configuration + grantType:kOIDTVDeviceTokenGrantType + authorizationCode:nil + redirectURL:[[NSURL alloc] initWithString:@""] + clientID:clientID + clientSecret:clientSecret + scope:nil + refreshToken:nil + codeVerifier:nil + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; + + 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 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 99% rename from Source/Framework/AppAuth.h rename to Sources/Framework/AppAuth.h index df923cc6e..3de0ed95b 100644 --- a/Source/Framework/AppAuth.h +++ b/Sources/Framework/AppAuth.h @@ -60,7 +60,7 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import "AppAuth/OIDExternalUserAgentCatalyst.h" -#elif TARGET_OS_MAC +#elif TARGET_OS_OSX #import #import #import 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/Sources/TVFramework/AppAuthTV.h b/Sources/TVFramework/AppAuthTV.h new file mode 100644 index 000000000..ab3c1e604 --- /dev/null +++ b/Sources/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/Sources/TVFramework/Info.plist b/Sources/TVFramework/Info.plist new file mode 100644 index 000000000..9bcb24442 --- /dev/null +++ b/Sources/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) + + diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.h b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.h new file mode 100644 index 000000000..d4198a448 --- /dev/null +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.h @@ -0,0 +1,60 @@ +/*! @file OIDTVAuthorizationRequestTests.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 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 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 deviceAuthorizationEndpoint + */ +- (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..a5ccd6e1d --- /dev/null +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationRequestTests.m @@ -0,0 +1,287 @@ +/*! @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" + +#if SWIFT_PACKAGE +@import AppAuthTV; +#else +#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 +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + +/*! @brief Test value for the @c deviceAuthorizationEndpoint property. + */ +static NSString *const kTestDeviceAuthorizationEndpoint = @"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 *deviceAuthorizationEndpoint = [NSURL URLWithString:kTestDeviceAuthorizationEndpoint]; + + OIDTVServiceConfiguration *configuration = + [[OIDTVServiceConfiguration alloc] initWithDeviceAuthorizationEndpoint:deviceAuthorizationEndpoint + 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 *authRequestDeviceAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)authRequest.configuration).deviceAuthorizationEndpoint; + + 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(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 deviceAuthorizationEndpoint + */ +- (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 *authRequestCopyDeviceAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)authRequestCopy.configuration).deviceAuthorizationEndpoint; + + 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 deviceAuthorizationEndpoint + */ +- (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 *authRequestCopyDeviceAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)authRequestCopy.configuration).deviceAuthorizationEndpoint; + + XCTAssertEqualObjects(authRequestCopyDeviceAuthorizationEndpoint, + serviceConfiguration.deviceAuthorizationEndpoint); +} + +/*! @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.deviceAuthorizationEndpoint); + + 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.deviceAuthorizationEndpoint); + + 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.deviceAuthorizationEndpoint); + + NSDictionary *bodyParameters = + [self bodyParametersFromURLRequest:URLRequest]; + NSDictionary *expectedParameters = @{ + kTestClientIDKey : kTestClientID, + kTestScopeKey : testScopeStringPercentEncoded, + kTestAdditionalParameterKey : kTestAdditionalParameterValue + }; + + XCTAssertEqualObjects(bodyParameters, expectedParameters); +} + +@end + +#pragma GCC diagnostic pop diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h new file mode 100644 index 000000000..2ffbc5775 --- /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 testTokenPollRequestWithAdditionalParametersAdditionalHeaders method with one additional + parameter and one additional header. + */ +- (void)testTokenPollRequestWithAdditionalParametersAdditionalHeaders; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m new file mode 100644 index 000000000..436232322 --- /dev/null +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m @@ -0,0 +1,295 @@ +/*! @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 "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. + */ +static NSString *const kTestDeviceAuthorizationEndpoint = @"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 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"; + +/*! @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 *deviceAuthorizationEndpoint = [NSURL URLWithString:kTestDeviceAuthorizationEndpoint]; + + OIDTVServiceConfiguration *configuration = + [[OIDTVServiceConfiguration alloc] initWithDeviceAuthorizationEndpoint:deviceAuthorizationEndpoint + 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 testTokenPollRequestWithAdditionalParametersAdditionalHeaders method with one additional + parameter and one additional header. + */ +- (void)testTokenPollRequestWithAdditionalParametersAdditionalHeaders { + OIDTVAuthorizationResponse *testResponse = [self testAuthorizationResponse]; + + NSDictionary *testAdditionalParameters = + @{kTestAdditionalParameterKey : kTestAdditionalParameterValue}; + + NSDictionary *testAdditionalHeaders = + @{kTestAdditionalHeaderKey : kTestAdditionalHeaderValue}; + + OIDTVTokenRequest *pollRequest = + [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.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..cd56344fd --- /dev/null +++ b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m @@ -0,0 +1,201 @@ +/*! @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 "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 +// raised by our use of the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + +/*! @brief Test value for the @c deviceAuthorizationEndpoint property. + */ +static NSString *const kTestDeviceAuthorizationEndpoint = + @"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 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"; + +/*! @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 *deviceAuthorizationEndpoint = [NSURL URLWithString:kTestDeviceAuthorizationEndpoint]; + + OIDTVServiceConfiguration *configuration = [[OIDTVServiceConfiguration alloc] + initWithDeviceAuthorizationEndpoint:deviceAuthorizationEndpoint + tokenEndpoint:tokenEndpoint]; + return configuration; +} + +- (OIDTVTokenRequest *)testTokenRequest { + OIDTVServiceConfiguration *service = [self testServiceConfiguration]; + return [[OIDTVTokenRequest alloc] + initWithConfiguration:service + deviceCode:kDeviceCodeValue + clientID:kTestClientID + clientSecret:kTestClientSecret + additionalParameters:@{kTestAdditionalParameterKey : kTestAdditionalParameterValue} + additionalHeaders:@{kTestAdditionalHeaderKey : kTestAdditionalHeaderValue}]; +} + +/*! @brief Tests the initializer +*/ +- (void)testInitializer { + OIDTVTokenRequest *request = [self testTokenRequest]; + NSURL *requestDeviceAuthorizationEndpoint = + ((OIDTVServiceConfiguration *)request.configuration).deviceAuthorizationEndpoint; + + XCTAssertEqualObjects(requestDeviceAuthorizationEndpoint, + [self testServiceConfiguration].deviceAuthorizationEndpoint); + XCTAssertEqualObjects(request.deviceCode, kDeviceCodeValue); + XCTAssertEqualObjects(request.grantType, kOIDTVDeviceTokenGrantType); + XCTAssertEqualObjects(request.clientID, kTestClientID); + 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 + * 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 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 4d7c3a8b7..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" @@ -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/OIDAuthorizationRequestTests.m b/UnitTests/OIDAuthorizationRequestTests.m index 06bfc6c13..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 @@ -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 }; 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/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]; 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 574cd794f..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 @@ -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/"; @@ -39,6 +75,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,12 +140,13 @@ + (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", 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", @@ -152,47 +190,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" @@ -398,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 @@ -489,6 +625,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) diff --git a/UnitTests/OIDTokenRequestTests.m b/UnitTests/OIDTokenRequestTests.m index 4211ef70a..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 @@ -48,6 +48,22 @@ */ 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 additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderKey2 = @"C"; + +/*! @brief Test value for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderValue2 = @"3"; + @implementation OIDTokenRequestTests + (OIDTokenRequest *)testInstance { @@ -56,6 +72,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 +85,8 @@ + (OIDTokenRequest *)testInstance { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; return request; } @@ -76,6 +96,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 +109,8 @@ + (OIDTokenRequest *)testInstanceCodeExchange { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; return request; } @@ -96,6 +120,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 +133,8 @@ + (OIDTokenRequest *)testInstanceCodeExchangeClientAuth { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; return request; } @@ -116,6 +144,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 +157,34 @@ + (OIDTokenRequest *)testInstanceRefresh { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; + 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; } @@ -157,11 +215,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 +245,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 +270,13 @@ - (void)testSecureCoding { XCTAssertNotNil(request.additionalParameters, @""); XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue, @""); + 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]; @@ -224,6 +298,13 @@ - (void)testSecureCoding { XCTAssertNotNil(requestCopy.additionalParameters, @""); XCTAssertEqualObjects(requestCopy.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue, @""); + XCTAssertNotNil(requestCopy.additionalHeaders, @""); + XCTAssertEqualObjects(requestCopy.additionalHeaders[kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue, @""); + + NSURLRequest *urlrequestCopy = [requestCopy URLRequest]; + XCTAssertEqualObjects([urlrequestCopy.allHTTPHeaderFields objectForKey:kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue); } - (void)testURLRequestNoClientAuth { @@ -248,6 +329,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 +340,19 @@ - (void)testAuthorizationCodeNullRedirectURL { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters], @""); + additionalParameters:additionalParameters + 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 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