diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index ab708c81c..eb21c6f2b 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -377,6 +377,23 @@ A6339DAB20321AE50043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6339DAC20321AE70043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6339DAD20321AEB0043D1C9 /* OIDAuthorizationFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6CEB11A2007E49C009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6CEB11B2007E49D009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6CEB11C2007E49E009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6CEB11D2007E49F009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6CEB11E2007E4A1009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6CEB11F2007E4A2009D492A /* OIDEndSessionRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */; }; + A6DEAB832017A7030022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB842017A7040022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB852017A7050022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB862017A7060022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB872017A70B0022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB882017A70B0022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB892017A70C0022AC32 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + A6DEAB8A2017A7140022AC32 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + A6DEAB8B2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + A6DEAB8C2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + A6DEAB8D2017A7170022AC32 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; A6DEAB9B2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -392,6 +409,31 @@ A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */; }; + A6DEABB02018ECE80022AC32 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB12018ECE80022AC32 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB22018ECE90022AC32 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB32018ECE90022AC32 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB42018ECF20022AC32 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB52018ECF30022AC32 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB62018ECF30022AC32 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DEABB72018ECF40022AC32 /* OIDEndSessionResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B39FA9C22052C02B00BD9778 /* OIDEndSessionFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = B39FA9C12052C02700BD9778 /* OIDEndSessionFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B39FA9C42052C02D00BD9778 /* OIDEndSessionFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = B39FA9C12052C02700BD9778 /* OIDEndSessionFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B39FA9C52052C02E00BD9778 /* OIDEndSessionFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = B39FA9C12052C02700BD9778 /* OIDEndSessionFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B39FA9C62052C02E00BD9778 /* OIDEndSessionFlowSession.h in Headers */ = {isa = PBXBuildFile; fileRef = B39FA9C12052C02700BD9778 /* OIDEndSessionFlowSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B39FA9C72052C03900BD9778 /* OIDEndSessionFlowSession.m in Sources */ = {isa = PBXBuildFile; fileRef = B39FA9C02052C02700BD9778 /* OIDEndSessionFlowSession.m */; }; + B39FA9C82052C03A00BD9778 /* OIDEndSessionFlowSession.m in Sources */ = {isa = PBXBuildFile; fileRef = B39FA9C02052C02700BD9778 /* OIDEndSessionFlowSession.m */; }; + B39FA9C92052C03A00BD9778 /* OIDEndSessionFlowSession.m in Sources */ = {isa = PBXBuildFile; fileRef = B39FA9C02052C02700BD9778 /* OIDEndSessionFlowSession.m */; }; + B39FA9CA2052C03B00BD9778 /* OIDEndSessionFlowSession.m in Sources */ = {isa = PBXBuildFile; fileRef = B39FA9C02052C02700BD9778 /* OIDEndSessionFlowSession.m */; }; + B39FA9CB2052C03C00BD9778 /* OIDEndSessionFlowSession.m in Sources */ = {isa = PBXBuildFile; fileRef = B39FA9C02052C02700BD9778 /* OIDEndSessionFlowSession.m */; }; + B39FA9CC2052C03D00BD9778 /* OIDEndSessionFlowSession.m in Sources */ = {isa = PBXBuildFile; fileRef = B39FA9C02052C02700BD9778 /* OIDEndSessionFlowSession.m */; }; + B39FA9CD2052C03D00BD9778 /* OIDEndSessionFlowSession.m in Sources */ = {isa = PBXBuildFile; fileRef = B39FA9C02052C02700BD9778 /* OIDEndSessionFlowSession.m */; }; + B39FA9CE2052C03E00BD9778 /* OIDEndSessionFlowSession.m in Sources */ = {isa = PBXBuildFile; fileRef = B39FA9C02052C02700BD9778 /* OIDEndSessionFlowSession.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 */; }; + CF37C0711F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + CF6431F41F228A980075B6B5 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -575,11 +617,19 @@ 60140F841DE43C8C00DA0DC3 /* OIDRegistrationResponseTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRegistrationResponseTests.h; sourceTree = ""; }; 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRegistrationResponseTests.m; sourceTree = ""; }; A6339DA3203211320043D1C9 /* OIDAuthorizationFlowSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDAuthorizationFlowSession.h; sourceTree = ""; }; + A6CEB1172007E384009D492A /* OIDEndSessionRequestTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionRequestTests.h; sourceTree = ""; }; + A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionRequestTests.m; sourceTree = ""; }; A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgent.h; sourceTree = ""; }; A6DEAB992018E4A20022AC32 /* OIDExternalUserAgentSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentSession.h; sourceTree = ""; }; A6DEAB9A2018E4A20022AC32 /* OIDExternalUserAgentRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentRequest.h; sourceTree = ""; }; A6DEABA82018E5B50022AC32 /* OIDExternalUserAgentIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OIDExternalUserAgentIOS.m; path = iOS/OIDExternalUserAgentIOS.m; sourceTree = ""; }; A6DEABA92018E5B50022AC32 /* OIDExternalUserAgentIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OIDExternalUserAgentIOS.h; path = iOS/OIDExternalUserAgentIOS.h; sourceTree = ""; }; + B39FA9C02052C02700BD9778 /* OIDEndSessionFlowSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionFlowSession.m; sourceTree = ""; }; + B39FA9C12052C02700BD9778 /* OIDEndSessionFlowSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionFlowSession.h; sourceTree = ""; }; + CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionRequest.h; sourceTree = ""; }; + CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionRequest.m; sourceTree = ""; }; + CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDEndSessionResponse.h; sourceTree = ""; }; + CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDEndSessionResponse.m; sourceTree = ""; }; F6F60FB01D2BFEFE00325CB3 /* OIDAuthState+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OIDAuthState+IOS.m"; path = "iOS/OIDAuthState+IOS.m"; sourceTree = ""; }; F6F60FB11D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OIDAuthorizationService+IOS.m"; path = "iOS/OIDAuthorizationService+IOS.m"; sourceTree = ""; }; F6F60FB31D2BFEFE00325CB3 /* OIDAuthorizationService+IOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OIDAuthorizationService+IOS.h"; path = "iOS/OIDAuthorizationService+IOS.h"; sourceTree = ""; }; @@ -773,6 +823,12 @@ A6DEAB982018E4A20022AC32 /* OIDExternalUserAgent.h */, 341741C31C5D8243000EF209 /* OIDFieldMapping.h */, 341741C41C5D8243000EF209 /* OIDFieldMapping.m */, + B39FA9C12052C02700BD9778 /* OIDEndSessionFlowSession.h */, + B39FA9C02052C02700BD9778 /* OIDEndSessionFlowSession.m */, + CF6431F21F228A980075B6B5 /* OIDEndSessionResponse.h */, + CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */, + CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */, + CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */, 60140F7E1DE4335200DA0DC3 /* OIDRegistrationResponse.h */, 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */, 60140F7D1DE42E3000DA0DC3 /* OIDRegistrationRequest.h */, @@ -816,6 +872,8 @@ 60140F811DE43B4D00DA0DC3 /* OIDRegistrationRequestTests.h */, 341742061C5D82D3000EF209 /* OIDGrantTypesTests.m */, 341742071C5D82D3000EF209 /* OIDResponseTypesTests.m */, + A6CEB1172007E384009D492A /* OIDEndSessionRequestTests.h */, + A6CEB1182007E384009D492A /* OIDEndSessionRequestTests.m */, 341742081C5D82D3000EF209 /* OIDScopesTests.m */, 341742091C5D82D3000EF209 /* OIDServiceConfigurationTests.h */, 3417420A1C5D82D3000EF209 /* OIDServiceConfigurationTests.m */, @@ -906,7 +964,9 @@ 343AAAF71E83499000F9D36E /* OIDTokenRequest.h in Headers */, 343AAAF41E83499000F9D36E /* OIDScopeUtilities.h in Headers */, 343AAAF81E83499000F9D36E /* OIDTokenResponse.h in Headers */, + A6DEABB42018ECF20022AC32 /* OIDEndSessionResponse.h in Headers */, 343AAAF61E83499000F9D36E /* OIDServiceDiscovery.h in Headers */, + B39FA9C22052C02B00BD9778 /* OIDEndSessionFlowSession.h in Headers */, 343AAAF11E83499000F9D36E /* OIDGrantTypes.h in Headers */, 343AAA6D1E83466B00F9D36E /* OIDAuthState+IOS.h in Headers */, 343AAAEF1E83499000F9D36E /* OIDRegistrationResponse.h in Headers */, @@ -916,6 +976,7 @@ 343AAAE91E83499000F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAAE51E83499000F9D36E /* OIDAuthorizationService.h in Headers */, 343AAAF01E83499000F9D36E /* OIDRegistrationRequest.h in Headers */, + A6DEABB02018ECE80022AC32 /* OIDEndSessionRequest.h in Headers */, 343AAAE71E83499000F9D36E /* OIDAuthState.h in Headers */, 343AAAED1E83499000F9D36E /* OIDErrorUtilities.h in Headers */, 343AAA6C1E83466B00F9D36E /* OIDAuthorizationService+IOS.h in Headers */, @@ -935,6 +996,7 @@ 343AAB091E83499100F9D36E /* OIDGrantTypes.h in Headers */, 343AAAFF1E83499100F9D36E /* OIDAuthState.h in Headers */, A6DEABA52018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */, + A6DEABB52018ECF30022AC32 /* OIDEndSessionResponse.h in Headers */, 343AAB0D1E83499100F9D36E /* OIDServiceConfiguration.h in Headers */, A6DEABA02018E4B00022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAAFD1E83499100F9D36E /* OIDAuthorizationService.h in Headers */, @@ -944,8 +1006,10 @@ 343AAB111E83499100F9D36E /* OIDTokenUtilities.h in Headers */, 343AAB0A1E83499100F9D36E /* OIDResponseTypes.h in Headers */, A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */, + B39FA9C42052C02D00BD9778 /* OIDEndSessionFlowSession.h in Headers */, 343AAB0B1E83499100F9D36E /* OIDScopes.h in Headers */, 343AAB9B1E834A8800F9D36E /* AppAuth.h in Headers */, + A6DEABB12018ECE80022AC32 /* OIDEndSessionRequest.h in Headers */, 343AAB001E83499100F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, 343AAB081E83499100F9D36E /* OIDRegistrationRequest.h in Headers */, 343AAB101E83499100F9D36E /* OIDTokenResponse.h in Headers */, @@ -966,6 +1030,7 @@ 343AAB211E83499200F9D36E /* OIDGrantTypes.h in Headers */, 343AAB171E83499200F9D36E /* OIDAuthState.h in Headers */, A6DEABA62018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */, + A6DEABB62018ECF30022AC32 /* OIDEndSessionResponse.h in Headers */, 343AAB251E83499200F9D36E /* OIDServiceConfiguration.h in Headers */, A6DEABA22018E4B60022AC32 /* OIDExternalUserAgentRequest.h in Headers */, 343AAB151E83499200F9D36E /* OIDAuthorizationService.h in Headers */, @@ -975,8 +1040,10 @@ 343AAB291E83499200F9D36E /* OIDTokenUtilities.h in Headers */, 343AAB221E83499200F9D36E /* OIDResponseTypes.h in Headers */, A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */, + B39FA9C52052C02E00BD9778 /* OIDEndSessionFlowSession.h in Headers */, 343AAB231E83499200F9D36E /* OIDScopes.h in Headers */, 343AAB9C1E834A8900F9D36E /* AppAuth.h in Headers */, + A6DEABB22018ECE90022AC32 /* OIDEndSessionRequest.h in Headers */, 343AAB181E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, 343AAB201E83499200F9D36E /* OIDRegistrationRequest.h in Headers */, 343AAB281E83499200F9D36E /* OIDTokenResponse.h in Headers */, @@ -996,9 +1063,11 @@ 343AAB9D1E834A8A00F9D36E /* AppAuth.h in Headers */, A6DEABA72018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */, 343AAADF1E83494400F9D36E /* OIDExternalUserAgentMac.h in Headers */, + B39FA9C62052C02E00BD9778 /* OIDEndSessionFlowSession.h in Headers */, 343AAAE01E83494400F9D36E /* OIDAuthState+Mac.h in Headers */, 343AAADD1E83494400F9D36E /* OIDRedirectHTTPHandler.h in Headers */, 343AAB3C1E83499200F9D36E /* OIDScopeUtilities.h in Headers */, + A6DEABB32018ECE90022AC32 /* OIDEndSessionRequest.h in Headers */, 343AAB3F1E83499200F9D36E /* OIDTokenRequest.h in Headers */, 343AAB411E83499200F9D36E /* OIDTokenUtilities.h in Headers */, A6DEABA32018E4B70022AC32 /* OIDExternalUserAgentRequest.h in Headers */, @@ -1009,6 +1078,7 @@ 343AAB3D1E83499200F9D36E /* OIDServiceConfiguration.h in Headers */, 343AAB351E83499200F9D36E /* OIDErrorUtilities.h in Headers */, 343AAB391E83499200F9D36E /* OIDGrantTypes.h in Headers */, + A6DEABB72018ECF40022AC32 /* OIDEndSessionResponse.h in Headers */, 343AAB341E83499200F9D36E /* OIDError.h in Headers */, 343AAB401E83499200F9D36E /* OIDTokenResponse.h in Headers */, 343AAB3A1E83499200F9D36E /* OIDResponseTypes.h in Headers */, @@ -1452,6 +1522,7 @@ 341310C21E6F944B00D5DEE5 /* OIDError.m in Sources */, 341310BE1E6F943C00D5DEE5 /* OIDClientMetadataParameters.m in Sources */, 34FEA6AF1DB6E083005C9212 /* OIDLoopbackHTTPServer.m in Sources */, + B39FA9C82052C03A00BD9778 /* OIDEndSessionFlowSession.m in Sources */, 341310C61E6F944B00D5DEE5 /* OIDRegistrationRequest.m in Sources */, 340DAEBC1D582AF100EC285B /* OIDRedirectHTTPHandler.m in Sources */, 340DAE5D1D5821AB00EC285B /* OIDAuthState.m in Sources */, @@ -1463,6 +1534,8 @@ 341310CC1E6F944B00D5DEE5 /* OIDServiceDiscovery.m in Sources */, 341310CA1E6F944B00D5DEE5 /* OIDScopeUtilities.m in Sources */, 340DAE5C1D5821AB00EC285B /* OIDAuthorizationService.m in Sources */, + CF37C06F1F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */, + A6DEAB832017A7030022AC32 /* OIDEndSessionResponse.m in Sources */, 341310CD1E6F944B00D5DEE5 /* OIDTokenRequest.m in Sources */, 341310C91E6F944B00D5DEE5 /* OIDScopes.m in Sources */, 341310CE1E6F944B00D5DEE5 /* OIDTokenResponse.m in Sources */, @@ -1484,6 +1557,7 @@ files = ( A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentIOS.m in Sources */, 341741E01C5D8243000EF209 /* OIDErrorUtilities.m in Sources */, + CF6431F41F228A980075B6B5 /* OIDEndSessionResponse.m in Sources */, 341741EA1C5D8243000EF209 /* OIDTokenUtilities.m in Sources */, 341741E21C5D8243000EF209 /* OIDGrantTypes.m in Sources */, 60140F7C1DE42E1000DA0DC3 /* OIDRegistrationRequest.m in Sources */, @@ -1493,8 +1567,10 @@ 341741E51C5D8243000EF209 /* OIDScopeUtilities.m in Sources */, 341741DC1C5D8243000EF209 /* OIDAuthorizationResponse.m in Sources */, 341741E61C5D8243000EF209 /* OIDServiceConfiguration.m in Sources */, + B39FA9C72052C03900BD9778 /* OIDEndSessionFlowSession.m in Sources */, 60140F7A1DE4276800DA0DC3 /* OIDClientMetadataParameters.m in Sources */, 341741DE1C5D8243000EF209 /* OIDAuthState.m in Sources */, + CF37C06E1F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */, 341741DD1C5D8243000EF209 /* OIDAuthorizationService.m in Sources */, 345AE747202D526900738D22 /* OIDExternalUserAgentIOSCustomBrowser.m in Sources */, 341741EB1C5D8243000EF209 /* OIDURLQueryComponent.m in Sources */, @@ -1523,6 +1599,7 @@ 341742181C5D82D3000EF209 /* OIDAuthorizationResponseTests.m in Sources */, 341742171C5D82D3000EF209 /* OIDAuthorizationRequestTests.m in Sources */, 0396974D1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m in Sources */, + A6CEB11A2007E49C009D492A /* OIDEndSessionRequestTests.m in Sources */, 3417421A1C5D82D3000EF209 /* OIDGrantTypesTests.m in Sources */, 3417421B1C5D82D3000EF209 /* OIDResponseTypesTests.m in Sources */, 60140F831DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m in Sources */, @@ -1539,6 +1616,7 @@ files = ( 341AA50A1E7F3A9B00FCA5C6 /* OIDScopesTests.m in Sources */, 341AA50F1E7F3A9B00FCA5C6 /* OIDURLQueryComponentTests.m in Sources */, + A6CEB11B2007E49D009D492A /* OIDEndSessionRequestTests.m in Sources */, 341AA50B1E7F3A9B00FCA5C6 /* OIDServiceConfigurationTests.m in Sources */, 341AA50C1E7F3A9B00FCA5C6 /* OIDServiceDiscoveryTests.m in Sources */, 341AA5071E7F3A9B00FCA5C6 /* OIDAuthStateTests.m in Sources */, @@ -1559,6 +1637,7 @@ files = ( 341AA4FD1E7F3A9400FCA5C6 /* OIDScopesTests.m in Sources */, 341AA5021E7F3A9400FCA5C6 /* OIDURLQueryComponentTests.m in Sources */, + A6CEB11C2007E49E009D492A /* OIDEndSessionRequestTests.m in Sources */, 341AA4FE1E7F3A9400FCA5C6 /* OIDServiceConfigurationTests.m in Sources */, 341AA4FF1E7F3A9400FCA5C6 /* OIDServiceDiscoveryTests.m in Sources */, 341AA4FA1E7F3A9400FCA5C6 /* OIDAuthStateTests.m in Sources */, @@ -1577,6 +1656,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B39FA9C92052C03A00BD9778 /* OIDEndSessionFlowSession.m in Sources */, 341310DA1E6F944D00D5DEE5 /* OIDScopes.m in Sources */, 341310D51E6F944D00D5DEE5 /* OIDFieldMapping.m in Sources */, 341E709B1DE18796004353C1 /* OIDAuthState.m in Sources */, @@ -1590,12 +1670,14 @@ 341310D31E6F944D00D5DEE5 /* OIDError.m in Sources */, 341310DE1E6F944D00D5DEE5 /* OIDTokenRequest.m in Sources */, 341310DF1E6F944D00D5DEE5 /* OIDTokenResponse.m in Sources */, + A6DEAB842017A7040022AC32 /* OIDEndSessionResponse.m in Sources */, 341310DC1E6F944D00D5DEE5 /* OIDServiceConfiguration.m in Sources */, 341310BF1E6F943C00D5DEE5 /* OIDClientMetadataParameters.m in Sources */, 341310E01E6F944D00D5DEE5 /* OIDTokenUtilities.m in Sources */, 341E709A1DE18796004353C1 /* OIDAuthorizationService.m in Sources */, 341310D91E6F944D00D5DEE5 /* OIDResponseTypes.m in Sources */, 341310E11E6F944D00D5DEE5 /* OIDURLQueryComponent.m in Sources */, + CF37C0701F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */, 341310D41E6F944D00D5DEE5 /* OIDErrorUtilities.m in Sources */, 341310D81E6F944D00D5DEE5 /* OIDGrantTypes.m in Sources */, ); @@ -1607,6 +1689,7 @@ files = ( A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentIOS.m in Sources */, 343AAA881E83478900F9D36E /* OIDFieldMapping.m in Sources */, + A6DEAB862017A7060022AC32 /* OIDEndSessionResponse.m in Sources */, 343AAA841E83478900F9D36E /* OIDAuthState.m in Sources */, 343AAA701E83467D00F9D36E /* OIDAuthState+IOS.m in Sources */, 343AAA921E83478900F9D36E /* OIDTokenResponse.m in Sources */, @@ -1616,9 +1699,11 @@ 343AAA831E83478900F9D36E /* OIDAuthorizationService.m in Sources */, 343AAA851E83478900F9D36E /* OIDClientMetadataParameters.m in Sources */, 343AAA931E83478900F9D36E /* OIDTokenUtilities.m in Sources */, + B39FA9CB2052C03C00BD9778 /* OIDEndSessionFlowSession.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 */, 343AAA891E83478900F9D36E /* OIDRegistrationResponse.m in Sources */, @@ -1650,6 +1735,7 @@ 343AAA7F1E8346B400F9D36E /* OIDRegistrationRequestTests.m in Sources */, 343AAA731E8346B400F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAA761E8346B400F9D36E /* OIDGrantTypesTests.m in Sources */, + A6CEB11D2007E49F009D492A /* OIDEndSessionRequestTests.m in Sources */, 343AAA741E8346B400F9D36E /* OIDAuthorizationResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1658,6 +1744,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B39FA9CC2052C03D00BD9778 /* OIDEndSessionFlowSession.m in Sources */, 343AAB771E8349B000F9D36E /* OIDScopes.m in Sources */, 343AAB721E8349B000F9D36E /* OIDFieldMapping.m in Sources */, 343AAB6E1E8349B000F9D36E /* OIDAuthState.m in Sources */, @@ -1669,8 +1756,10 @@ 343AAB781E8349B000F9D36E /* OIDScopeUtilities.m in Sources */, 343AAB731E8349B000F9D36E /* OIDRegistrationResponse.m in Sources */, 343AAB701E8349B000F9D36E /* OIDError.m in Sources */, + A6DEAB8B2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */, 343AAB7B1E8349B000F9D36E /* OIDTokenRequest.m in Sources */, 343AAB7C1E8349B000F9D36E /* OIDTokenResponse.m in Sources */, + A6DEAB872017A70B0022AC32 /* OIDEndSessionResponse.m in Sources */, 343AAB791E8349B000F9D36E /* OIDServiceConfiguration.m in Sources */, 343AAB6F1E8349B000F9D36E /* OIDClientMetadataParameters.m in Sources */, 343AAB7D1E8349B000F9D36E /* OIDTokenUtilities.m in Sources */, @@ -1686,6 +1775,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B39FA9CD2052C03D00BD9778 /* OIDEndSessionFlowSession.m in Sources */, 343AAB631E8349B000F9D36E /* OIDScopes.m in Sources */, 343AAB5E1E8349B000F9D36E /* OIDFieldMapping.m in Sources */, 343AAB5A1E8349B000F9D36E /* OIDAuthState.m in Sources */, @@ -1697,8 +1787,10 @@ 343AAB641E8349B000F9D36E /* OIDScopeUtilities.m in Sources */, 343AAB5F1E8349B000F9D36E /* OIDRegistrationResponse.m in Sources */, 343AAB5C1E8349B000F9D36E /* OIDError.m in Sources */, + A6DEAB8C2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */, 343AAB671E8349B000F9D36E /* OIDTokenRequest.m in Sources */, 343AAB681E8349B000F9D36E /* OIDTokenResponse.m in Sources */, + A6DEAB882017A70B0022AC32 /* OIDEndSessionResponse.m in Sources */, 343AAB651E8349B000F9D36E /* OIDServiceConfiguration.m in Sources */, 343AAB5B1E8349B000F9D36E /* OIDClientMetadataParameters.m in Sources */, 343AAB691E8349B000F9D36E /* OIDTokenUtilities.m in Sources */, @@ -1727,6 +1819,7 @@ 343AAB8B1E8349CE00F9D36E /* OIDRegistrationRequestTests.m in Sources */, 343AAB7F1E8349CE00F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAB821E8349CE00F9D36E /* OIDGrantTypesTests.m in Sources */, + A6CEB11E2007E4A1009D492A /* OIDEndSessionRequestTests.m in Sources */, 343AAB801E8349CE00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1738,7 +1831,9 @@ 343AAB481E8349AF00F9D36E /* OIDError.m in Sources */, 343AAADC1E83493D00F9D36E /* OIDAuthState+Mac.m in Sources */, 343AAADA1E83493D00F9D36E /* OIDAuthorizationService+Mac.m in Sources */, + B39FA9CE2052C03E00BD9778 /* OIDEndSessionFlowSession.m in Sources */, 343AAB4C1E8349AF00F9D36E /* OIDRegistrationRequest.m in Sources */, + A6DEAB8D2017A7170022AC32 /* OIDEndSessionRequest.m in Sources */, 343AAB451E8349AF00F9D36E /* OIDAuthorizationService.m in Sources */, 343AAB431E8349AF00F9D36E /* OIDAuthorizationRequest.m in Sources */, 343AAAD91E83493D00F9D36E /* OIDRedirectHTTPHandler.m in Sources */, @@ -1760,6 +1855,7 @@ 34AF736D1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */, 343AAB561E8349AF00F9D36E /* OIDURLQueryComponent.m in Sources */, 343AAB4E1E8349AF00F9D36E /* OIDResponseTypes.m in Sources */, + A6DEAB892017A70C0022AC32 /* OIDEndSessionResponse.m in Sources */, 343AAB4A1E8349AF00F9D36E /* OIDFieldMapping.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1781,6 +1877,7 @@ 343AAB991E8349CF00F9D36E /* OIDRegistrationRequestTests.m in Sources */, 343AAB8D1E8349CF00F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAB901E8349CF00F9D36E /* OIDGrantTypesTests.m in Sources */, + A6CEB11F2007E4A2009D492A /* OIDEndSessionRequestTests.m in Sources */, 343AAB8E1E8349CF00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1789,6 +1886,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B39FA9CA2052C03B00BD9778 /* OIDEndSessionFlowSession.m in Sources */, 3474240B1E7F4BA000D3E6D6 /* OIDScopes.m in Sources */, 347424061E7F4BA000D3E6D6 /* OIDFieldMapping.m in Sources */, 347424021E7F4BA000D3E6D6 /* OIDAuthState.m in Sources */, @@ -1802,12 +1900,14 @@ 347424041E7F4BA000D3E6D6 /* OIDError.m in Sources */, 3474240F1E7F4BA000D3E6D6 /* OIDTokenRequest.m in Sources */, 347424101E7F4BA000D3E6D6 /* OIDTokenResponse.m in Sources */, + A6DEAB852017A7050022AC32 /* OIDEndSessionResponse.m in Sources */, 3474240D1E7F4BA000D3E6D6 /* OIDServiceConfiguration.m in Sources */, 347424031E7F4BA000D3E6D6 /* OIDClientMetadataParameters.m in Sources */, 347424111E7F4BA000D3E6D6 /* OIDTokenUtilities.m in Sources */, 347424011E7F4BA000D3E6D6 /* OIDAuthorizationService.m in Sources */, 3474240A1E7F4BA000D3E6D6 /* OIDResponseTypes.m in Sources */, 347424121E7F4BA000D3E6D6 /* OIDURLQueryComponent.m in Sources */, + CF37C0711F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */, 347424051E7F4BA000D3E6D6 /* OIDErrorUtilities.m in Sources */, 347424091E7F4BA000D3E6D6 /* OIDGrantTypes.m in Sources */, ); diff --git a/Source/AppAuth.h b/Source/AppAuth.h index 06375affe..c54640adf 100644 --- a/Source/AppAuth.h +++ b/Source/AppAuth.h @@ -39,6 +39,8 @@ #import "OIDTokenResponse.h" #import "OIDTokenUtilities.h" #import "OIDURLSessionProvider.h" +#import "OIDEndSessionRequest.h" +#import "OIDEndSessionResponse.h" #if TARGET_OS_TV #elif TARGET_OS_WATCH diff --git a/Source/Framework/AppAuth.h b/Source/Framework/AppAuth.h index 94716b73d..006a6c030 100644 --- a/Source/Framework/AppAuth.h +++ b/Source/Framework/AppAuth.h @@ -29,6 +29,7 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import +#import #import #import #import @@ -44,6 +45,8 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[]; #import #import #import +#import +#import #if TARGET_OS_TV #elif TARGET_OS_WATCH diff --git a/Source/OIDAuthState.h b/Source/OIDAuthState.h index 3a98d378a..e00e64253 100644 --- a/Source/OIDAuthState.h +++ b/Source/OIDAuthState.h @@ -20,6 +20,8 @@ @class OIDAuthorizationRequest; @class OIDAuthorizationResponse; @class OIDAuthState; +@class OIDEndSessionRequest; +@class OIDEndSessionResponse; @class OIDRegistrationResponse; @class OIDTokenResponse; @class OIDTokenRequest; @@ -41,7 +43,7 @@ typedef void (^OIDAuthStateAction)(NSString *_Nullable accessToken, NSError *_Nullable error); /*! @brief The method called when the @c - OIDAuthState.authStateByPresentingAuthorizationRequest:presentingViewController:callback: + OIDAuthState.authStateByPresentingAuthorizationRequest:externalUserAgent:callback: method has completed or failed. @param authState The auth state, if the authorization request succeeded. @param error The error if an error occurred. @@ -49,6 +51,15 @@ typedef void (^OIDAuthStateAction)(NSString *_Nullable accessToken, typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authState, NSError *_Nullable error); +/*! @brief The method called when the @c OIDAuthState.presentEndSessionRequest:externalUserAgent:(id)externalUserAgent + callback has completed or failed. + method has completed or failed. + @param endSessionResponse The response to the end session request from the OP. + @param error The error if an error occurred. + */ +typedef void (^OIDEndSessionCallback)(OIDEndSessionResponse *_Nullable endSessionResponse, + NSError *_Nullable error); + /*! @brief A convenience class that retains the auth state between @c OIDAuthorizationResponse%s and @c OIDTokenResponse%s. */ @@ -284,6 +295,23 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt - (void)withFreshTokensPerformAction:(OIDAuthStateAction)action __deprecated_msg("Use OIDAuthState.performActionWithFreshTokens:"); + +/*! @brief Presents a RP-initiated logout using the external user agent. + @param endSessionRequest The end session request to present. + @param externalUserAgent A external user agent that can present an external user-agent request. + @param callback The method called when the request has completed or failed. + @return A @c OIDExternalUserAgentSession instance which will terminate when it receives a + @c OIDExternalUserAgentSession.cancel message, or after processing a + @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message. + @discussion Afer a successful ending of the session, it invalidates the @c OIDAuthState. + Remove references to this instance. Hence, a new authorization flow is required + to retrieve a new instance of @c OIDAuthState. R + @see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout + */ +- (id)presentEndSessionRequest:(OIDEndSessionRequest *)endSessionRequest + externalUserAgent:(id)externalUserAgent + callback:(OIDEndSessionCallback)callback; + @end NS_ASSUME_NONNULL_END diff --git a/Source/OIDAuthState.m b/Source/OIDAuthState.m index 06a515d44..a9b59d61c 100644 --- a/Source/OIDAuthState.m +++ b/Source/OIDAuthState.m @@ -24,8 +24,11 @@ #import "OIDAuthorizationResponse.h" #import "OIDAuthorizationService.h" #import "OIDDefines.h" +#import "OIDEndSessionFlowSession.h" +#import "OIDEndSessionRequest.h" #import "OIDError.h" #import "OIDErrorUtilities.h" +#import "OIDExternalUserAgent.h" #import "OIDRegistrationResponse.h" #import "OIDTokenRequest.h" #import "OIDTokenResponse.h" @@ -310,12 +313,16 @@ - (BOOL)isAuthorized { - (void)updateWithRegistrationResponse:(OIDRegistrationResponse *)registrationResponse { _lastRegistrationResponse = registrationResponse; + [self resetState]; + [self didChangeState]; +} + +- (void)resetState { _refreshToken = nil; _scope = nil; _lastAuthorizationResponse = nil; _lastTokenResponse = nil; _authorizationError = nil; - [self didChangeState]; } - (void)updateWithAuthorizationResponse:(nullable OIDAuthorizationResponse *)authorizationResponse @@ -532,6 +539,28 @@ - (BOOL)isTokenFresh { return tokenFresh; } +#pragma mark - End session + +- (id)presentEndSessionRequest:(OIDEndSessionRequest *)endSessionRequest + externalUserAgent:(id)externalUserAgent + callback:(OIDEndSessionCallback)callback { + OIDEndSessionFlowSession *session = [[OIDEndSessionFlowSession alloc] initWithRequest:endSessionRequest]; + OIDEndSessionCallback wrappedCallback = ^(OIDEndSessionResponse *_Nullable endSessionResponse, + NSError *_Nullable error) { + if (!error) { + [self resetState]; + [self didChangeState]; + } + + callback(endSessionResponse, error); + }; + + // presents the end session request + [session presentEndSessionWithExternalUserAgent:externalUserAgent callback:wrappedCallback]; + + return session; +} + @end diff --git a/Source/OIDEndSessionFlowSession.h b/Source/OIDEndSessionFlowSession.h new file mode 100644 index 000000000..d7750ae5a --- /dev/null +++ b/Source/OIDEndSessionFlowSession.h @@ -0,0 +1,53 @@ +/*! @file OIDEndSessionFlowSession.h + @brief AppAuth iOS SDK + @copyright + Copyright 2018 The AppAuth Authors. All Rights Reserved. + @copydetails + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "OIDAuthState.h" + +@protocol OIDExternalUserAgent; +@protocol OIDExternalUserAgentSession; + +@class OIDEndSessionRequest; +@class OIDEndSessionResponse; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Flow handler for the @c OIDEndSessionRequest + @see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout + */ +@interface OIDEndSessionFlowSession : NSObject + +/*! @internal + @brief Unavailable. Please use @c initWithRequest:. + */ +- (instancetype)init NS_UNAVAILABLE; + +/*! @brief Create new end session flow + @param request The end session request + */ +- (instancetype)initWithRequest:(OIDEndSessionRequest *)request NS_DESIGNATED_INITIALIZER; + +/*! @brief Present the sesion request on the provided @c OIDExternalUserAgent. + @param externalUserAgent The user agent for presenting the request. + @param endSessionFlowCallback The callback that will be triggered after the session is completed. + */ +- (void)presentEndSessionWithExternalUserAgent:(id)externalUserAgent + callback:(OIDEndSessionCallback)endSessionFlowCallback; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/OIDEndSessionFlowSession.m b/Source/OIDEndSessionFlowSession.m new file mode 100644 index 000000000..c8732cc63 --- /dev/null +++ b/Source/OIDEndSessionFlowSession.m @@ -0,0 +1,163 @@ +/*! @file OIDEndSessionFlowSession.m + @brief AppAuth iOS SDK + @copyright + Copyright 2018 The AppAuth Authors. All Rights Reserved. + @copydetails + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "OIDEndSessionFlowSession.h" + +#import "OIDDefines.h" +#import "OIDEndSessionRequest.h" +#import "OIDEndSessionResponse.h" +#import "OIDErrorUtilities.h" +#import "OIDExternalUserAgent.h" +#import "OIDURLQueryComponent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDEndSessionFlowSession() { + // private variables + OIDEndSessionRequest *_request; + id _externalUserAgent; + OIDEndSessionCallback _pendingEndSessionFlowCallback; +} +@end + +@implementation OIDEndSessionFlowSession + +- (instancetype)initWithRequest:(OIDEndSessionRequest *)request { + self = [super init]; + if (self) { + _request = [request copy]; + } + return self; +} + +- (void)presentEndSessionWithExternalUserAgent:(id)externalUserAgent + callback:(OIDEndSessionCallback)endSessionFlowCallback { + _externalUserAgent = externalUserAgent; + _pendingEndSessionFlowCallback = endSessionFlowCallback; + BOOL authorizationFlowStarted = + [_externalUserAgent presentExternalUserAgentRequest:_request session:self]; + if (!authorizationFlowStarted) { + NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeSafariOpenError + underlyingError:nil + description:@"Unable to open Safari."]; + [self didFinishWithResponse:nil error:safariError]; + } +} + +- (void)cancel { + [_externalUserAgent dismissExternalUserAgentAnimated:YES completion:^{ + NSError *error = [OIDErrorUtilities + errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow + underlyingError:nil + description:nil]; + [self didFinishWithResponse:nil error:error]; + }]; +} + +- (BOOL)shouldHandleURL:(NSURL *)URL { + NSURL *standardizedURL = [URL standardizedURL]; + NSURL *standardizedRedirectURL = [_request.postLogoutRedirectURL standardizedURL]; + + return OIDIsEqualIncludingNil(standardizedURL.scheme, standardizedRedirectURL.scheme) && + OIDIsEqualIncludingNil(standardizedURL.user, standardizedRedirectURL.user) && + OIDIsEqualIncludingNil(standardizedURL.password, standardizedRedirectURL.password) && + OIDIsEqualIncludingNil(standardizedURL.host, standardizedRedirectURL.host) && + OIDIsEqualIncludingNil(standardizedURL.port, standardizedRedirectURL.port) && + OIDIsEqualIncludingNil(standardizedURL.path, standardizedRedirectURL.path); +} + +- (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL { + // rejects URLs that don't match redirect (these may be completely unrelated to the authorization) + if (![self shouldHandleURL:URL]) { + return NO; + } + // checks for an invalid state + if (!_pendingEndSessionFlowCallback) { + [NSException raise:OIDOAuthExceptionInvalidAuthorizationFlow + format:@"%@", OIDOAuthExceptionInvalidAuthorizationFlow, nil]; + } + + OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] initWithURL:URL]; + + NSError *error; + OIDEndSessionResponse *response = nil; + + // checks for an OAuth error response as per RFC6749 Section 4.1.2.1 + if (query.dictionaryValue[OIDOAuthErrorFieldError]) { + error = [OIDErrorUtilities OAuthErrorWithDomain:OIDOAuthAuthorizationErrorDomain + OAuthResponse:query.dictionaryValue + underlyingError:nil]; + } + + // no error, should be a valid OAuth 2.0 response + if (!error) { + response = [[OIDEndSessionResponse alloc] initWithRequest:_request + parameters:query.dictionaryValue]; + + // verifies that the state in the response matches the state in the request, or both are nil + if (!OIDIsEqualIncludingNil(_request.state, response.state)) { + NSMutableDictionary *userInfo = [query.dictionaryValue mutableCopy]; + userInfo[NSLocalizedDescriptionKey] = + [NSString stringWithFormat:@"State mismatch, expecting %@ but got %@ in authorization " + "response %@", + _request.state, + response.state, + response]; + response = nil; + error = [NSError errorWithDomain:OIDOAuthAuthorizationErrorDomain + code:OIDErrorCodeOAuthAuthorizationClientError + userInfo:userInfo]; + } + } + + [_externalUserAgent dismissExternalUserAgentAnimated:YES completion:^{ + [self didFinishWithResponse:response error:error]; + }]; + + return YES; +} + +- (void)failExternalUserAgentFlowWithError:(NSError *)error { + [self didFinishWithResponse:nil error:error]; +} + +/*! @brief Invokes the pending callback and performs cleanup. + @param response The authorization response, if any to return to the callback. + @param error The error, if any, to return to the callback. + */ +- (void)didFinishWithResponse:(nullable OIDEndSessionResponse *)response + error:(nullable NSError *)error { + OIDEndSessionCallback callback = _pendingEndSessionFlowCallback; + _pendingEndSessionFlowCallback = nil; + _externalUserAgent = nil; + if (callback) { + callback(response, error); + } +} + +- (void)failAuthorizationFlowWithError:(NSError *)error { + [self failExternalUserAgentFlowWithError:error]; +} + +- (BOOL)resumeAuthorizationFlowWithURL:(NSURL *)URL { + return [self resumeExternalUserAgentFlowWithURL:URL]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/OIDEndSessionRequest.h b/Source/OIDEndSessionRequest.h new file mode 100644 index 000000000..3157a712e --- /dev/null +++ b/Source/OIDEndSessionRequest.h @@ -0,0 +1,104 @@ +/*! @file OIDEndSessionRequest.h + @brief AppAuth iOS SDK + @copyright + Copyright 2017 The AppAuth Authors. All Rights Reserved. + @copydetails + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import +#import "OIDExternalUserAgentRequest.h" + +@class OIDServiceConfiguration; + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDEndSessionRequest : NSObject { + OIDServiceConfiguration *_configuration; + NSURL *_postLogoutRedirectURL; + NSString *_idTokenHint; + NSString *_state; + NSDictionary *_additionalParameters; +} + +/*! @brief The service's configuration. + @remarks This configuration specifies how to connect to a particular OAuth provider. + Configurations may be created manually, or via an OpenID Connect Discovery Document. + */ +@property(nonatomic, readonly) OIDServiceConfiguration *configuration; + +/*! @brief The client's redirect URI. + @remarks post_logout_redirect_uri + @see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout + */ +@property(nonatomic, readonly, nullable) NSURL *postLogoutRedirectURL; + +/*! @brief Previously issued ID Token passed to the end session endpoint as a hint about the End-User's current authenticated + session with the Client + @remarks id_token_hint + @see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout + */ +@property(nonatomic, readonly, nullable) NSString *idTokenHint; + +/*! @brief An opaque value used by the client to maintain state between the request and callback. + @remarks state + @discussion If this value is not explicitly set, this library will automatically add state and + perform appropriate validation of the state in the authorization response. It is recommended + that the default implementation of this parameter be used wherever possible. Typically used + to prevent CSRF attacks, as recommended in RFC6819 Section 5.3.5. + @see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout + */ +@property(nonatomic, readonly, nullable) NSString *state; + +/*! @brief The client's additional authorization parameters. + @see https://tools.ietf.org/html/rfc6749#section-3.1 + */ +@property(nonatomic, readonly, nullable) NSDictionary *additionalParameters; + +/*! @internal + @brief Unavailable. Please use @c initWithConfiguration:clientId:scopes:redirectURL:additionalParameters:. + */ +- (instancetype)init NS_UNAVAILABLE; + +/*! @brief Creates an authorization request with opinionated defaults (a secure @c state). + @param configuration The service's configuration. + @param idTokenHint The previously issued ID Token + @param postLogoutRedirectURL The client's post-logout redirect URI. + callback. + @param additionalParameters The client's additional authorization parameters. +*/ +- (instancetype) + initWithConfiguration:(OIDServiceConfiguration *)configuration + idTokenHint:(nullable NSString *)idTokenHint + postLogoutRedirectURL:(NSURL *)postLogoutRedirectURL + additionalParameters:(nullable NSDictionary *)additionalParameters; + +/*! @brief Designated initializer. + @param configuration The service's configuration. + @param idTokenHint The previously issued ID Token + @param postLogoutRedirectURL The client's post-logout redirect URI. + @param state An opaque value used by the client to maintain state between the request and + callback. + @param additionalParameters The client's additional authorization parameters. + */ +- (instancetype) + initWithConfiguration:(OIDServiceConfiguration *)configuration + idTokenHint:(nullable NSString *)idTokenHint + postLogoutRedirectURL:(NSURL *)postLogoutRedirectURL + state:(NSString *)state + additionalParameters:(nullable NSDictionary *)additionalParameters + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/OIDEndSessionRequest.m b/Source/OIDEndSessionRequest.m new file mode 100644 index 000000000..1e7b23387 --- /dev/null +++ b/Source/OIDEndSessionRequest.m @@ -0,0 +1,191 @@ +/*! @file OIDEndSessionRequest.m + @brief AppAuth iOS SDK + @copyright + Copyright 2017 The AppAuth Authors. All Rights Reserved. + @copydetails + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "OIDEndSessionRequest.h" + +#import "OIDDefines.h" +#import "OIDTokenUtilities.h" +#import "OIDServiceConfiguration.h" +#import "OIDServiceDiscovery.h" +#import "OIDURLQueryComponent.h" + +/*! @brief The key for the @c configuration property for @c NSSecureCoding + */ +static NSString *const kConfigurationKey = @"configuration"; + +/*! @brief Key used to encode the @c state property for @c NSSecureCoding, and on the URL request. + */ +static NSString *const kStateKey = @"state"; + +/*! @brief Key used to encode the @c postLogoutRedirectURL property for @c NSSecureCoding, and on the URL request. + */ +static NSString *const kPostLogoutRedirectURLKey = @"post_logout_redirect_uri"; + +/*! @brief Key used to encode the @c idTokenHint property for @c NSSecureCoding, and on the URL request. + */ +static NSString *const kIdTokenHintKey = @"id_token_hint"; + + +/*! @brief Key used to encode the @c additionalParameters property for @c NSSecureCoding + */ +static NSString *const kAdditionalParametersKey = @"additionalParameters"; + +/*! @brief Number of random bytes generated for the @state. + */ +static NSUInteger const kStateSizeBytes = 32; + +/*! @brief Assertion text for missing end_session_endpoint. + */ +static NSString *const OIDMissingEndSessionEndpointMessage = +@"The service configuration is missing an end_session_endpoint."; + +@implementation OIDEndSessionRequest + +@synthesize configuration = _configuration; +@synthesize postLogoutRedirectURL = _postLogoutRedirectURL; +@synthesize idTokenHint = _idTokenHint; +@synthesize state = _state; +@synthesize additionalParameters = _additionalParameters; + +- (instancetype)init + OID_UNAVAILABLE_USE_INITIALIZER( + @selector(initWithConfiguration: + idTokenHint: + postLogoutRedirectURL: + additionalParameters:) + ); + +- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration + idTokenHint:(nullable NSString *)idTokenHint + postLogoutRedirectURL:(NSURL *)postLogoutRedirectURL + state:(NSString *)state + additionalParameters:(nullable NSDictionary *)additionalParameters +{ + self = [super init]; + if (self) { + _configuration = [configuration copy]; + _idTokenHint = [idTokenHint copy]; + _postLogoutRedirectURL = [postLogoutRedirectURL copy]; + _state = [state copy]; + _additionalParameters = + [[NSDictionary alloc] initWithDictionary:additionalParameters copyItems:YES]; + } + return self; +} + +- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration + idTokenHint:(nullable NSString *)idTokenHint + postLogoutRedirectURL:(NSURL *)postLogoutRedirectURL + additionalParameters:(nullable NSDictionary *)additionalParameters +{ + return [self initWithConfiguration:configuration + idTokenHint:idTokenHint + postLogoutRedirectURL:postLogoutRedirectURL + state:[[self class] generateState] + additionalParameters:additionalParameters]; +} +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(nullable NSZone *)zone { + // The documentation for NSCopying specifically advises us to return a reference to the original + // instance in the case where instances are immutable (as ours is): + // "Implement NSCopying by retaining the original instead of creating a new copy when the class + // and its contents are immutable." + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + OIDServiceConfiguration *configuration = [aDecoder decodeObjectOfClass:[OIDServiceConfiguration class] forKey:kConfigurationKey]; + + NSString *idTokenHint = [aDecoder decodeObjectOfClass:[NSString class] forKey:kIdTokenHintKey]; + NSURL *postLogoutRedirectURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kPostLogoutRedirectURLKey]; + NSString *state = [aDecoder decodeObjectOfClass:[NSString class] forKey:kStateKey]; + NSSet *additionalParameterCodingClasses = [NSSet setWithArray:@[ + [NSDictionary class], + [NSString class] + ]]; + NSDictionary *additionalParameters = [aDecoder decodeObjectOfClasses:additionalParameterCodingClasses + forKey:kAdditionalParametersKey]; + + self = [self initWithConfiguration:configuration + idTokenHint:idTokenHint + postLogoutRedirectURL:postLogoutRedirectURL + state:state + additionalParameters:additionalParameters]; + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_configuration forKey:kConfigurationKey]; + [aCoder encodeObject:_idTokenHint forKey:kIdTokenHintKey]; + [aCoder encodeObject:_postLogoutRedirectURL forKey:kPostLogoutRedirectURLKey]; + [aCoder encodeObject:_state forKey:kStateKey]; + [aCoder encodeObject:_additionalParameters forKey:kAdditionalParametersKey]; +} + +#pragma mark - NSObject overrides + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, request: %@>", + NSStringFromClass([self class]), + self, + self.externalUserAgentRequestURL]; +} + ++ (nullable NSString *)generateState { + return [OIDTokenUtilities randomURLSafeStringWithSize:kStateSizeBytes]; +} + +#pragma mark - OIDExternalUserAgentRequest + +- (NSURL *)externalUserAgentRequestURL { + OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init]; + + // Add any additional parameters the client has specified. + [query addParameters:_additionalParameters]; + + // Add optional parameters, as applicable. + if (_idTokenHint) { + [query addParameter:kIdTokenHintKey value:_idTokenHint]; + } + + if (_postLogoutRedirectURL) { + [query addParameter:kPostLogoutRedirectURLKey value:_postLogoutRedirectURL.absoluteString]; + } + + if (_state) { + [query addParameter:kStateKey value:_state]; + } + + NSAssert(_configuration.discoveryDocument.endSessionEndpoint, OIDMissingEndSessionEndpointMessage); + + // Construct the URL: + return [query URLByReplacingQueryInURL:_configuration.discoveryDocument.endSessionEndpoint]; +} + +- (NSString *)redirectScheme { + return self.postLogoutRedirectURL.scheme; +} + +@end diff --git a/Source/OIDEndSessionResponse.h b/Source/OIDEndSessionResponse.h new file mode 100644 index 000000000..6fc0131c3 --- /dev/null +++ b/Source/OIDEndSessionResponse.h @@ -0,0 +1,62 @@ +/*! @file OIDEndSessionResponse.h + @brief AppAuth iOS SDK + @copyright + Copyright 2017 The AppAuth Authors. All Rights Reserved. + @copydetails + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +@class OIDEndSessionRequest; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Represents the response to an End Session request. + @see http://openid.net/specs/openid-connect-frontchannel-1_0.html + */ + +@interface OIDEndSessionResponse : NSObject { + OIDEndSessionRequest *_request; + NSString *_state; +} + +/*! @brief The request which was serviced. + */ +@property(nonatomic, readonly) OIDEndSessionRequest *request; + +/*! @brief REQUIRED if the "state" parameter was present in the client authorization request. The + exact value received from the client. + @remarks state + */ +@property(nonatomic, readonly, nullable) NSString *state; + +/*! @internal + @brief Unavailable. Please use initWithParameters:. + */ +- (instancetype)init NS_UNAVAILABLE; + +/*! @brief Designated initializer. + @param request The serviced request. + @param parameters The decoded parameters returned from the End Session Endpoint. + @remarks Known parameters are extracted from the @c parameters parameter and the normative + properties are populated. Non-normative parameters are placed in the + @c #additionalParameters dictionary. + */ +- (instancetype)initWithRequest:(OIDEndSessionRequest *)request + parameters:(NSDictionary *> *)parameters +NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/OIDEndSessionResponse.m b/Source/OIDEndSessionResponse.m new file mode 100644 index 000000000..ce111c173 --- /dev/null +++ b/Source/OIDEndSessionResponse.m @@ -0,0 +1,84 @@ +/*! @file OIDEndSessionResponse.m + @brief AppAuth iOS SDK + @copyright + Copyright 2017 The AppAuth Authors. All Rights Reserved. + @copydetails + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "OIDEndSessionResponse.h" + +#import "OIDEndSessionRequest.h" +#import "OIDDefines.h" + +/*! @brief The key for the @c state property in the incoming parameters and for @c NSSecureCoding. + */ +static NSString *const kStateKey = @"state"; + +/*! @brief Key used to encode the @c request property for @c NSSecureCoding + */ +static NSString *const kRequestKey = @"request"; + +@implementation OIDEndSessionResponse + +@synthesize request = _request; +@synthesize state = _state; + +#pragma mark - Initializers + +- (instancetype)init +OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:parameters:)); + +- (instancetype)initWithRequest:(OIDEndSessionRequest *)request parameters:(NSDictionary *> *)parameters +{ + self = [super init]; + if (self) { + _request = [request copy]; + _state = [parameters[kStateKey] copy]; + } + return self; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(nullable NSZone *)zone { + // The documentation for NSCopying specifically advises us to return a reference to the original + // instance in the case where instances are immutable (as ours is): + // "Implement NSCopying by retaining the original instead of creating a new copy when the class + // and its contents are immutable." + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + OIDEndSessionRequest *request = [aDecoder decodeObjectOfClass:[OIDEndSessionRequest class] forKey:kRequestKey]; + NSString *state = [aDecoder decodeObjectOfClass:[NSString class] forKey:kStateKey]; + + self = [self initWithRequest:request parameters:@{}]; + if (self) { + _state = state; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_request forKey:kRequestKey]; + [aCoder encodeObject:_state forKey:kStateKey]; +} + +@end diff --git a/Source/OIDServiceDiscovery.h b/Source/OIDServiceDiscovery.h index e3c8eda90..73721bdb6 100644 --- a/Source/OIDServiceDiscovery.h +++ b/Source/OIDServiceDiscovery.h @@ -81,6 +81,12 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, readonly, nullable) NSURL *registrationEndpoint; +/* @brief OPTIONAL. URL of the OP's RP-Initiated Logout endpoint. + @remarks end_session_endpoint + @seealso http://openid.net/specs/openid-connect-session-1_0.html#OPMetadata + */ +@property(nonatomic, readonly, nullable) NSURL *endSessionEndpoint; + /*! @brief RECOMMENDED. JSON array containing a list of the OAuth 2.0 [RFC6749] scope values that this server supports. The server MUST support the openid scope value. Servers MAY choose not to advertise some supported scope values even when this parameter is used, although those diff --git a/Source/OIDServiceDiscovery.m b/Source/OIDServiceDiscovery.m index 565010577..ce233967f 100644 --- a/Source/OIDServiceDiscovery.m +++ b/Source/OIDServiceDiscovery.m @@ -30,6 +30,7 @@ static NSString *const kUserinfoEndpointKey = @"userinfo_endpoint"; static NSString *const kJWKSURLKey = @"jwks_uri"; static NSString *const kRegistrationEndpointKey = @"registration_endpoint"; +static NSString *const kEndSessionEndpointKey = @"end_session_endpoint"; static NSString *const kScopesSupportedKey = @"scopes_supported"; static NSString *const kResponseTypesSupportedKey = @"response_types_supported"; static NSString *const kResponseModesSupportedKey = @"response_modes_supported"; @@ -223,6 +224,10 @@ - (nullable NSURL *)registrationEndpoint { return [NSURL URLWithString:_discoveryDictionary[kRegistrationEndpointKey]]; } +- (nullable NSURL *)endSessionEndpoint { + return [NSURL URLWithString:_discoveryDictionary[kEndSessionEndpointKey]]; +} + - (nullable NSArray *)scopesSupported { return _discoveryDictionary[kScopesSupportedKey]; } diff --git a/Source/iOS/OIDAuthState+IOS.h b/Source/iOS/OIDAuthState+IOS.h index 3ebc9b028..265d3db07 100644 --- a/Source/iOS/OIDAuthState+IOS.h +++ b/Source/iOS/OIDAuthState+IOS.h @@ -41,6 +41,22 @@ NS_ASSUME_NONNULL_BEGIN presentingViewController:(UIViewController *)presentingViewController callback:(OIDAuthStateAuthorizationCallback)callback; +/*! @brief Presents a RP-initiated logout using the external user agent. + @param endSessionRequest The end session request to present. + @param presentingViewController The view controller from which to present external user agent. + @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 Afer a successful ending of the session, it invalidates the @c OIDAuthState. + Remove references to this instance. Hence, a new authorization flow is required + to retrieve a new instance of @c OIDAuthState. + @see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout +*/ +- (id)presentEndSessionRequest:(OIDEndSessionRequest *)endSessionRequest + presentingViewController:(UIViewController *)presentingViewController + callback:(OIDEndSessionCallback)callback; + @end NS_ASSUME_NONNULL_END diff --git a/Source/iOS/OIDAuthState+IOS.m b/Source/iOS/OIDAuthState+IOS.m index 37f614021..4cae18fe5 100644 --- a/Source/iOS/OIDAuthState+IOS.m +++ b/Source/iOS/OIDAuthState+IOS.m @@ -34,4 +34,14 @@ @implementation OIDAuthState (IOS) callback:callback]; } +- (id)presentEndSessionRequest:(OIDEndSessionRequest *)endSessionRequest + presentingViewController:(UIViewController *)presentingViewController + callback:(OIDEndSessionCallback)callback { + OIDExternalUserAgentIOS *externalUserAgent = [[OIDExternalUserAgentIOS alloc] + initWithPresentingViewController:presentingViewController]; + return [self presentEndSessionRequest:endSessionRequest + externalUserAgent:externalUserAgent + callback:callback]; +} + @end diff --git a/Source/iOS/OIDExternalUserAgentIOS.h b/Source/iOS/OIDExternalUserAgentIOS.h index 9f5ebdfff..c6c99ac0e 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.h +++ b/Source/iOS/OIDExternalUserAgentIOS.h @@ -58,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN @param presentingViewController The view controller from which to present the \SFSafariViewController. */ -- (nullable instancetype)initWithPresentingViewController: +- (instancetype)initWithPresentingViewController: (UIViewController *)presentingViewController NS_DESIGNATED_INITIALIZER; diff --git a/Source/iOS/OIDExternalUserAgentIOS.m b/Source/iOS/OIDExternalUserAgentIOS.m index d87aecf07..1c4b259e5 100644 --- a/Source/iOS/OIDExternalUserAgentIOS.m +++ b/Source/iOS/OIDExternalUserAgentIOS.m @@ -67,7 +67,7 @@ + (void)setSafariViewControllerFactory:(id)facto gSafariViewControllerFactory = factory; } -- (nullable instancetype)initWithPresentingViewController: +- (instancetype)initWithPresentingViewController: (UIViewController *)presentingViewController { self = [super init]; if (self) { diff --git a/Source/macOS/OIDAuthState+Mac.h b/Source/macOS/OIDAuthState+Mac.h index 7de9f7324..c3276e7cb 100644 --- a/Source/macOS/OIDAuthState+Mac.h +++ b/Source/macOS/OIDAuthState+Mac.h @@ -35,6 +35,21 @@ NS_ASSUME_NONNULL_BEGIN + (id) authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest callback:(OIDAuthStateAuthorizationCallback)callback; + +/*! @brief Presents a RP-initiated logout using the external user agent. + @param endSessionRequest The end session 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 Afer a successful ending of the session, it invalidates the @c OIDAuthState. + Remove references to this instance. Hence, a new authorization flow is required + to retrieve a new instance of @c OIDAuthState. + @see http://openid.net/specs/openid-connect-session-1_0.html#RPLogout + */ +- (id)presentEndSessionRequest:(OIDEndSessionRequest *)endSessionRequest + callback:(OIDEndSessionCallback)callback; + @end NS_ASSUME_NONNULL_END diff --git a/Source/macOS/OIDAuthState+Mac.m b/Source/macOS/OIDAuthState+Mac.m index 7b99e734f..08980cdbc 100644 --- a/Source/macOS/OIDAuthState+Mac.m +++ b/Source/macOS/OIDAuthState+Mac.m @@ -31,4 +31,12 @@ @implementation OIDAuthState (Mac) callback:callback]; } +- (id)presentEndSessionRequest:(OIDEndSessionRequest *)endSessionRequest + callback:(OIDEndSessionCallback)callback { + OIDExternalUserAgentMac *externalUserAgent = [[OIDExternalUserAgentMac alloc] init]; + return [self presentEndSessionRequest:endSessionRequest + externaluserAgent:externalUserAgent + callback:callback]; +} + @end diff --git a/UnitTests/OIDEndSessionRequestTests.h b/UnitTests/OIDEndSessionRequestTests.h new file mode 100644 index 000000000..48f902757 --- /dev/null +++ b/UnitTests/OIDEndSessionRequestTests.h @@ -0,0 +1,31 @@ +/*! @file OIDServiceDiscoveryTests.m + @brief AppAuth iOS SDK + @copyright + Copyright 2017 The AppAuth Authors. All Rights Reserved. + @copydetails + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +@class OIDEndSessionRequest; + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDEndSessionRequestTests : XCTestCase + ++ (OIDEndSessionRequest *)testInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/UnitTests/OIDEndSessionRequestTests.m b/UnitTests/OIDEndSessionRequestTests.m new file mode 100644 index 000000000..785fec02a --- /dev/null +++ b/UnitTests/OIDEndSessionRequestTests.m @@ -0,0 +1,124 @@ +/*! @file OIDServiceDiscoveryTests.m + @brief AppAuth iOS SDK + @copyright + Copyright 2017 The AppAuth Authors. All Rights Reserved. + @copydetails + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "OIDEndSessionRequestTests.h" + +#import "OIDServiceDiscoveryTests.h" +#import "Source/OIDEndSessionRequest.h" +#import "Source/OIDServiceConfiguration.h" +#import "Source/OIDServiceDiscovery.h" + +/*! @brief Test value for the @c redirectURL property. + */ +static NSString *const kTestRedirectURL = @"http://www.google.com/"; + +/*! @brief Test key for the @c additionalParameters property. + */ +static NSString *const kTestAdditionalParameterKey = @"A"; + +/*! @brief Test value for the @c additionalParameters property. + */ +static NSString *const kTestAdditionalParameterValue = @"1"; + +/*! @brief Test value for the @c state property. + */ +static NSString *const kTestState = @"State"; + +/*! @brief Test value for the @c idTokenHint property. + */ +static NSString *const kTestIdTokenHint = @"id-token-hint"; + +@implementation OIDEndSessionRequestTests + ++ (OIDEndSessionRequest *)testInstance { + NSDictionary *additionalParameters = + @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + + OIDServiceDiscovery *discoveryDocument = [[OIDServiceDiscovery alloc] initWithDictionary:[OIDServiceDiscoveryTests completeServiceDiscoveryDictionary] error:nil]; + OIDServiceConfiguration *configuration = [[OIDServiceConfiguration alloc] initWithDiscoveryDocument:discoveryDocument]; + + return [[OIDEndSessionRequest alloc] initWithConfiguration:configuration + idTokenHint:kTestIdTokenHint + postLogoutRedirectURL:[NSURL URLWithString:kTestRedirectURL] + state:kTestState + additionalParameters:additionalParameters]; +} + +/*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying + process and checking to make sure the source and destination instances are equivalent. + */ +- (void)testCopying { + OIDEndSessionRequest *request = [[self class] testInstance]; + + XCTAssertEqualObjects(request.idTokenHint, kTestIdTokenHint); + XCTAssertEqualObjects(request.postLogoutRedirectURL, [NSURL URLWithString:kTestRedirectURL]); + XCTAssertEqualObjects(request.state, kTestState); + XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], + kTestAdditionalParameterValue); + + OIDEndSessionRequest *requestCopy = [request copy]; + + XCTAssertNotNil(requestCopy.configuration); + XCTAssertEqualObjects(requestCopy.configuration, request.configuration); + XCTAssertEqualObjects(requestCopy.postLogoutRedirectURL, request.postLogoutRedirectURL); + XCTAssertEqualObjects(requestCopy.state, request.state); + XCTAssertEqualObjects(requestCopy.idTokenHint, request.idTokenHint); +} + +/*! @brief Tests the @c NSSecureCoding by round-tripping an instance through the coding process and + checking to make sure the source and destination instances are equivalent. + */ +- (void)testSecureCoding { + OIDEndSessionRequest *request = [[self class] testInstance]; + + XCTAssertEqualObjects(request.idTokenHint, kTestIdTokenHint); + XCTAssertEqualObjects(request.postLogoutRedirectURL, [NSURL URLWithString:kTestRedirectURL]); + XCTAssertEqualObjects(request.state, kTestState); + XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], + kTestAdditionalParameterValue); + + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; + OIDEndSessionRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + + XCTAssertNotNil(requestCopy.configuration); + XCTAssertEqualObjects(requestCopy.configuration.authorizationEndpoint, + request.configuration.authorizationEndpoint); + XCTAssertEqualObjects(requestCopy.postLogoutRedirectURL, request.postLogoutRedirectURL); + XCTAssertEqualObjects(requestCopy.state, request.state); + XCTAssertEqualObjects(requestCopy.idTokenHint, request.idTokenHint); +} + +- (void)testLogoutRequestURL { + OIDEndSessionRequest *request = [[self class] testInstance]; + NSURL *endSessionRequestURL = request.externalUserAgentRequestURL; + + NSURLComponents *components = [NSURLComponents componentsWithString:endSessionRequestURL.absoluteString]; + + XCTAssertTrue([endSessionRequestURL.absoluteString hasPrefix:@"https://www.example.com/logout"]); + + NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; + for (NSURLQueryItem *queryItem in components.queryItems) { + query[queryItem.name] = queryItem.value; + } + + XCTAssertEqualObjects(query[@"state"], kTestState); + XCTAssertEqualObjects(query[@"id_token_hint"], kTestIdTokenHint); + XCTAssertEqualObjects(query[@"post_logout_redirect_uri"], kTestRedirectURL); +} + +@end diff --git a/UnitTests/OIDServiceDiscoveryTests.m b/UnitTests/OIDServiceDiscoveryTests.m index 4352c0b9c..b80d7e638 100644 --- a/UnitTests/OIDServiceDiscoveryTests.m +++ b/UnitTests/OIDServiceDiscoveryTests.m @@ -34,6 +34,7 @@ static NSString *const kUserinfoEndpointKey = @"userinfo_endpoint"; static NSString *const kJWKSURLKey = @"jwks_uri"; static NSString *const kRegistrationEndpointKey = @"registration_endpoint"; +static NSString *const kEndSessionEndpointKey = @"end_session_endpoint"; static NSString *const kScopesSupportedKey = @"scopes_supported"; static NSString *const kResponseTypesSupportedKey = @"response_types_supported"; static NSString *const kResponseModesSupportedKey = @"response_modes_supported"; @@ -97,6 +98,7 @@ + (NSDictionary *)completeServiceDiscoveryDictionary { kUserinfoEndpointKey : @"User Info Endpoint", kJWKSURLKey : @"http://www.example.com/jwks", kRegistrationEndpointKey : @"Registration Endpoint", + kEndSessionEndpointKey : @"https://www.example.com/logout", kScopesSupportedKey : @"Scopes Supported", kResponseTypesSupportedKey : @"Response Types Supported", kResponseModesSupportedKey : @"Response Modes Supported", @@ -466,6 +468,7 @@ - (void)testField_##_field_ { TestURLFieldBackedBy(userinfoEndpoint, kUserinfoEndpointKey, kTestURL); TestURLFieldBackedBy(jwksURL, kJWKSURLKey, kTestURL); TestURLFieldBackedBy(registrationEndpoint, kRegistrationEndpointKey, kTestURL); +TestURLFieldBackedBy(endSessionEndpoint, kEndSessionEndpointKey, kTestURL); TestFieldBackedBy(scopesSupported, kScopesSupportedKey, @"Scopes Supported"); TestFieldBackedBy(responseTypesSupported, kResponseTypesSupportedKey, @"Response Types Supported"); TestFieldBackedBy(responseModesSupported, kResponseModesSupportedKey, @"Response Modes Supported");