├── .gitignore ├── LICENSE ├── PnP-IdentityModel.sln ├── README.md └── SharePointPnP.IdentityModel.Extensions ├── Properties └── AssemblyInfo.cs ├── S2S ├── DateTimeUtil.cs ├── DictionaryExtension.cs ├── EpochTime.cs ├── Protocols │ └── OAuth2 │ │ ├── OAuth2AccessTokenRequest.cs │ │ ├── OAuth2AccessTokenResponse.cs │ │ ├── OAuth2AuthorizationRequest.cs │ │ ├── OAuth2AuthorizationResponse.cs │ │ ├── OAuth2Constants.cs │ │ ├── OAuth2ErrorCode.cs │ │ ├── OAuth2ErrorResponse.cs │ │ ├── OAuth2Message.cs │ │ ├── OAuth2MessageFactory.cs │ │ ├── OAuth2S2SClient.cs │ │ └── OAuth2WebRequest.cs ├── Tokens │ ├── AudienceValidator.cs │ ├── Base64UrlEncoder.cs │ ├── JsonWebSecurityToken.cs │ ├── JsonWebSecurityTokenHandler.cs │ ├── JsonWebSecurityTokenRequirement.cs │ ├── JsonWebTokenClaim.cs │ ├── JsonWebTokenConstants.cs │ ├── NativeMethods.cs │ ├── RSACryptoServiceProviderProxy.cs │ ├── SignatureProvider.cs │ ├── SimpleSymmetricKeySecurityToken.cs │ ├── SymmetricIssuerKeyIdentifierClause.cs │ ├── SymmetricKeyIssuerNameRegistry.cs │ ├── SymmetricSignatureProvider.cs │ ├── Utility.cs │ └── X509AsymmetricSignatureProvider.cs └── Web │ └── OAuth2ProtectedResourceUtility.cs └── SharePointPnP.IdentityModel.Extensions.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Microsoft SharePoint 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PnP-IdentityModel.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.4 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharePointPnP.IdentityModel.Extensions", "SharePointPnP.IdentityModel.Extensions\SharePointPnP.IdentityModel.Extensions.csproj", "{EB239573-1869-4DF4-8973-E01B7796DA02}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EB239573-1869-4DF4-8973-E01B7796DA02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {EB239573-1869-4DF4-8973-E01B7796DA02}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {EB239573-1869-4DF4-8973-E01B7796DA02}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {EB239573-1869-4DF4-8973-E01B7796DA02}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | products: 4 | - office-sp 5 | languages: 6 | - csharp 7 | extensions: 8 | contentType: samples 9 | createdDate: 1/9/2017 11:01:15 AM 10 | --- 11 | # PnP-IdentityModel 12 | 13 | This repository holds code related to authentication in SharePoint. 14 | 15 | ##SharePointPnP.IdentityModel.Extensions 16 | Originally, Microsoft.IdentityModel.Extensions.dll is where the code for SharePoint provider-hosted apps OAuth and S2S token processing is located. Microsoft.IdentityModel.Extensions is not maintained by anyone, but SharePoint add-ins, SharePointPnP.Core and a few other things depend on it. SharePointPnP.IdentityModel.Extensions is a port of that library created by the PnP team. We reference it in OfficeDevPnP.Core (and all other supporting solutions) instead of depending on Microsoft.IdentityModel.Extensions. 17 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("SharePointPnP.IdentityModel")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("eb239573-1869-4df4-8973-e01b7796da02")] 20 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/DateTimeUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S 7 | { 8 | [System.Diagnostics.DebuggerNonUserCode] 9 | public static class DateTimeUtil 10 | { 11 | public static System.DateTime Add(System.DateTime time, System.TimeSpan timespan) 12 | { 13 | if (timespan == System.TimeSpan.Zero) 14 | { 15 | return time; 16 | } 17 | if (timespan > System.TimeSpan.Zero && System.DateTime.MaxValue - time <= timespan) 18 | { 19 | return DateTimeUtil.GetMaxValue(time.Kind); 20 | } 21 | if (timespan < System.TimeSpan.Zero && System.DateTime.MinValue - time >= timespan) 22 | { 23 | return DateTimeUtil.GetMinValue(time.Kind); 24 | } 25 | return time + timespan; 26 | } 27 | 28 | public static System.DateTime AddNonNegative(System.DateTime time, System.TimeSpan timeSpan) 29 | { 30 | if (timeSpan < System.TimeSpan.Zero) 31 | { 32 | throw new System.ArgumentException("TimeSpan must be greater than or equal to TimeSpan.Zero.", "timeSpan"); 33 | } 34 | return DateTimeUtil.Add(time, timeSpan); 35 | } 36 | 37 | public static System.DateTime GetMaxValue(System.DateTimeKind kind) 38 | { 39 | return new System.DateTime(System.DateTime.MaxValue.Ticks, kind); 40 | } 41 | 42 | public static System.DateTime GetMinValue(System.DateTimeKind kind) 43 | { 44 | return new System.DateTime(System.DateTime.MinValue.Ticks, kind); 45 | } 46 | 47 | public static System.DateTime? ToUniversalTime(System.DateTime? value) 48 | { 49 | if (!value.HasValue || value.Value.Kind == System.DateTimeKind.Utc) 50 | { 51 | return value; 52 | } 53 | return new System.DateTime?(DateTimeUtil.ToUniversalTime(value.Value)); 54 | } 55 | 56 | public static System.DateTime ToUniversalTime(System.DateTime value) 57 | { 58 | if (value.Kind == System.DateTimeKind.Utc) 59 | { 60 | return value; 61 | } 62 | return value.ToUniversalTime(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/DictionaryExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Web; 6 | using System.Web.Script.Serialization; 7 | 8 | namespace SharePointPnP.IdentityModel.Extensions.S2S 9 | { 10 | public static class DictionaryExtension 11 | { 12 | public delegate string Encoder(string input); 13 | 14 | public const char DefaultSeparator = '&'; 15 | 16 | public const char DefaultKeyValueSeparator = '='; 17 | 18 | public static DictionaryExtension.Encoder DefaultDecoder = new DictionaryExtension.Encoder(HttpUtility.UrlDecode); 19 | 20 | public static DictionaryExtension.Encoder DefaultEncoder = new DictionaryExtension.Encoder(HttpUtility.UrlEncode); 21 | 22 | public static DictionaryExtension.Encoder NullEncoder = new DictionaryExtension.Encoder(DictionaryExtension.NullEncode); 23 | 24 | private static string NullEncode(string value) 25 | { 26 | return value; 27 | } 28 | 29 | public static void Decode(this System.Collections.Generic.IDictionary self, string encodedDictionary) 30 | { 31 | self.Decode(encodedDictionary, '&', '=', DictionaryExtension.DefaultDecoder, DictionaryExtension.DefaultDecoder, false); 32 | } 33 | 34 | public static void Decode(this System.Collections.Generic.IDictionary self, string encodedDictionary, DictionaryExtension.Encoder decoder) 35 | { 36 | self.Decode(encodedDictionary, '&', '=', decoder, decoder, false); 37 | } 38 | 39 | public static void Decode(this System.Collections.Generic.IDictionary self, string encodedDictionary, char separator, char keyValueSplitter, bool endsWithSeparator) 40 | { 41 | self.Decode(encodedDictionary, separator, keyValueSplitter, DictionaryExtension.DefaultDecoder, DictionaryExtension.DefaultDecoder, endsWithSeparator); 42 | } 43 | 44 | public static void Decode(this System.Collections.Generic.IDictionary self, string encodedDictionary, char separator, char keyValueSplitter, DictionaryExtension.Encoder keyDecoder, DictionaryExtension.Encoder valueDecoder, bool endsWithSeparator) 45 | { 46 | if (encodedDictionary == null) 47 | { 48 | throw new System.ArgumentNullException("encodedDictionary"); 49 | } 50 | if (keyDecoder == null) 51 | { 52 | throw new System.ArgumentNullException("keyDecoder"); 53 | } 54 | if (valueDecoder == null) 55 | { 56 | throw new System.ArgumentNullException("valueDecoder"); 57 | } 58 | if (endsWithSeparator && encodedDictionary.LastIndexOf(separator) == encodedDictionary.Length - 1) 59 | { 60 | encodedDictionary = encodedDictionary.Substring(0, encodedDictionary.Length - 1); 61 | } 62 | string[] array = encodedDictionary.Split(new char[] 63 | { 64 | separator 65 | }); 66 | for (int i = 0; i < array.Length; i++) 67 | { 68 | string text = array[i]; 69 | string[] array2 = text.Split(new char[] 70 | { 71 | keyValueSplitter 72 | }); 73 | if ((array2.Length == 1 || array2.Length > 2) && !string.IsNullOrEmpty(array2[0])) 74 | { 75 | throw new System.ArgumentException("The request is not properly formatted.", "encodedDictionary"); 76 | } 77 | if (array2.Length != 2) 78 | { 79 | throw new System.ArgumentException("The request is not properly formatted.", "encodedDictionary"); 80 | } 81 | string text2 = keyDecoder(array2[0].Trim()); 82 | string value = valueDecoder(array2[1].Trim().Trim(new char[] 83 | { 84 | '"' 85 | })); 86 | try 87 | { 88 | self.Add(text2, value); 89 | } 90 | catch (System.ArgumentException) 91 | { 92 | string message = string.Format(System.Globalization.CultureInfo.InvariantCulture, "The request is not properly formatted. The parameter '{0}' is duplicated.", new object[] 93 | { 94 | text2 95 | }); 96 | throw new System.ArgumentException(message, "encodedDictionary"); 97 | } 98 | } 99 | } 100 | 101 | public static string Encode(this System.Collections.Generic.IDictionary self) 102 | { 103 | return self.Encode('&', '=', DictionaryExtension.DefaultEncoder, DictionaryExtension.DefaultEncoder, false); 104 | } 105 | 106 | public static string Encode(this System.Collections.Generic.IDictionary self, DictionaryExtension.Encoder encoder) 107 | { 108 | return self.Encode('&', '=', encoder, encoder, false); 109 | } 110 | 111 | public static string Encode(this System.Collections.Generic.IDictionary self, char separator, char keyValueSplitter, bool endsWithSeparator) 112 | { 113 | return self.Encode(separator, keyValueSplitter, DictionaryExtension.DefaultEncoder, DictionaryExtension.DefaultEncoder, endsWithSeparator); 114 | } 115 | 116 | public static string Encode(this System.Collections.Generic.IDictionary self, char separator, char keyValueSplitter, DictionaryExtension.Encoder keyEncoder, DictionaryExtension.Encoder valueEncoder, bool endsWithSeparator) 117 | { 118 | if (keyEncoder == null) 119 | { 120 | throw new System.ArgumentNullException("keyEncoder"); 121 | } 122 | if (valueEncoder == null) 123 | { 124 | throw new System.ArgumentNullException("valueEncoder"); 125 | } 126 | System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); 127 | foreach (System.Collections.Generic.KeyValuePair current in self) 128 | { 129 | if (stringBuilder.Length != 0) 130 | { 131 | stringBuilder.Append(separator); 132 | } 133 | stringBuilder.AppendFormat("{0}{1}{2}", keyEncoder(current.Key), keyValueSplitter, valueEncoder(current.Value)); 134 | } 135 | if (endsWithSeparator) 136 | { 137 | stringBuilder.Append(separator); 138 | } 139 | return stringBuilder.ToString(); 140 | } 141 | 142 | public static string EncodeToJson(this System.Collections.Generic.IDictionary self) 143 | { 144 | JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); 145 | return javaScriptSerializer.Serialize(self); 146 | } 147 | 148 | public static void DecodeFromJson(this System.Collections.Generic.IDictionary self, string encodedDictionary) 149 | { 150 | JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); 151 | System.Collections.Generic.Dictionary dictionary = javaScriptSerializer.DeserializeObject(encodedDictionary) as System.Collections.Generic.Dictionary; 152 | if (dictionary == null) 153 | { 154 | throw new System.ArgumentException("Invalid request format.", "encodedDictionary"); 155 | } 156 | foreach (System.Collections.Generic.KeyValuePair current in dictionary) 157 | { 158 | if (current.Value == null) 159 | { 160 | self.Add(current.Key, null); 161 | } 162 | else if (current.Value is object[]) 163 | { 164 | self.Add(current.Key, javaScriptSerializer.Serialize(current.Value)); 165 | } 166 | else 167 | { 168 | self.Add(current.Key, current.Value.ToString()); 169 | } 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/EpochTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S 7 | { 8 | public class EpochTime 9 | { 10 | public static readonly System.DateTime UnixEpoch = 11 | new System.DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); 12 | 13 | private long _secondsSinceUnixEpoch; 14 | 15 | public long SecondsSinceUnixEpoch 16 | { 17 | get 18 | { 19 | return this._secondsSinceUnixEpoch; 20 | } 21 | } 22 | 23 | public System.DateTime DateTime 24 | { 25 | get 26 | { 27 | System.TimeSpan timeSpan = System.TimeSpan.FromSeconds((double)this._secondsSinceUnixEpoch); 28 | return DateTimeUtil.AddNonNegative(EpochTime.UnixEpoch, timeSpan); 29 | } 30 | } 31 | 32 | public EpochTime(string secondsSinceUnixEpochString) 33 | { 34 | long secondsSinceUnixEpoch; 35 | if (!long.TryParse(secondsSinceUnixEpochString, out secondsSinceUnixEpoch)) 36 | { 37 | throw new System.ArgumentException("Invalid date time string format.", "secondsSinceUnixEpochString"); 38 | } 39 | this._secondsSinceUnixEpoch = secondsSinceUnixEpoch; 40 | } 41 | 42 | public EpochTime(long secondsSinceUnixEpoch) 43 | { 44 | if (secondsSinceUnixEpoch < 0L) 45 | { 46 | throw new System.ArgumentException("secondsSinceUnixEpoch must be greater than or equal to zero.", "secondsSinceUnixEpoch"); 47 | } 48 | this._secondsSinceUnixEpoch = secondsSinceUnixEpoch; 49 | } 50 | 51 | public EpochTime(System.DateTime dateTime) 52 | { 53 | if (dateTime < EpochTime.UnixEpoch) 54 | { 55 | string message = string.Format(System.Globalization.CultureInfo.InvariantCulture, "DateTime must be greater than or equal to {0}", new object[] 56 | { 57 | EpochTime.UnixEpoch.ToString() 58 | }); 59 | throw new System.ArgumentOutOfRangeException("dateTime", message); 60 | } 61 | this._secondsSinceUnixEpoch = (long)(dateTime - EpochTime.UnixEpoch).TotalSeconds; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Protocols/OAuth2/OAuth2AccessTokenRequest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.IdentityModel.SecurityTokenService; 2 | using SharePointPnP.IdentityModel.Extensions.S2S.Tokens; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Web; 8 | 9 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2 10 | { 11 | public class OAuth2AccessTokenRequest : OAuth2Message 12 | { 13 | public static System.Collections.Specialized.StringCollection TokenResponseParameters = 14 | OAuth2AccessTokenRequest.GetTokenResponseParameters(); 15 | 16 | public string Password 17 | { 18 | get 19 | { 20 | return base.Message["password"]; 21 | } 22 | set 23 | { 24 | base.Message["password"] = value; 25 | } 26 | } 27 | 28 | public string RefreshToken 29 | { 30 | get 31 | { 32 | return base.Message["refresh_token"]; 33 | } 34 | set 35 | { 36 | base.Message["refresh_token"] = value; 37 | } 38 | } 39 | 40 | public string Resource 41 | { 42 | get 43 | { 44 | return base.Message["resource"]; 45 | } 46 | set 47 | { 48 | base.Message["resource"] = value; 49 | } 50 | } 51 | 52 | public string Scope 53 | { 54 | get 55 | { 56 | return base.Message["scope"]; 57 | } 58 | set 59 | { 60 | base.Message["scope"] = value; 61 | } 62 | } 63 | 64 | public string AppContext 65 | { 66 | get 67 | { 68 | return base["AppContext"]; 69 | } 70 | set 71 | { 72 | base["AppContext"] = value; 73 | } 74 | } 75 | 76 | public string Assertion 77 | { 78 | get 79 | { 80 | return base["assertion"]; 81 | } 82 | set 83 | { 84 | base["assertion"] = value; 85 | } 86 | } 87 | 88 | public string GrantType 89 | { 90 | get 91 | { 92 | return base["grant_type"]; 93 | } 94 | set 95 | { 96 | base["grant_type"] = value; 97 | } 98 | } 99 | 100 | public string ClientId 101 | { 102 | get 103 | { 104 | return base["client_id"]; 105 | } 106 | set 107 | { 108 | base["client_id"] = value; 109 | } 110 | } 111 | 112 | public string ClientSecret 113 | { 114 | get 115 | { 116 | return base["client_secret"]; 117 | } 118 | set 119 | { 120 | base["client_secret"] = value; 121 | } 122 | } 123 | 124 | public string Code 125 | { 126 | get 127 | { 128 | return base["code"]; 129 | } 130 | set 131 | { 132 | base["code"] = value; 133 | } 134 | } 135 | 136 | public string RedirectUri 137 | { 138 | get 139 | { 140 | return base["redirect_uri"]; 141 | } 142 | set 143 | { 144 | base["redirect_uri"] = value; 145 | } 146 | } 147 | 148 | public static OAuth2AccessTokenRequest Read(System.IO.StreamReader reader) 149 | { 150 | string requestString = null; 151 | try 152 | { 153 | requestString = reader.ReadToEnd(); 154 | } 155 | catch (System.Text.DecoderFallbackException innerException) 156 | { 157 | throw new System.IO.InvalidDataException("Request encoding is not ASCII", innerException); 158 | } 159 | return OAuth2AccessTokenRequest.Read(requestString); 160 | } 161 | 162 | public static OAuth2AccessTokenRequest Read(string requestString) 163 | { 164 | OAuth2AccessTokenRequest oAuth2AccessTokenRequest = new OAuth2AccessTokenRequest(); 165 | try 166 | { 167 | oAuth2AccessTokenRequest.Decode(requestString); 168 | } 169 | catch (InvalidRequestException) 170 | { 171 | System.Collections.Specialized.NameValueCollection nameValueCollection = HttpUtility.ParseQueryString(requestString); 172 | if (string.IsNullOrEmpty(nameValueCollection["client_id"]) && string.IsNullOrEmpty(nameValueCollection["assertion"])) 173 | { 174 | throw new System.IO.InvalidDataException("The request body must contain a client_id or assertion parameter."); 175 | } 176 | throw; 177 | } 178 | foreach (string current in oAuth2AccessTokenRequest.Keys) 179 | { 180 | if (OAuth2AccessTokenRequest.TokenResponseParameters.Contains(current)) 181 | { 182 | throw new System.IO.InvalidDataException(); 183 | } 184 | } 185 | return oAuth2AccessTokenRequest; 186 | } 187 | 188 | private static System.Collections.Specialized.StringCollection GetTokenResponseParameters() 189 | { 190 | return new System.Collections.Specialized.StringCollection 191 | { 192 | "access_token", 193 | "expires_in" 194 | }; 195 | } 196 | 197 | public void SetCustomProperty(string propertyName, string propertyValue) 198 | { 199 | Utility.VerifyNonNullOrEmptyStringArgument("propertyName", propertyName); 200 | Utility.VerifyNonNullOrEmptyStringArgument("propertyValue", propertyValue); 201 | base[propertyName] = propertyValue; 202 | } 203 | 204 | public virtual void Write(System.IO.StreamWriter writer) 205 | { 206 | if (writer == null) 207 | { 208 | throw new System.ArgumentNullException("writer"); 209 | } 210 | writer.Write(base.Encode()); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Protocols/OAuth2/OAuth2AccessTokenResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2 7 | { 8 | public class OAuth2AccessTokenResponse : OAuth2Message 9 | { 10 | public string AccessToken 11 | { 12 | get 13 | { 14 | return base.Message["access_token"]; 15 | } 16 | set 17 | { 18 | base.Message["access_token"] = value; 19 | } 20 | } 21 | 22 | public virtual string ExpiresIn 23 | { 24 | get 25 | { 26 | return base.Message["expires_in"]; 27 | } 28 | set 29 | { 30 | base.Message["expires_in"] = value; 31 | } 32 | } 33 | 34 | public System.DateTime ExpiresOn 35 | { 36 | get 37 | { 38 | return this.GetDateTimeParameter("expires_on"); 39 | } 40 | set 41 | { 42 | this.SetDateTimeParameter("expires_on", value); 43 | } 44 | } 45 | 46 | public System.DateTime NotBefore 47 | { 48 | get 49 | { 50 | return this.GetDateTimeParameter("not_before"); 51 | } 52 | set 53 | { 54 | this.SetDateTimeParameter("not_before", value); 55 | } 56 | } 57 | 58 | public string RefreshToken 59 | { 60 | get 61 | { 62 | return base.Message["refresh_token"]; 63 | } 64 | set 65 | { 66 | base.Message["refresh_token"] = value; 67 | } 68 | } 69 | 70 | public string Scope 71 | { 72 | get 73 | { 74 | return base.Message["scope"]; 75 | } 76 | set 77 | { 78 | base.Message["scope"] = value; 79 | } 80 | } 81 | 82 | public string TokenType 83 | { 84 | get 85 | { 86 | return base.Message["token_type"]; 87 | } 88 | set 89 | { 90 | base.Message["token_type"] = value; 91 | } 92 | } 93 | 94 | public static OAuth2AccessTokenResponse Read(string responseString) 95 | { 96 | OAuth2AccessTokenResponse oAuth2AccessTokenResponse = new OAuth2AccessTokenResponse(); 97 | oAuth2AccessTokenResponse.DecodeFromJson(responseString); 98 | return oAuth2AccessTokenResponse; 99 | } 100 | 101 | public override string ToString() 102 | { 103 | return base.EncodeToJson(); 104 | } 105 | 106 | private System.DateTime GetDateTimeParameter(string parameterName) 107 | { 108 | return new EpochTime(base.Message[parameterName]).DateTime; 109 | } 110 | 111 | private void SetDateTimeParameter(string parameterName, System.DateTime value) 112 | { 113 | base.Message[parameterName] = new EpochTime(value).SecondsSinceUnixEpoch.ToString(); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Protocols/OAuth2/OAuth2AuthorizationRequest.cs: -------------------------------------------------------------------------------- 1 | using SharePointPnP.IdentityModel.Extensions.S2S.Tokens; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2 8 | { 9 | internal class OAuth2AuthorizationRequest : OAuth2Message 10 | { 11 | public string ClientId 12 | { 13 | get 14 | { 15 | return base["client_id"]; 16 | } 17 | set 18 | { 19 | base["client_id"] = value; 20 | } 21 | } 22 | 23 | public string ResponseType 24 | { 25 | get 26 | { 27 | return base.Message["response_type"]; 28 | } 29 | set 30 | { 31 | base.Message["response_type"] = value; 32 | } 33 | } 34 | 35 | public string RedirectUri 36 | { 37 | get 38 | { 39 | return base["redirect_uri"]; 40 | } 41 | set 42 | { 43 | base["redirect_uri"] = value; 44 | } 45 | } 46 | 47 | public string Scope 48 | { 49 | get 50 | { 51 | return base.Message["scope"]; 52 | } 53 | set 54 | { 55 | base.Message["scope"] = value; 56 | } 57 | } 58 | 59 | public OAuth2AuthorizationRequest(string clientId) : this(clientId, "code") 60 | { 61 | } 62 | 63 | public OAuth2AuthorizationRequest(string clientId, string responseType) 64 | { 65 | Utility.VerifyNonNullOrEmptyStringArgument("clientId", clientId); 66 | Utility.VerifyNonNullOrEmptyStringArgument("responseType", responseType); 67 | this.ResponseType = responseType; 68 | this.ClientId = clientId; 69 | } 70 | 71 | public override string ToString() 72 | { 73 | return base.EncodeToJson(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Protocols/OAuth2/OAuth2AuthorizationResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2 7 | { 8 | internal class OAuth2AuthorizationResponse : OAuth2Message 9 | { 10 | public string Code 11 | { 12 | get 13 | { 14 | return base.Message["code"]; 15 | } 16 | set 17 | { 18 | base.Message["code"] = value; 19 | } 20 | } 21 | 22 | private OAuth2AuthorizationResponse() 23 | { 24 | } 25 | 26 | public static OAuth2AuthorizationResponse Read(string authorizationResponseString) 27 | { 28 | OAuth2AuthorizationResponse oAuth2AuthorizationResponse = new OAuth2AuthorizationResponse(); 29 | oAuth2AuthorizationResponse.DecodeFromJson(authorizationResponseString); 30 | return oAuth2AuthorizationResponse; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Protocols/OAuth2/OAuth2Constants.cs: -------------------------------------------------------------------------------- 1 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2 2 | { 3 | public static class OAuth2Constants 4 | { 5 | public static class GrantTypeConstants 6 | { 7 | public const string AuthorizationCode = "authorization_code"; 8 | 9 | public const string ClientCredentials = "client_credentials"; 10 | 11 | public const string RefreshToken = "refresh_token"; 12 | } 13 | 14 | public static class ContentTypes 15 | { 16 | public const string Json = "application/json"; 17 | 18 | public const string UrlEncoded = "application/x-www-form-urlencoded"; 19 | } 20 | 21 | public static class ErrorConstants 22 | { 23 | public const string Error = "error"; 24 | 25 | public const string ErrorDescription = "error_description"; 26 | 27 | public const string ErrorUri = "error_uri"; 28 | } 29 | 30 | public static class ErrorCodes 31 | { 32 | public const string InvalidClient = "invalid_client"; 33 | 34 | public const string InvalidGrant = "invalid_grant"; 35 | 36 | public const string InvalidRequest = "invalid_request"; 37 | 38 | public const string InvalidScope = "invalid_scope"; 39 | 40 | public const string UnauthorizedClient = "unauthorized_client"; 41 | 42 | public const string UnsupportedGrantType = "unsupported_grant_type"; 43 | 44 | public const string TemporarilyUnavailable = "temporarily_unavailable"; 45 | } 46 | 47 | public const string AccessToken = "access_token"; 48 | 49 | public const string Assertion = "assertion"; 50 | 51 | public const string ClientId = "client_id"; 52 | 53 | public const string ClientSecret = "client_secret"; 54 | 55 | public const string Code = "code"; 56 | 57 | public const string ExpiresIn = "expires_in"; 58 | 59 | public const string GrantType = "grant_type"; 60 | 61 | public const string BearerAuthenticationType = "Bearer"; 62 | 63 | public const string RedirectUri = "redirect_uri"; 64 | 65 | public const string RefreshToken = "refresh_token"; 66 | 67 | public const string ResponseType = "response_type"; 68 | 69 | public const string Scope = "scope"; 70 | 71 | public const string State = "state"; 72 | 73 | public const string TokenType = "token_type"; 74 | 75 | public const string Password = "password"; 76 | 77 | public const string AppContext = "AppContext"; 78 | 79 | public const string ExpiresOn = "expires_on"; 80 | 81 | public const string NotBefore = "not_before"; 82 | 83 | public const string Resource = "resource"; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Protocols/OAuth2/OAuth2ErrorCode.cs: -------------------------------------------------------------------------------- 1 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2 2 | { 3 | public class OAuth2ErrorCode 4 | { 5 | private readonly string _value; 6 | 7 | public static OAuth2ErrorCode InvalidClient = new OAuth2ErrorCode("invalid_client"); 8 | 9 | public static OAuth2ErrorCode InvalidGrant = new OAuth2ErrorCode("invalid_grant"); 10 | 11 | public static OAuth2ErrorCode InvalidRequest = new OAuth2ErrorCode("invalid_request"); 12 | 13 | public static OAuth2ErrorCode InvalidScope = new OAuth2ErrorCode("invalid_scope"); 14 | 15 | public static OAuth2ErrorCode UnauthorizedClient = new OAuth2ErrorCode("unauthorized_client"); 16 | 17 | public static OAuth2ErrorCode UnsupportedGrantType = new OAuth2ErrorCode("unsupported_grant_type"); 18 | 19 | public static OAuth2ErrorCode TemporarilyUnavailable = new OAuth2ErrorCode("temporarily_unavailable"); 20 | 21 | public OAuth2ErrorCode(string errorCode) 22 | { 23 | switch (errorCode) 24 | { 25 | case "invalid_client": 26 | case "invalid_grant": 27 | case "invalid_request": 28 | case "invalid_scope": 29 | case "temporarily_unavailable": 30 | case "unauthorized_client": 31 | case "unsupported_grant_type": 32 | this._value = errorCode; 33 | return; 34 | } 35 | throw new System.InvalidOperationException(string.Format(System.Globalization.CultureInfo.InvariantCulture, "'{0}' is not a supported OAuth2 error code value.", new object[] 36 | { 37 | errorCode 38 | })); 39 | } 40 | 41 | public override string ToString() 42 | { 43 | return this._value; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Protocols/OAuth2/OAuth2ErrorResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2 7 | { 8 | public class OAuth2ErrorResponse : OAuth2Message 9 | { 10 | public string Error 11 | { 12 | get 13 | { 14 | return base.Message["error"]; 15 | } 16 | set 17 | { 18 | if (string.IsNullOrEmpty(value)) 19 | { 20 | throw new System.ArgumentException("Error property cannot be null or empty.", "value"); 21 | } 22 | base.Message["error"] = value; 23 | } 24 | } 25 | 26 | public string ErrorDescription 27 | { 28 | get 29 | { 30 | return base.Message["error_description"]; 31 | } 32 | set 33 | { 34 | base.Message["error_description"] = value; 35 | } 36 | } 37 | 38 | public string ErrorUri 39 | { 40 | get 41 | { 42 | return base.Message["error_uri"]; 43 | } 44 | set 45 | { 46 | base.Message["error_uri"] = value; 47 | } 48 | } 49 | 50 | public static OAuth2ErrorResponse CreateFromEncodedResponse(string responseString) 51 | { 52 | OAuth2ErrorResponse oAuth2ErrorResponse = new OAuth2ErrorResponse(); 53 | oAuth2ErrorResponse.DecodeFromJson(responseString); 54 | if (string.IsNullOrEmpty(oAuth2ErrorResponse.Error)) 55 | { 56 | throw new System.ArgumentException("Error property is null or empty. This message is not a valid OAuth2 error response.", "responseString"); 57 | } 58 | return oAuth2ErrorResponse; 59 | } 60 | 61 | private OAuth2ErrorResponse() 62 | { 63 | } 64 | 65 | public OAuth2ErrorResponse(string error) 66 | { 67 | this.Error = error; 68 | } 69 | 70 | public override string ToString() 71 | { 72 | return base.EncodeToJson(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Protocols/OAuth2/OAuth2Message.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2 7 | { 8 | public abstract class OAuth2Message 9 | { 10 | private System.Collections.Generic.Dictionary _message = new System.Collections.Generic.Dictionary(System.StringComparer.Ordinal); 11 | 12 | protected string this[string index] 13 | { 14 | get 15 | { 16 | return this.GetValue(index); 17 | } 18 | set 19 | { 20 | this._message[index] = value; 21 | } 22 | } 23 | 24 | protected System.Collections.Generic.IEnumerable Keys 25 | { 26 | get 27 | { 28 | return this._message.Keys; 29 | } 30 | } 31 | 32 | public System.Collections.Generic.Dictionary Message 33 | { 34 | get 35 | { 36 | return this._message; 37 | } 38 | } 39 | 40 | public override string ToString() 41 | { 42 | return this.Encode(); 43 | } 44 | 45 | protected bool ContainsKey(string key) 46 | { 47 | return this._message.ContainsKey(key); 48 | } 49 | 50 | protected void Decode(string message) 51 | { 52 | this._message.Decode(message); 53 | } 54 | 55 | protected void DecodeFromJson(string message) 56 | { 57 | this._message.DecodeFromJson(message); 58 | } 59 | 60 | protected string Encode() 61 | { 62 | return this._message.Encode(); 63 | } 64 | 65 | protected string EncodeToJson() 66 | { 67 | return this._message.EncodeToJson(); 68 | } 69 | 70 | protected string GetValue(string key) 71 | { 72 | if (string.IsNullOrEmpty(key)) 73 | { 74 | throw new System.ArgumentException("The input string parameter is either null or empty.", "key"); 75 | } 76 | string result = null; 77 | this._message.TryGetValue(key, out result); 78 | return result; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Protocols/OAuth2/OAuth2MessageFactory.cs: -------------------------------------------------------------------------------- 1 | using SharePointPnP.IdentityModel.Extensions.S2S.Tokens; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2 8 | { 9 | public static class OAuth2MessageFactory 10 | { 11 | public static OAuth2Message CreateFromEncodedResponse(System.IO.StreamReader reader) 12 | { 13 | return OAuth2MessageFactory.CreateFromEncodedResponse(reader.ReadToEnd()); 14 | } 15 | 16 | public static OAuth2Message CreateFromEncodedResponse(string responseString) 17 | { 18 | if (responseString.StartsWith("{\"error")) 19 | { 20 | return OAuth2ErrorResponse.CreateFromEncodedResponse(responseString); 21 | } 22 | return OAuth2AccessTokenResponse.Read(responseString); 23 | } 24 | 25 | public static OAuth2AccessTokenRequest CreateAccessTokenRequestWithAuthorizationCode(string clientId, string clientSecret, string authorizationCode, System.Uri redirectUri, string resource) 26 | { 27 | OAuth2AccessTokenRequest oAuth2AccessTokenRequest = new OAuth2AccessTokenRequest(); 28 | oAuth2AccessTokenRequest.GrantType = "authorization_code"; 29 | oAuth2AccessTokenRequest.ClientId = clientId; 30 | oAuth2AccessTokenRequest.ClientSecret = clientSecret; 31 | oAuth2AccessTokenRequest.Code = authorizationCode; 32 | if (redirectUri != null) 33 | { 34 | oAuth2AccessTokenRequest.RedirectUri = redirectUri.AbsoluteUri; 35 | } 36 | oAuth2AccessTokenRequest.Resource = resource; 37 | return oAuth2AccessTokenRequest; 38 | } 39 | 40 | public static OAuth2AccessTokenRequest CreateAccessTokenRequestWithAuthorizationCode(string clientId, string clientSecret, string authorizationCode, string resource) 41 | { 42 | return new OAuth2AccessTokenRequest 43 | { 44 | GrantType = "authorization_code", 45 | ClientId = clientId, 46 | ClientSecret = clientSecret, 47 | Code = authorizationCode, 48 | Resource = resource 49 | }; 50 | } 51 | 52 | public static OAuth2AccessTokenRequest CreateAccessTokenRequestWithRefreshToken(string clientId, string clientSecret, string refreshToken, string resource) 53 | { 54 | return new OAuth2AccessTokenRequest 55 | { 56 | GrantType = "refresh_token", 57 | ClientId = clientId, 58 | ClientSecret = clientSecret, 59 | RefreshToken = refreshToken, 60 | Resource = resource 61 | }; 62 | } 63 | 64 | public static OAuth2AccessTokenRequest CreateAccessTokenRequestWithClientCredentials(string clientId, string clientSecret, string scope) 65 | { 66 | return new OAuth2AccessTokenRequest 67 | { 68 | GrantType = "client_credentials", 69 | ClientId = clientId, 70 | ClientSecret = clientSecret, 71 | Scope = scope 72 | }; 73 | } 74 | 75 | public static OAuth2AccessTokenRequest CreateAccessTokenRequestWithAssertion(System.IdentityModel.Tokens.SecurityToken token, string resource) 76 | { 77 | Utility.VerifyNonNullArgument("token", token); 78 | Microsoft.IdentityModel.Tokens.SecurityTokenHandlerCollection securityTokenHandlerCollection = Microsoft.IdentityModel.Tokens.SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection(); 79 | securityTokenHandlerCollection.Add(new JsonWebSecurityTokenHandler()); 80 | return OAuth2MessageFactory.CreateAccessTokenRequestWithAssertion(token, securityTokenHandlerCollection, resource); 81 | } 82 | 83 | public static OAuth2AccessTokenRequest CreateAccessTokenRequestWithAssertion(System.IdentityModel.Tokens.SecurityToken token, Microsoft.IdentityModel.Tokens.SecurityTokenHandlerCollection securityTokenHandlers, string resource) 84 | { 85 | Utility.VerifyNonNullArgument("token", token); 86 | if (token is JsonWebSecurityToken) 87 | { 88 | return OAuth2MessageFactory.CreateAccessTokenRequestWithAssertion((JsonWebSecurityToken)token, securityTokenHandlers, resource); 89 | } 90 | if (token is System.IdentityModel.Tokens.GenericXmlSecurityToken) 91 | { 92 | return OAuth2MessageFactory.CreateAccessTokenRequestWithAssertion((System.IdentityModel.Tokens.GenericXmlSecurityToken)token, resource); 93 | } 94 | if (token is System.IdentityModel.Tokens.SamlSecurityToken || token is Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken) 95 | { 96 | return OAuth2MessageFactory.CreateAccessTokenRequestWithAssertionForSamlSecurityTokens(token, securityTokenHandlers, resource); 97 | } 98 | throw new System.ArgumentException("Unsupported SecurityToken"); 99 | } 100 | 101 | private static OAuth2AccessTokenRequest CreateAccessTokenRequestWithAssertion(System.IdentityModel.Tokens.GenericXmlSecurityToken token, string resource) 102 | { 103 | Utility.VerifyNonNullArgument("token", token); 104 | OAuth2AccessTokenRequest oAuth2AccessTokenRequest = new OAuth2AccessTokenRequest(); 105 | JsonWebSecurityTokenHandler jsonWebSecurityTokenHandler = new JsonWebSecurityTokenHandler(); 106 | System.Xml.XmlReader reader = new System.Xml.XmlNodeReader(token.TokenXml); 107 | string text; 108 | string jsonTokenString = jsonWebSecurityTokenHandler.GetJsonTokenString(reader, out text); 109 | oAuth2AccessTokenRequest.GrantType = OAuth2MessageFactory.GetTokenType(token); 110 | oAuth2AccessTokenRequest.Assertion = jsonTokenString; 111 | oAuth2AccessTokenRequest.Resource = resource; 112 | return oAuth2AccessTokenRequest; 113 | } 114 | 115 | private static OAuth2AccessTokenRequest CreateAccessTokenRequestWithAssertionForSamlSecurityTokens(System.IdentityModel.Tokens.SecurityToken token, Microsoft.IdentityModel.Tokens.SecurityTokenHandlerCollection securityTokenHandlers, string resource) 116 | { 117 | Utility.VerifyNonNullArgument("securityTokenHandlers", securityTokenHandlers); 118 | OAuth2AccessTokenRequest oAuth2AccessTokenRequest = new OAuth2AccessTokenRequest(); 119 | if (token is System.IdentityModel.Tokens.SamlSecurityToken) 120 | { 121 | oAuth2AccessTokenRequest.GrantType = "urn:oasis:names:tc:SAML:1.0:assertion"; 122 | } 123 | else 124 | { 125 | oAuth2AccessTokenRequest.GrantType = "urn:oasis:names:tc:SAML:2.0:assertion"; 126 | } 127 | System.Xml.XmlWriterSettings xmlWriterSettings = new System.Xml.XmlWriterSettings(); 128 | System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); 129 | xmlWriterSettings.OmitXmlDeclaration = true; 130 | using (System.Xml.XmlWriter xmlWriter = System.Xml.XmlWriter.Create(stringBuilder, xmlWriterSettings)) 131 | { 132 | securityTokenHandlers.WriteToken(xmlWriter, token); 133 | oAuth2AccessTokenRequest.Assertion = stringBuilder.ToString(); 134 | } 135 | oAuth2AccessTokenRequest.Resource = resource; 136 | return oAuth2AccessTokenRequest; 137 | } 138 | 139 | private static OAuth2AccessTokenRequest CreateAccessTokenRequestWithAssertion(JsonWebSecurityToken token, Microsoft.IdentityModel.Tokens.SecurityTokenHandlerCollection securityTokenHandlers, string resource) 140 | { 141 | Utility.VerifyNonNullArgument("token", token); 142 | Utility.VerifyNonNullArgument("securityTokenHandlers", securityTokenHandlers); 143 | JsonWebSecurityTokenHandler jsonWebSecurityTokenHandler = securityTokenHandlers[typeof(JsonWebSecurityToken)] as JsonWebSecurityTokenHandler; 144 | if (jsonWebSecurityTokenHandler == null) 145 | { 146 | throw new System.ArgumentException("The input security token handlers collection does not contain a handler for JWT tokens.", "securityTokenHandlers"); 147 | } 148 | string assertion = jsonWebSecurityTokenHandler.WriteTokenAsString(token); 149 | return new OAuth2AccessTokenRequest 150 | { 151 | GrantType = "http://oauth.net/grant_type/jwt/1.0/bearer", 152 | Assertion = assertion, 153 | Resource = resource 154 | }; 155 | } 156 | 157 | private static string GetTokenType(System.IdentityModel.Tokens.GenericXmlSecurityToken token) 158 | { 159 | using (System.Xml.XmlReader xmlReader = new System.Xml.XmlNodeReader(token.TokenXml)) 160 | { 161 | xmlReader.MoveToContent(); 162 | if (xmlReader.IsStartElement("BinarySecurityToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")) 163 | { 164 | return xmlReader.GetAttribute("ValueType", null); 165 | } 166 | } 167 | return string.Empty; 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Protocols/OAuth2/OAuth2S2SClient.cs: -------------------------------------------------------------------------------- 1 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2 2 | { 3 | using Microsoft.IdentityModel.SecurityTokenService; 4 | 5 | public class OAuth2S2SClient 6 | { 7 | public OAuth2Message Issue(string securityTokenServiceUrl, OAuth2AccessTokenRequest oauth2Request) 8 | { 9 | OAuth2WebRequest oAuth2WebRequest = new OAuth2WebRequest(securityTokenServiceUrl, oauth2Request); 10 | OAuth2Message result; 11 | try 12 | { 13 | System.Net.WebResponse response = oAuth2WebRequest.GetResponse(); 14 | result = OAuth2MessageFactory.CreateFromEncodedResponse(new System.IO.StreamReader(response.GetResponseStream())); 15 | } 16 | catch (System.Exception innerException) 17 | { 18 | throw new RequestFailedException("Token request failed.", innerException); 19 | } 20 | return result; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Protocols/OAuth2/OAuth2WebRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Protocols.OAuth2 7 | { 8 | public class OAuth2WebRequest : System.Net.WebRequest 9 | { 10 | private static readonly System.TimeSpan DefaultTimeout = System.TimeSpan.FromMinutes(10.0); 11 | 12 | private System.Net.WebRequest _innerRequest; 13 | 14 | private OAuth2AccessTokenRequest _request; 15 | 16 | public OAuth2WebRequest(string requestUriString, OAuth2AccessTokenRequest request) 17 | { 18 | this._innerRequest = System.Net.WebRequest.Create(requestUriString); 19 | this._request = request; 20 | } 21 | 22 | public override System.Net.WebResponse GetResponse() 23 | { 24 | string text = this._request.ToString(); 25 | this._innerRequest.AuthenticationLevel = System.Net.Security.AuthenticationLevel.None; 26 | this._innerRequest.ContentLength = (long)text.Length; 27 | this._innerRequest.ContentType = "application/x-www-form-urlencoded"; 28 | this._innerRequest.Method = "POST"; 29 | this._innerRequest.Timeout = (int)OAuth2WebRequest.DefaultTimeout.TotalMilliseconds; 30 | System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(this._innerRequest.GetRequestStream(), System.Text.Encoding.ASCII); 31 | streamWriter.Write(text); 32 | streamWriter.Close(); 33 | return this._innerRequest.GetResponse(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/AudienceValidator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.IdentityModel.Tokens; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 8 | { 9 | internal static class AudienceValidator 10 | { 11 | public static void ValidateAudiences(System.Collections.Generic.IList allowedAudiences, System.Collections.Generic.IList tokenAudiences) 12 | { 13 | if (allowedAudiences == null) 14 | { 15 | throw new System.ArgumentNullException("allowedAudiences"); 16 | } 17 | if (tokenAudiences == null) 18 | { 19 | throw new System.ArgumentNullException("tokenAudiences"); 20 | } 21 | if (tokenAudiences.Count == 0) 22 | { 23 | throw new AudienceUriValidationFailedException("Audience URI validation failed. No token audiences were found."); 24 | } 25 | if (allowedAudiences.Count == 0) 26 | { 27 | throw new AudienceUriValidationFailedException("Audience URI validation failed. No allowed audiences are configured."); 28 | } 29 | foreach (System.Uri current in tokenAudiences) 30 | { 31 | if (current != null) 32 | { 33 | foreach (System.Uri current2 in allowedAudiences) 34 | { 35 | if (System.Uri.Compare(current2, current, System.UriComponents.AbsoluteUri, System.UriFormat.Unescaped, System.StringComparison.OrdinalIgnoreCase) == 0) 36 | { 37 | return; 38 | } 39 | System.Uri uri; 40 | if (!current2.OriginalString.EndsWith("/") && System.Uri.TryCreate(current2.OriginalString + "/", System.UriKind.RelativeOrAbsolute, out uri) && System.Uri.Compare(uri, current, System.UriComponents.AbsoluteUri, System.UriFormat.Unescaped, System.StringComparison.OrdinalIgnoreCase) == 0) 41 | { 42 | return; 43 | } 44 | } 45 | } 46 | } 47 | throw new AudienceUriValidationFailedException("Audience URI validation failed. Audience does not match."); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/Base64UrlEncoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 7 | { 8 | internal static class Base64UrlEncoder 9 | { 10 | public static System.Text.Encoding TextEncoding = System.Text.Encoding.UTF8; 11 | 12 | private static char Base64PadCharacter = '='; 13 | 14 | private static string DoubleBase64PadCharacter = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{0}", new object[] 15 | { 16 | Base64UrlEncoder.Base64PadCharacter 17 | }); 18 | 19 | private static char Base64Character62 = '+'; 20 | 21 | private static char Base64Character63 = '/'; 22 | 23 | private static char Base64UrlCharacter62 = '-'; 24 | 25 | private static char Base64UrlCharacter63 = '_'; 26 | 27 | public static string Encode(string arg) 28 | { 29 | Utility.VerifyNonNullOrEmptyStringArgument("arg", arg); 30 | return Base64UrlEncoder.Encode(Base64UrlEncoder.TextEncoding.GetBytes(arg)); 31 | } 32 | 33 | public static string Encode(byte[] arg) 34 | { 35 | Utility.VerifyNonNullArgument("arg", arg); 36 | string text = System.Convert.ToBase64String(arg); 37 | text = text.Split(new char[] 38 | { 39 | Base64UrlEncoder.Base64PadCharacter 40 | })[0]; 41 | text = text.Replace(Base64UrlEncoder.Base64Character62, Base64UrlEncoder.Base64UrlCharacter62); 42 | return text.Replace(Base64UrlEncoder.Base64Character63, Base64UrlEncoder.Base64UrlCharacter63); 43 | } 44 | 45 | public static byte[] DecodeBytes(string arg) 46 | { 47 | Utility.VerifyNonNullOrEmptyStringArgument("arg", arg); 48 | string text = arg.Replace(Base64UrlEncoder.Base64UrlCharacter62, Base64UrlEncoder.Base64Character62); 49 | text = text.Replace(Base64UrlEncoder.Base64UrlCharacter63, Base64UrlEncoder.Base64Character63); 50 | switch (text.Length % 4) 51 | { 52 | case 0: 53 | goto IL_7D; 54 | case 2: 55 | text += Base64UrlEncoder.DoubleBase64PadCharacter; 56 | goto IL_7D; 57 | case 3: 58 | text += Base64UrlEncoder.Base64PadCharacter; 59 | goto IL_7D; 60 | } 61 | throw new System.ArgumentException("Illegal base64url string!", arg); 62 | IL_7D: 63 | return System.Convert.FromBase64String(text); 64 | } 65 | 66 | public static string Decode(string arg) 67 | { 68 | return Base64UrlEncoder.TextEncoding.GetString(Base64UrlEncoder.DecodeBytes(arg)); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/JsonWebSecurityToken.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.IdentityModel; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.ServiceModel.Security.Tokens; 6 | using System.Threading.Tasks; 7 | 8 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 9 | { 10 | public class JsonWebSecurityToken : System.IdentityModel.Tokens.SecurityToken 11 | { 12 | private JsonWebSecurityToken _actorToken; 13 | 14 | private string _audience; 15 | 16 | private System.Collections.Generic.List _claims; 17 | 18 | private string _id; 19 | 20 | private string _issuer; 21 | 22 | private System.IdentityModel.Tokens.SecurityToken _issuerToken; 23 | 24 | private System.IdentityModel.Tokens.SigningCredentials _signingCredentials; 25 | 26 | private string _sourceData; 27 | 28 | private System.DateTime _validFrom; 29 | 30 | private System.DateTime _validTo; 31 | 32 | public JsonWebSecurityToken ActorToken 33 | { 34 | get 35 | { 36 | return this._actorToken; 37 | } 38 | } 39 | 40 | public string Audience 41 | { 42 | get 43 | { 44 | return this._audience; 45 | } 46 | } 47 | 48 | public virtual bool CanWriteSourceData 49 | { 50 | get 51 | { 52 | return !string.IsNullOrEmpty(this._sourceData); 53 | } 54 | } 55 | 56 | public System.Collections.ObjectModel.ReadOnlyCollection Claims 57 | { 58 | get 59 | { 60 | return new System.Collections.ObjectModel.ReadOnlyCollection(this._claims); 61 | } 62 | } 63 | 64 | public override string Id 65 | { 66 | get 67 | { 68 | return this._id; 69 | } 70 | } 71 | 72 | public string Issuer 73 | { 74 | get 75 | { 76 | return this._issuer; 77 | } 78 | } 79 | 80 | public System.IdentityModel.Tokens.SecurityToken IssuerToken 81 | { 82 | get 83 | { 84 | return this._issuerToken; 85 | } 86 | } 87 | 88 | public override System.Collections.ObjectModel.ReadOnlyCollection SecurityKeys 89 | { 90 | get 91 | { 92 | return new System.Collections.ObjectModel.ReadOnlyCollection(new System.Collections.Generic.List()); 93 | } 94 | } 95 | 96 | public System.IdentityModel.Tokens.SigningCredentials SigningCredentials 97 | { 98 | get 99 | { 100 | return this._signingCredentials; 101 | } 102 | } 103 | 104 | public override System.DateTime ValidFrom 105 | { 106 | get 107 | { 108 | return this._validFrom; 109 | } 110 | } 111 | 112 | public override System.DateTime ValidTo 113 | { 114 | get 115 | { 116 | return this._validTo; 117 | } 118 | } 119 | 120 | public JsonWebSecurityToken(string issuer, string audience, System.DateTime validFrom, System.DateTime validTo, System.Collections.Generic.IEnumerable claims, System.IdentityModel.Tokens.SigningCredentials signingCredentials) : this(issuer, audience, validFrom, validTo, claims) 121 | { 122 | Utility.VerifyNonNullArgument("signingCredentials", signingCredentials); 123 | this._signingCredentials = signingCredentials; 124 | } 125 | 126 | public JsonWebSecurityToken(string issuer, string audience, System.DateTime validFrom, System.DateTime validTo, System.Collections.Generic.IEnumerable claims, System.IdentityModel.Tokens.SecurityToken issuerToken, JsonWebSecurityToken actorToken) : this(issuer, audience, validFrom, validTo, claims) 127 | { 128 | this._issuerToken = issuerToken; 129 | this._actorToken = actorToken; 130 | } 131 | 132 | public JsonWebSecurityToken(string issuer, string audience, System.DateTime validFrom, System.DateTime validTo, System.Collections.Generic.IEnumerable claims) 133 | { 134 | Utility.VerifyNonNullOrEmptyStringArgument("issuer", issuer); 135 | Utility.VerifyNonNullOrEmptyStringArgument("audience", audience); 136 | Utility.VerifyNonNullArgument("claims", claims); 137 | this._id = UniqueId.CreateUniqueId(); 138 | this._issuer = issuer; 139 | this._audience = audience; 140 | this._validFrom = DateTimeUtil.ToUniversalTime(validFrom); 141 | this._validTo = DateTimeUtil.ToUniversalTime(validTo); 142 | this._claims = new System.Collections.Generic.List(claims); 143 | } 144 | 145 | public virtual void CaptureSourceData(string tokenString) 146 | { 147 | Utility.VerifyNonNullOrEmptyStringArgument("tokenString", tokenString); 148 | this._sourceData = tokenString; 149 | } 150 | 151 | internal void SetId(string id) 152 | { 153 | this._id = id; 154 | } 155 | 156 | public override string ToString() 157 | { 158 | System.Collections.Generic.IDictionary self = this.CreateHeaderClaims(); 159 | System.Collections.Generic.IDictionary self2 = this.CreatePayloadClaims(); 160 | return string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}.{1}", new object[] 161 | { 162 | self.EncodeToJson(), 163 | self2.EncodeToJson() 164 | }); 165 | } 166 | 167 | public virtual string WriteSourceData() 168 | { 169 | if (!this.CanWriteSourceData) 170 | { 171 | throw new System.InvalidOperationException("This token's raw data cannot be re-emitted. The token was not deserialized in the first place."); 172 | } 173 | return this._sourceData; 174 | } 175 | 176 | public virtual System.Collections.Generic.IDictionary CreatePayloadClaims() 177 | { 178 | System.Collections.Generic.Dictionary dictionary = new System.Collections.Generic.Dictionary(System.StringComparer.Ordinal); 179 | dictionary.Add("aud", this.Audience); 180 | dictionary.Add("iss", this.Issuer); 181 | dictionary.Add("nbf", this.GetTimeInSeconds(this.ValidFrom)); 182 | dictionary.Add("exp", this.GetTimeInSeconds(this.ValidTo)); 183 | foreach (JsonWebTokenClaim current in this.Claims) 184 | { 185 | dictionary.Add(current.ClaimType, current.Value); 186 | } 187 | return dictionary; 188 | } 189 | 190 | public virtual System.Collections.Generic.IDictionary CreateHeaderClaims() 191 | { 192 | System.Collections.Generic.Dictionary dictionary = new System.Collections.Generic.Dictionary(System.StringComparer.Ordinal); 193 | dictionary.Add("typ", "JWT"); 194 | if (this.SigningCredentials != null) 195 | { 196 | if (System.StringComparer.Ordinal.Compare(this.SigningCredentials.SignatureAlgorithm, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256") == 0) 197 | { 198 | Microsoft.IdentityModel.SecurityTokenService.X509SigningCredentials x509SigningCredentials = this.SigningCredentials as Microsoft.IdentityModel.SecurityTokenService.X509SigningCredentials; 199 | if (x509SigningCredentials == null) 200 | { 201 | throw new System.InvalidOperationException("JWT token is not valid. RSA signature requires X509SigningCredentials"); 202 | } 203 | dictionary.Add("alg", "RS256"); 204 | dictionary.Add("x5t", Base64UrlEncoder.Encode(x509SigningCredentials.Certificate.GetCertHash())); 205 | } 206 | else if (System.StringComparer.Ordinal.Compare(this.SigningCredentials.SignatureAlgorithm, "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256") == 0) 207 | { 208 | dictionary.Add("alg", "HS256"); 209 | } 210 | } 211 | else if (this.IssuerToken != null) 212 | { 213 | System.IdentityModel.Tokens.X509SecurityToken x509SecurityToken = this.IssuerToken as System.IdentityModel.Tokens.X509SecurityToken; 214 | if (x509SecurityToken != null) 215 | { 216 | dictionary.Add("alg", "RS256"); 217 | dictionary.Add("x5t", Base64UrlEncoder.Encode(x509SecurityToken.Certificate.GetCertHash())); 218 | } 219 | else if (this.IssuerToken is BinarySecretSecurityToken) 220 | { 221 | dictionary.Add("alg", "HS256"); 222 | } 223 | } 224 | else 225 | { 226 | dictionary.Add("alg", "none"); 227 | } 228 | return dictionary; 229 | } 230 | 231 | private string GetTimeInSeconds(System.DateTime time) 232 | { 233 | return new EpochTime(time).SecondsSinceUnixEpoch.ToString(); 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/JsonWebSecurityTokenHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.IdentityModel.Claims; 2 | using System.ServiceModel.Security.Tokens; 3 | using System.Xml; 4 | 5 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 6 | { 7 | public class JsonWebSecurityTokenHandler : Microsoft.IdentityModel.Tokens.SecurityTokenHandler 8 | { 9 | private const string JsonCompactSerializationRegex = "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$"; 10 | 11 | private JsonWebSecurityTokenRequirement _jsonWebSecurityTokenRequirement; 12 | 13 | public override bool CanValidateToken 14 | { 15 | get 16 | { 17 | return true; 18 | } 19 | } 20 | 21 | public override bool CanWriteToken 22 | { 23 | get 24 | { 25 | return true; 26 | } 27 | } 28 | 29 | public JsonWebSecurityTokenRequirement JsonWebSecurityTokenRequirement 30 | { 31 | get 32 | { 33 | return this._jsonWebSecurityTokenRequirement; 34 | } 35 | set 36 | { 37 | Utility.VerifyNonNullArgument("jsonWebSecurityTokenRequirement", value); 38 | this._jsonWebSecurityTokenRequirement = value; 39 | } 40 | } 41 | 42 | public override System.Type TokenType 43 | { 44 | get 45 | { 46 | return typeof(JsonWebSecurityToken); 47 | } 48 | } 49 | 50 | public JsonWebSecurityTokenHandler() : this(new JsonWebSecurityTokenRequirement()) 51 | { 52 | } 53 | 54 | public JsonWebSecurityTokenHandler(JsonWebSecurityTokenRequirement jsonWebSecurityTokenRequirement) 55 | { 56 | Utility.VerifyNonNullArgument("jsonWebSecurityTokenRequirement", jsonWebSecurityTokenRequirement); 57 | this._jsonWebSecurityTokenRequirement = jsonWebSecurityTokenRequirement; 58 | } 59 | 60 | public override bool CanReadToken(System.Xml.XmlReader reader) 61 | { 62 | Utility.VerifyNonNullArgument("reader", reader); 63 | return this.IsJsonWebSecurityToken(reader); 64 | } 65 | 66 | public virtual bool CanReadToken(string token) 67 | { 68 | Utility.VerifyNonNullOrEmptyStringArgument("token", token); 69 | return this.IsJsonWebSecurityToken(token); 70 | } 71 | 72 | public override System.IdentityModel.Tokens.SecurityKeyIdentifierClause CreateSecurityTokenReference(System.IdentityModel.Tokens.SecurityToken token, bool attached) 73 | { 74 | return null; 75 | } 76 | 77 | public override System.IdentityModel.Tokens.SecurityToken CreateToken(Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor tokenDescriptor) 78 | { 79 | Utility.VerifyNonNullArgument("tokenDescriptor", tokenDescriptor); 80 | if (tokenDescriptor.SigningCredentials == null) 81 | { 82 | throw new System.ArgumentException("tokenDescriptor.SigningCredentials cannot be null"); 83 | } 84 | if (tokenDescriptor.Subject == null) 85 | { 86 | throw new System.ArgumentException("tokenDescriptor.Subject cannot be null"); 87 | } 88 | if (string.IsNullOrEmpty(tokenDescriptor.TokenIssuerName)) 89 | { 90 | throw new System.ArgumentException("tokenDescriptor.TokenIssuerName cannot be null"); 91 | } 92 | System.DateTime dateTime = System.DateTime.UtcNow; 93 | System.DateTime validTo = DateTimeUtil.Add(dateTime, System.TimeSpan.FromHours(1.0)); 94 | if (tokenDescriptor.Lifetime != null) 95 | { 96 | if (tokenDescriptor.Lifetime.Created.HasValue) 97 | { 98 | dateTime = DateTimeUtil.ToUniversalTime(tokenDescriptor.Lifetime.Created.Value); 99 | } 100 | if (tokenDescriptor.Lifetime.Expires.HasValue) 101 | { 102 | validTo = DateTimeUtil.ToUniversalTime(tokenDescriptor.Lifetime.Expires.Value); 103 | } 104 | } 105 | System.Collections.Generic.List list = new System.Collections.Generic.List(); 106 | foreach (Claim current in tokenDescriptor.Subject.Claims) 107 | { 108 | list.Add(new JsonWebTokenClaim(current.ClaimType, current.Value)); 109 | } 110 | return new JsonWebSecurityToken(tokenDescriptor.TokenIssuerName, this.GetAppliesTo(tokenDescriptor), dateTime, validTo, list, tokenDescriptor.SigningCredentials); 111 | } 112 | 113 | protected virtual string GetAppliesTo(Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor tokenDescriptor) 114 | { 115 | if (string.IsNullOrEmpty(tokenDescriptor.AppliesToAddress)) 116 | { 117 | throw new System.ArgumentException("tokenDescriptor.AppliesToAddress cannot be null"); 118 | } 119 | return tokenDescriptor.AppliesToAddress; 120 | } 121 | 122 | protected virtual string GetIssuerName(JsonWebSecurityToken token) 123 | { 124 | if (token.IssuerToken == null) 125 | { 126 | throw new System.IdentityModel.Tokens.SecurityTokenException("JWT tokens must be signed."); 127 | } 128 | string issuerName = base.Configuration.IssuerNameRegistry.GetIssuerName(token.IssuerToken, token.Issuer); 129 | if (string.IsNullOrEmpty(issuerName)) 130 | { 131 | throw new System.IdentityModel.Tokens.SecurityTokenException("Invalid issuer or signature."); 132 | } 133 | return issuerName; 134 | } 135 | 136 | protected virtual System.IdentityModel.Tokens.SecurityKeyIdentifier GetSigningKeyIdentifier(System.Collections.Generic.IDictionary header, System.Collections.Generic.IDictionary payload) 137 | { 138 | string x; 139 | if (!header.TryGetValue("alg", out x)) 140 | { 141 | throw new System.IdentityModel.Tokens.SecurityTokenException("Invalid JWT token. No signature algorithm specified in token header."); 142 | } 143 | System.IdentityModel.Tokens.SecurityKeyIdentifierClause securityKeyIdentifierClause; 144 | if (System.StringComparer.Ordinal.Equals(x, "RS256")) 145 | { 146 | string arg; 147 | if (!header.TryGetValue("x5t", out arg)) 148 | { 149 | throw new System.IdentityModel.Tokens.SecurityTokenException("Invalid JWT token. No certificate thumbprint specified in token header."); 150 | } 151 | securityKeyIdentifierClause = new System.IdentityModel.Tokens.X509ThumbprintKeyIdentifierClause(Base64UrlEncoder.DecodeBytes(arg)); 152 | } 153 | else 154 | { 155 | if (!System.StringComparer.Ordinal.Equals(x, "HS256")) 156 | { 157 | throw new System.IdentityModel.Tokens.SecurityTokenException("Invalid JWT token. Didn't find a supported signature algorithm in token header."); 158 | } 159 | string issuer; 160 | payload.TryGetValue("iss", out issuer); 161 | securityKeyIdentifierClause = new SymmetricIssuerKeyIdentifierClause(issuer); 162 | } 163 | return new System.IdentityModel.Tokens.SecurityKeyIdentifier(new System.IdentityModel.Tokens.SecurityKeyIdentifierClause[] 164 | { 165 | securityKeyIdentifierClause 166 | }); 167 | } 168 | 169 | public override string[] GetTokenTypeIdentifiers() 170 | { 171 | return new string[] 172 | { 173 | "http://oauth.net/grant_type/jwt/1.0/bearer" 174 | }; 175 | } 176 | 177 | private bool IsJsonWebSecurityToken(System.Xml.XmlReader reader) 178 | { 179 | return reader.IsStartElement("BinarySecurityToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd") && reader.GetAttribute("ValueType", null) == "http://oauth.net/grant_type/jwt/1.0/bearer"; 180 | } 181 | 182 | private bool IsJsonWebSecurityToken(string token) 183 | { 184 | return System.Text.RegularExpressions.Regex.IsMatch(token, "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$"); 185 | } 186 | 187 | private JsonWebSecurityToken ReadActor(System.Collections.Generic.IDictionary payload) 188 | { 189 | if (!this.JsonWebSecurityTokenRequirement.AllowActorToken) 190 | { 191 | return null; 192 | } 193 | JsonWebSecurityToken result = null; 194 | string text; 195 | payload.TryGetValue("actortoken", out text); 196 | if (!string.IsNullOrEmpty(text)) 197 | { 198 | result = (this.ReadTokenCore(text, true) as JsonWebSecurityToken); 199 | payload.Remove("actortoken"); 200 | } 201 | return result; 202 | } 203 | 204 | public override System.IdentityModel.Tokens.SecurityToken ReadToken(System.Xml.XmlReader reader) 205 | { 206 | if (!this.CanReadToken(reader)) 207 | { 208 | throw new System.Xml.XmlException("Unsupported security token."); 209 | } 210 | string id = null; 211 | string jsonTokenString = this.GetJsonTokenString(reader, out id); 212 | JsonWebSecurityToken jsonWebSecurityToken = this.ReadToken(jsonTokenString) as JsonWebSecurityToken; 213 | if (jsonWebSecurityToken != null) 214 | { 215 | jsonWebSecurityToken.SetId(id); 216 | } 217 | return jsonWebSecurityToken; 218 | } 219 | 220 | internal string GetJsonTokenString(System.Xml.XmlReader reader, out string wsuId) 221 | { 222 | reader.MoveToContent(); 223 | string @string; 224 | using (XmlDictionaryReader xmlDictionaryReader = XmlDictionaryReader.CreateDictionaryReader(reader)) 225 | { 226 | wsuId = xmlDictionaryReader.GetAttribute("Id", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); 227 | string attribute = xmlDictionaryReader.GetAttribute("EncodingType", null); 228 | if (attribute != null && !(attribute == "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary")) 229 | { 230 | throw new System.Xml.XmlException(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Unsupported encoding type: {0}", new object[] 231 | { 232 | attribute 233 | })); 234 | } 235 | byte[] bytes = xmlDictionaryReader.ReadElementContentAsBase64(); 236 | @string = Base64UrlEncoder.TextEncoding.GetString(bytes); 237 | } 238 | return @string; 239 | } 240 | 241 | public virtual System.IdentityModel.Tokens.SecurityToken ReadToken(string token) 242 | { 243 | return this.ReadTokenCore(token, false); 244 | } 245 | 246 | private System.IdentityModel.Tokens.SecurityToken ReadTokenCore(string token, bool isActorToken) 247 | { 248 | Utility.VerifyNonNullOrEmptyStringArgument("token", token); 249 | if (base.Configuration == null) 250 | { 251 | throw new System.InvalidOperationException("No configuration"); 252 | } 253 | if (base.Configuration.IssuerTokenResolver == null) 254 | { 255 | throw new System.InvalidOperationException("No configured IssuerTokenResolver"); 256 | } 257 | if (!this.CanReadToken(token)) 258 | { 259 | throw new System.IdentityModel.Tokens.SecurityTokenException("Unsupported security token."); 260 | } 261 | string[] array = token.Split(new char[] 262 | { 263 | '.' 264 | }); 265 | string text = array[0]; 266 | string text2 = array[1]; 267 | string text3 = array[2]; 268 | System.Collections.Generic.Dictionary dictionary = new System.Collections.Generic.Dictionary(System.StringComparer.Ordinal); 269 | dictionary.DecodeFromJson(Base64UrlEncoder.Decode(text)); 270 | System.Collections.Generic.Dictionary dictionary2 = new System.Collections.Generic.Dictionary(System.StringComparer.Ordinal); 271 | dictionary2.DecodeFromJson(Base64UrlEncoder.Decode(text2)); 272 | string text4; 273 | dictionary.TryGetValue("alg", out text4); 274 | System.IdentityModel.Tokens.SecurityToken issuerToken = null; 275 | if (!System.StringComparer.Ordinal.Equals(text4, "none")) 276 | { 277 | if (string.IsNullOrEmpty(text3)) 278 | { 279 | throw new System.IdentityModel.Tokens.SecurityTokenException("Missing signature."); 280 | } 281 | System.IdentityModel.Tokens.SecurityKeyIdentifier signingKeyIdentifier = this.GetSigningKeyIdentifier(dictionary, dictionary2); 282 | System.IdentityModel.Tokens.SecurityToken securityToken; 283 | base.Configuration.IssuerTokenResolver.TryResolveToken(signingKeyIdentifier, out securityToken); 284 | if (securityToken == null) 285 | { 286 | throw new System.IdentityModel.Tokens.SecurityTokenException("Invalid JWT token. Could not resolve issuer token."); 287 | } 288 | issuerToken = this.VerifySignature(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}.{1}", new object[] 289 | { 290 | text, 291 | text2 292 | }), text3, text4, securityToken); 293 | } 294 | JsonWebSecurityToken actorToken = null; 295 | if (!isActorToken) 296 | { 297 | actorToken = this.ReadActor(dictionary2); 298 | } 299 | string text5; 300 | dictionary2.TryGetValue("iss", out text5); 301 | if (string.IsNullOrEmpty(text5)) 302 | { 303 | throw new System.IdentityModel.Tokens.SecurityTokenValidationException("The token being parsed does not have an issuer."); 304 | } 305 | string text6; 306 | dictionary2.TryGetValue("aud", out text6); 307 | if (string.IsNullOrEmpty(text6)) 308 | { 309 | throw new System.IdentityModel.Tokens.SecurityTokenValidationException("The token being parsed does not have an audience."); 310 | } 311 | string text7; 312 | dictionary2.TryGetValue("nbf", out text7); 313 | if (string.IsNullOrEmpty(text7)) 314 | { 315 | throw new System.IdentityModel.Tokens.SecurityTokenValidationException("The token being parsed does not have an 'not before' claim."); 316 | } 317 | System.DateTime dateTimeFromSeconds = this.GetDateTimeFromSeconds(text7); 318 | text7 = ""; 319 | dictionary2.TryGetValue("exp", out text7); 320 | if (string.IsNullOrEmpty(text7)) 321 | { 322 | throw new System.IdentityModel.Tokens.SecurityTokenValidationException("The token being parsed does not have an 'expires at' claim."); 323 | } 324 | System.DateTime dateTimeFromSeconds2 = this.GetDateTimeFromSeconds(text7); 325 | JsonWebSecurityToken jsonWebSecurityToken = new JsonWebSecurityToken(text5, text6, dateTimeFromSeconds, dateTimeFromSeconds2, this.CreateClaims(dictionary2), issuerToken, actorToken); 326 | jsonWebSecurityToken.CaptureSourceData(token); 327 | return jsonWebSecurityToken; 328 | } 329 | 330 | protected virtual string Sign(string signingInput, System.IdentityModel.Tokens.SigningCredentials signingCredentials) 331 | { 332 | if (signingCredentials == null) 333 | { 334 | return string.Empty; 335 | } 336 | string result; 337 | using (SignatureProvider signatureProvider = SignatureProvider.Create(signingCredentials)) 338 | { 339 | result = Base64UrlEncoder.Encode(signatureProvider.Sign(Base64UrlEncoder.TextEncoding.GetBytes(signingInput))); 340 | } 341 | return result; 342 | } 343 | 344 | protected virtual ClaimsIdentityCollection ValidateActorToken(JsonWebSecurityToken actorToken) 345 | { 346 | return this.ValidateTokenCore(actorToken, true); 347 | } 348 | 349 | protected virtual void ValidateAudience(JsonWebSecurityToken token) 350 | { 351 | if (base.Configuration.AudienceRestriction.AudienceMode == System.IdentityModel.Selectors.AudienceUriMode.Always || base.Configuration.AudienceRestriction.AudienceMode == System.IdentityModel.Selectors.AudienceUriMode.BearerKeyOnly) 352 | { 353 | if (string.IsNullOrEmpty(token.Audience)) 354 | { 355 | throw new Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException("Audience URI validation failed. Token audience must be specified."); 356 | } 357 | AudienceValidator.ValidateAudiences(base.Configuration.AudienceRestriction.AllowedAudienceUris, new System.Uri[] 358 | { 359 | new System.Uri(token.Audience, System.UriKind.RelativeOrAbsolute) 360 | }); 361 | } 362 | } 363 | 364 | protected virtual void ValidateLifetime(JsonWebSecurityToken token) 365 | { 366 | System.TimeSpan maxClockSkew = base.Configuration.MaxClockSkew; 367 | System.DateTime utcNow = System.DateTime.UtcNow; 368 | if (maxClockSkew < System.TimeSpan.Zero) 369 | { 370 | throw new System.InvalidOperationException("No valid ClockSkew configured."); 371 | } 372 | if (token.ValidTo < utcNow - maxClockSkew) 373 | { 374 | throw new Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException("Invalid JWT token. The token is expired."); 375 | } 376 | if (token.ValidFrom > utcNow + maxClockSkew) 377 | { 378 | throw new Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException(string.Concat(new object[] 379 | { 380 | "Invalid JWT token. The token is not yet valid. Current time is ", 381 | utcNow, 382 | " and the token is Valid from ", 383 | token.ValidFrom, 384 | "." 385 | })); 386 | } 387 | } 388 | 389 | private ClaimsIdentityCollection ValidateTokenCore(System.IdentityModel.Tokens.SecurityToken token, bool isActorToken) 390 | { 391 | JsonWebSecurityToken jsonWebSecurityToken = token as JsonWebSecurityToken; 392 | if (jsonWebSecurityToken == null) 393 | { 394 | return base.ValidateToken(token); 395 | } 396 | if (base.Configuration == null) 397 | { 398 | throw new System.InvalidOperationException("No configuration."); 399 | } 400 | if (base.Configuration.IssuerNameRegistry == null) 401 | { 402 | throw new System.InvalidOperationException("No issuername registry configured."); 403 | } 404 | this.ValidateLifetime(jsonWebSecurityToken); 405 | this.ValidateAudience(jsonWebSecurityToken); 406 | System.IdentityModel.Tokens.X509SecurityToken x509SecurityToken = jsonWebSecurityToken.IssuerToken as System.IdentityModel.Tokens.X509SecurityToken; 407 | if (x509SecurityToken != null) 408 | { 409 | base.Configuration.CertificateValidator.Validate(x509SecurityToken.Certificate); 410 | } 411 | ClaimsIdentityCollection claimsIdentityCollection = new ClaimsIdentityCollection(); 412 | ClaimsIdentity claimsIdentity = new ClaimsIdentity("Federation"); 413 | if (!isActorToken && jsonWebSecurityToken.ActorToken != null) 414 | { 415 | ClaimsIdentityCollection claimsIdentityCollection2 = this.ValidateActorToken(jsonWebSecurityToken.ActorToken); 416 | if (claimsIdentityCollection2.Count > 1) 417 | { 418 | throw new System.IdentityModel.Tokens.SecurityTokenException("Invalid JWT token. Actor has multiple identities."); 419 | } 420 | claimsIdentity.Actor = claimsIdentityCollection2[0]; 421 | } 422 | string issuerName = this.GetIssuerName(jsonWebSecurityToken); 423 | foreach (JsonWebTokenClaim current in jsonWebSecurityToken.Claims) 424 | { 425 | if (claimsIdentity.Actor == null || !System.StringComparer.Ordinal.Equals("actortoken", current.ClaimType)) 426 | { 427 | string text = current.Value; 428 | if (text == null) 429 | { 430 | text = "NULL"; 431 | } 432 | claimsIdentity.Claims.Add(new Claim(current.ClaimType, text, "http://www.w3.org/2001/XMLSchema#string", issuerName)); 433 | } 434 | } 435 | if (!isActorToken && base.Configuration.SaveBootstrapTokens) 436 | { 437 | claimsIdentity.BootstrapToken = token; 438 | } 439 | claimsIdentityCollection.Add(claimsIdentity); 440 | return claimsIdentityCollection; 441 | } 442 | 443 | public override ClaimsIdentityCollection ValidateToken(System.IdentityModel.Tokens.SecurityToken token) 444 | { 445 | return this.ValidateTokenCore(token, false); 446 | } 447 | 448 | protected virtual System.IdentityModel.Tokens.SecurityToken VerifySignature(string signingInput, string signature, string algorithm, System.IdentityModel.Tokens.SecurityToken signingToken) 449 | { 450 | Utility.VerifyNonNullArgument("signingToken", signingToken); 451 | bool flag = false; 452 | System.IdentityModel.Tokens.SecurityToken result = null; 453 | if (string.Equals(algorithm, "RS256", System.StringComparison.Ordinal)) 454 | { 455 | System.IdentityModel.Tokens.X509SecurityToken x509SecurityToken = signingToken as System.IdentityModel.Tokens.X509SecurityToken; 456 | if (x509SecurityToken == null) 457 | { 458 | throw new System.IdentityModel.Tokens.SecurityTokenException("Unsupported issuer token type for asymmetric signature."); 459 | } 460 | System.Security.Cryptography.RSACryptoServiceProvider rSACryptoServiceProvider = x509SecurityToken.Certificate.PublicKey.Key as System.Security.Cryptography.RSACryptoServiceProvider; 461 | if (rSACryptoServiceProvider == null) 462 | { 463 | throw new System.IdentityModel.Tokens.SecurityTokenException("Unsupported asymmetric signing algorithm."); 464 | } 465 | using (X509AsymmetricSignatureProvider x509AsymmetricSignatureProvider = new X509AsymmetricSignatureProvider(rSACryptoServiceProvider)) 466 | { 467 | flag = x509AsymmetricSignatureProvider.Verify(Base64UrlEncoder.TextEncoding.GetBytes(signingInput), Base64UrlEncoder.DecodeBytes(signature)); 468 | if (flag) 469 | { 470 | result = signingToken; 471 | } 472 | goto IL_133; 473 | } 474 | } 475 | if (string.Equals(algorithm, "HS256", System.StringComparison.Ordinal)) 476 | { 477 | byte[] bytes = Base64UrlEncoder.TextEncoding.GetBytes(signingInput); 478 | byte[] signature2 = Base64UrlEncoder.DecodeBytes(signature); 479 | using (System.Collections.Generic.IEnumerator enumerator = signingToken.SecurityKeys.GetEnumerator()) 480 | { 481 | while (enumerator.MoveNext()) 482 | { 483 | System.IdentityModel.Tokens.SecurityKey current = enumerator.Current; 484 | System.IdentityModel.Tokens.SymmetricSecurityKey symmetricSecurityKey = current as System.IdentityModel.Tokens.SymmetricSecurityKey; 485 | if (symmetricSecurityKey != null) 486 | { 487 | using (SymmetricSignatureProvider symmetricSignatureProvider = new SymmetricSignatureProvider(symmetricSecurityKey)) 488 | { 489 | flag = symmetricSignatureProvider.Verify(bytes, signature2); 490 | if (flag) 491 | { 492 | result = new BinarySecretSecurityToken(symmetricSecurityKey.GetSymmetricKey()); 493 | break; 494 | } 495 | } 496 | } 497 | } 498 | goto IL_133; 499 | } 500 | } 501 | throw new System.IdentityModel.Tokens.SecurityTokenException("Unsupported signing algorithm."); 502 | IL_133: 503 | if (!flag) 504 | { 505 | throw new System.IdentityModel.Tokens.SecurityTokenException("Invalid issuer or signature."); 506 | } 507 | return result; 508 | } 509 | 510 | public override void WriteToken(System.Xml.XmlWriter writer, System.IdentityModel.Tokens.SecurityToken token) 511 | { 512 | if (!(token is JsonWebSecurityToken)) 513 | { 514 | base.WriteToken(writer, token); 515 | } 516 | Utility.VerifyNonNullArgument("writer", writer); 517 | byte[] bytes = Base64UrlEncoder.TextEncoding.GetBytes(this.WriteTokenAsString(token)); 518 | writer.WriteStartElement("wsse", "BinarySecurityToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); 519 | if (token.Id != null) 520 | { 521 | writer.WriteAttributeString("wsu", "Id", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", token.Id); 522 | } 523 | writer.WriteAttributeString("ValueType", null, "http://oauth.net/grant_type/jwt/1.0/bearer"); 524 | writer.WriteAttributeString("EncodingType", null, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"); 525 | writer.WriteBase64(bytes, 0, bytes.Length); 526 | writer.WriteEndElement(); 527 | } 528 | 529 | public virtual string WriteTokenAsString(System.IdentityModel.Tokens.SecurityToken token) 530 | { 531 | Utility.VerifyNonNullArgument("token", token); 532 | JsonWebSecurityToken jsonWebSecurityToken = token as JsonWebSecurityToken; 533 | if (jsonWebSecurityToken == null) 534 | { 535 | throw new System.ArgumentException("Unsupported token type", "token"); 536 | } 537 | if (jsonWebSecurityToken.CanWriteSourceData) 538 | { 539 | return jsonWebSecurityToken.WriteSourceData(); 540 | } 541 | System.Collections.Generic.IDictionary self = jsonWebSecurityToken.CreateHeaderClaims(); 542 | System.Collections.Generic.IDictionary self2 = jsonWebSecurityToken.CreatePayloadClaims(); 543 | string text = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}.{1}", new object[] 544 | { 545 | Base64UrlEncoder.Encode(self.EncodeToJson()), 546 | Base64UrlEncoder.Encode(self2.EncodeToJson()) 547 | }); 548 | string text2 = this.Sign(text, jsonWebSecurityToken.SigningCredentials); 549 | return string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}.{1}", new object[] 550 | { 551 | text, 552 | text2 553 | }); 554 | } 555 | 556 | private System.DateTime GetDateTimeFromSeconds(string seconds) 557 | { 558 | long secondsSinceUnixEpoch = System.Convert.ToInt64(seconds); 559 | return new EpochTime(secondsSinceUnixEpoch).DateTime; 560 | } 561 | 562 | private System.Collections.Generic.IEnumerable CreateClaims(System.Collections.Generic.IDictionary payloadClaims) 563 | { 564 | System.Collections.Generic.List list = new System.Collections.Generic.List(); 565 | foreach (string current in payloadClaims.Keys) 566 | { 567 | if (!this.IsReservedClaimType(current)) 568 | { 569 | list.Add(new JsonWebTokenClaim(current, payloadClaims[current])); 570 | } 571 | } 572 | return list; 573 | } 574 | 575 | protected virtual bool IsReservedClaimType(string claimType) 576 | { 577 | return System.StringComparer.OrdinalIgnoreCase.Equals(claimType, "aud") || System.StringComparer.OrdinalIgnoreCase.Equals(claimType, "iss") || System.StringComparer.OrdinalIgnoreCase.Equals(claimType, "nbf") || System.StringComparer.OrdinalIgnoreCase.Equals(claimType, "exp"); 578 | } 579 | } 580 | } 581 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/JsonWebSecurityTokenRequirement.cs: -------------------------------------------------------------------------------- 1 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 2 | { 3 | public class JsonWebSecurityTokenRequirement 4 | { 5 | public bool AllowActorToken 6 | { 7 | get; 8 | set; 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/JsonWebTokenClaim.cs: -------------------------------------------------------------------------------- 1 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 2 | { 3 | public class JsonWebTokenClaim 4 | { 5 | private string _claimType; 6 | 7 | private string _value; 8 | 9 | public string ClaimType 10 | { 11 | get 12 | { 13 | return this._claimType; 14 | } 15 | } 16 | 17 | public string Value 18 | { 19 | get 20 | { 21 | return this._value; 22 | } 23 | } 24 | 25 | public JsonWebTokenClaim(string claimType, string value) 26 | { 27 | Utility.VerifyNonNullOrEmptyStringArgument("claimType", claimType); 28 | this._claimType = claimType; 29 | this._value = value; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/JsonWebTokenConstants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 8 | { 9 | public static class JsonWebTokenConstants 10 | { 11 | [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential, Size = 1)] 12 | public struct Algorithms 13 | { 14 | public const string HMAC_SHA256 = "HS256"; 15 | 16 | public const string HMAC_SHA384 = "HS384"; 17 | 18 | public const string HMAC_SHA512 = "HS512"; 19 | 20 | public const string RSA_SHA256 = "RS256"; 21 | 22 | public const string RSA_SHA384 = "RS384"; 23 | 24 | public const string RSA_SHA512 = "RS512"; 25 | 26 | public const string ECDSA_SHA256 = "ES256"; 27 | 28 | public const string ECDSA_SHA384 = "ES384"; 29 | 30 | public const string ECDSA_SHA512 = "ES512"; 31 | 32 | public const string NONE = "none"; 33 | } 34 | 35 | [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential, Size = 1)] 36 | public struct ReservedClaims 37 | { 38 | public const string Actor = "actor"; 39 | 40 | public const string ActorToken = "actortoken"; 41 | 42 | public const string AppContext = "appctx"; 43 | 44 | public const string Audience = "aud"; 45 | 46 | public const string ExpiresOn = "exp"; 47 | 48 | public const string IdentityProvider = "identityprovider"; 49 | 50 | public const string IssuedAt = "iat"; 51 | 52 | public const string Issuer = "iss"; 53 | 54 | public const string NameIdentifier = "nameid"; 55 | 56 | public const string NotBefore = "nbf"; 57 | } 58 | 59 | [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential, Size = 1)] 60 | public struct ReservedHeaderParameters 61 | { 62 | public const string Algorithm = "alg"; 63 | 64 | public const string Type = "typ"; 65 | 66 | public const string X509CertificateThumbprint = "x5t"; 67 | } 68 | 69 | public const string HeaderType = "JWT"; 70 | 71 | public const string TokenType = "http://oauth.net/grant_type/jwt/1.0/bearer"; 72 | 73 | public static readonly System.DateTime BaseTime = EpochTime.UnixEpoch; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 7 | { 8 | internal static class NativeMethods 9 | { 10 | private const string BCRYPT = "bcrypt.dll"; 11 | 12 | [System.Runtime.InteropServices.DllImport("bcrypt.dll", SetLastError = true)] 13 | public static extern int BCryptGetFipsAlgorithmMode([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.U1)] out bool pfEnabled); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/RSACryptoServiceProviderProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 4 | { 5 | internal sealed class RSACryptoServiceProviderProxy : System.IDisposable 6 | { 7 | private const int PROV_RSA_AES = 24; 8 | 9 | private bool _disposed; 10 | 11 | private bool _disposeRsa; 12 | 13 | private System.Security.Cryptography.RSACryptoServiceProvider _rsa; 14 | 15 | public RSACryptoServiceProviderProxy(System.Security.Cryptography.RSACryptoServiceProvider rsa) 16 | { 17 | Utility.VerifyNonNullArgument("rsa", rsa); 18 | if (rsa.CspKeyContainerInfo.ProviderType != 24) 19 | { 20 | System.Security.Cryptography.CspParameters cspParameters = new System.Security.Cryptography.CspParameters(); 21 | cspParameters.ProviderType = 24; 22 | cspParameters.KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName; 23 | cspParameters.KeyNumber = (int)rsa.CspKeyContainerInfo.KeyNumber; 24 | if (rsa.CspKeyContainerInfo.MachineKeyStore) 25 | { 26 | cspParameters.Flags = System.Security.Cryptography.CspProviderFlags.UseMachineKeyStore; 27 | } 28 | cspParameters.Flags |= System.Security.Cryptography.CspProviderFlags.UseExistingKey; 29 | this._rsa = new System.Security.Cryptography.RSACryptoServiceProvider(cspParameters); 30 | this._disposeRsa = true; 31 | return; 32 | } 33 | this._rsa = rsa; 34 | } 35 | 36 | private void Dispose(bool disposing) 37 | { 38 | if (!this._disposed) 39 | { 40 | if (disposing && this._disposeRsa && this._rsa != null) 41 | { 42 | this._rsa.Dispose(); 43 | this._rsa = null; 44 | } 45 | this._disposed = true; 46 | } 47 | } 48 | 49 | public byte[] SignData(byte[] signingInput, object hash) 50 | { 51 | return this._rsa.SignData(signingInput, hash); 52 | } 53 | 54 | public bool VerifyData(byte[] signingInput, object hash, byte[] signature) 55 | { 56 | return this._rsa.VerifyData(signingInput, hash, signature); 57 | } 58 | 59 | public void Dispose() 60 | { 61 | this.Dispose(true); 62 | System.GC.SuppressFinalize(this); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/SignatureProvider.cs: -------------------------------------------------------------------------------- 1 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 2 | { 3 | internal abstract class SignatureProvider : System.IDisposable 4 | { 5 | public static SignatureProvider Create(System.IdentityModel.Tokens.SigningCredentials signingCredentials) 6 | { 7 | Utility.VerifyNonNullArgument("signingCredentials", signingCredentials); 8 | if (System.StringComparer.Ordinal.Compare(signingCredentials.DigestAlgorithm, "http://www.w3.org/2001/04/xmlenc#sha256") != 0) 9 | { 10 | throw new System.ArgumentException("signingCredentials.DigestAlgorithm must be SHA-256"); 11 | } 12 | System.IdentityModel.Tokens.X509AsymmetricSecurityKey x509AsymmetricSecurityKey = signingCredentials.SigningKey as System.IdentityModel.Tokens.X509AsymmetricSecurityKey; 13 | if (x509AsymmetricSecurityKey != null) 14 | { 15 | return new X509AsymmetricSignatureProvider(x509AsymmetricSecurityKey); 16 | } 17 | System.IdentityModel.Tokens.SymmetricSecurityKey symmetricSecurityKey = signingCredentials.SigningKey as System.IdentityModel.Tokens.SymmetricSecurityKey; 18 | if (symmetricSecurityKey != null) 19 | { 20 | return new SymmetricSignatureProvider(symmetricSecurityKey); 21 | } 22 | throw new System.ArgumentException("signingCredentials.SigningKey must be either X509AsymmetricSecurityKey or SymmetricSecurityKey"); 23 | } 24 | 25 | protected virtual void Dispose(bool disposing) 26 | { 27 | } 28 | 29 | public abstract byte[] Sign(byte[] signingInput); 30 | 31 | public abstract bool Verify(byte[] signingInput, byte[] signature); 32 | 33 | public void Dispose() 34 | { 35 | this.Dispose(true); 36 | System.GC.SuppressFinalize(this); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/SimpleSymmetricKeySecurityToken.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.IdentityModel; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 8 | { 9 | public class SimpleSymmetricKeySecurityToken : System.IdentityModel.Tokens.SecurityToken 10 | { 11 | private string id; 12 | 13 | private System.DateTime effectiveTime; 14 | 15 | private byte[] key; 16 | 17 | private System.Collections.ObjectModel.ReadOnlyCollection securityKeys; 18 | 19 | public override string Id 20 | { 21 | get 22 | { 23 | return this.id; 24 | } 25 | } 26 | 27 | public override System.DateTime ValidFrom 28 | { 29 | get 30 | { 31 | return this.effectiveTime; 32 | } 33 | } 34 | 35 | public override System.DateTime ValidTo 36 | { 37 | get 38 | { 39 | return System.DateTime.MaxValue; 40 | } 41 | } 42 | 43 | public int KeySize 44 | { 45 | get 46 | { 47 | return this.key.Length * 8; 48 | } 49 | } 50 | 51 | public override System.Collections.ObjectModel.ReadOnlyCollection SecurityKeys 52 | { 53 | get 54 | { 55 | return this.securityKeys; 56 | } 57 | } 58 | 59 | public SimpleSymmetricKeySecurityToken(byte[] key) : this(UniqueId.CreateUniqueId(), key) 60 | { 61 | } 62 | 63 | public SimpleSymmetricKeySecurityToken(string id, byte[] key) 64 | { 65 | Utility.VerifyNonNullOrEmptyStringArgument("id", id); 66 | Utility.VerifyNonNullArgument("key", key); 67 | if (key.Length <= 0) 68 | { 69 | throw new System.ArgumentException("The key length must be greater then zero."); 70 | } 71 | this.id = id; 72 | this.effectiveTime = System.DateTime.UtcNow; 73 | this.key = new byte[key.Length]; 74 | System.Buffer.BlockCopy(key, 0, this.key, 0, key.Length); 75 | this.securityKeys = this.CreateSymmetricSecurityKeys(this.key); 76 | } 77 | 78 | public byte[] GetKeyBytes() 79 | { 80 | int num = this.key.Length; 81 | byte[] array = new byte[num]; 82 | System.Buffer.BlockCopy(this.key, 0, array, 0, num); 83 | return array; 84 | } 85 | 86 | private System.Collections.ObjectModel.ReadOnlyCollection CreateSymmetricSecurityKeys(byte[] key) 87 | { 88 | return new System.Collections.ObjectModel.ReadOnlyCollection(new System.Collections.Generic.List 89 | { 90 | new System.IdentityModel.Tokens.InMemorySymmetricSecurityKey(key) 91 | }); 92 | } 93 | 94 | public override bool MatchesKeyIdentifierClause(System.IdentityModel.Tokens.SecurityKeyIdentifierClause keyIdentifierClause) 95 | { 96 | Utility.VerifyNonNullArgument("keyIdentifierClause", keyIdentifierClause); 97 | return keyIdentifierClause is SymmetricIssuerKeyIdentifierClause || base.MatchesKeyIdentifierClause(keyIdentifierClause); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/SymmetricIssuerKeyIdentifierClause.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 7 | { 8 | public class SymmetricIssuerKeyIdentifierClause : System.IdentityModel.Tokens.SecurityKeyIdentifierClause 9 | { 10 | private const string SymmetricIssuerClauseType = "SymmetricIssuer"; 11 | 12 | private string _issuer; 13 | 14 | public string Issuer 15 | { 16 | get 17 | { 18 | return this._issuer; 19 | } 20 | } 21 | 22 | public SymmetricIssuerKeyIdentifierClause(string issuer) : base("SymmetricIssuer") 23 | { 24 | Utility.VerifyNonNullOrEmptyStringArgument("issuer", issuer); 25 | this._issuer = issuer; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/SymmetricKeyIssuerNameRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.ServiceModel.Security.Tokens; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 8 | { 9 | public class SymmetricKeyIssuerNameRegistry : Microsoft.IdentityModel.Tokens.IssuerNameRegistry 10 | { 11 | private System.Collections.Generic.Dictionary _issuerList = new System.Collections.Generic.Dictionary(); 12 | 13 | public void AddTrustedIssuer(byte[] symmetricKey, string issuerName) 14 | { 15 | Utility.VerifyNonNullArgument("symmetricKey", symmetricKey); 16 | Utility.VerifyNonNullOrEmptyStringArgument("issuerName", issuerName); 17 | this._issuerList.Add(System.Convert.ToBase64String(symmetricKey), issuerName); 18 | } 19 | 20 | public override string GetIssuerName(System.IdentityModel.Tokens.SecurityToken securityToken) 21 | { 22 | Utility.VerifyNonNullArgument("securityToken", securityToken); 23 | string result = null; 24 | BinarySecretSecurityToken binarySecretSecurityToken = securityToken as BinarySecretSecurityToken; 25 | if (binarySecretSecurityToken != null) 26 | { 27 | this._issuerList.TryGetValue(System.Convert.ToBase64String(binarySecretSecurityToken.GetKeyBytes()), out result); 28 | } 29 | return result; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/SymmetricSignatureProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 7 | { 8 | internal class SymmetricSignatureProvider : SignatureProvider 9 | { 10 | private bool _disposed; 11 | 12 | private System.Security.Cryptography.HMACSHA256 _hash; 13 | 14 | public SymmetricSignatureProvider(System.IdentityModel.Tokens.SymmetricSecurityKey symmetricKey) 15 | { 16 | Utility.VerifyNonNullArgument("symmetricKey", symmetricKey); 17 | this._hash = new System.Security.Cryptography.HMACSHA256(symmetricKey.GetSymmetricKey()); 18 | } 19 | 20 | protected override void Dispose(bool disposing) 21 | { 22 | if (!this._disposed) 23 | { 24 | if (disposing && this._hash != null) 25 | { 26 | this._hash.Dispose(); 27 | this._hash = null; 28 | } 29 | this._disposed = true; 30 | } 31 | } 32 | 33 | public override byte[] Sign(byte[] signingInput) 34 | { 35 | Utility.VerifyNonNullArgument("signingInput", signingInput); 36 | return this._hash.ComputeHash(signingInput); 37 | } 38 | 39 | public override bool Verify(byte[] signingInput, byte[] signature) 40 | { 41 | Utility.VerifyNonNullArgument("signingInput", signingInput); 42 | Utility.VerifyNonNullArgument("signature", signature); 43 | byte[] b = this._hash.ComputeHash(signingInput); 44 | return this.AreEqual(signature, b); 45 | } 46 | 47 | private bool AreEqual(byte[] a, byte[] b) 48 | { 49 | if (a == null || b == null) 50 | { 51 | return a == null && null == b; 52 | } 53 | if (object.ReferenceEquals(a, b)) 54 | { 55 | return true; 56 | } 57 | if (a.Length != b.Length) 58 | { 59 | return false; 60 | } 61 | for (int i = 0; i < a.Length; i++) 62 | { 63 | if (a[i] != b[i]) 64 | { 65 | return false; 66 | } 67 | } 68 | return true; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/Utility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 7 | { 8 | public static class Utility 9 | { 10 | private const int WindowsVistaMajorNumber = 6; 11 | 12 | internal const int S_OK = 0; 13 | 14 | private const string fipsPolicyRegistryKey = "System\\CurrentControlSet\\Control\\Lsa"; 15 | 16 | private static int fipsAlgorithmPolicy = -1; 17 | 18 | internal static bool RequiresFipsCompliance 19 | { 20 | [System.Security.SecuritySafeCritical] 21 | get 22 | { 23 | if (Utility.fipsAlgorithmPolicy == -1) 24 | { 25 | if (System.Environment.OSVersion.Version.Major >= 6) 26 | { 27 | bool flag2; 28 | bool flag = 0 == NativeMethods.BCryptGetFipsAlgorithmMode(out flag2); 29 | if (flag && flag2) 30 | { 31 | Utility.fipsAlgorithmPolicy = 1; 32 | } 33 | else 34 | { 35 | Utility.fipsAlgorithmPolicy = 0; 36 | } 37 | } 38 | else 39 | { 40 | Utility.fipsAlgorithmPolicy = Utility.GetFipsAlgorithmPolicyKeyFromRegistry(); 41 | if (Utility.fipsAlgorithmPolicy != 1) 42 | { 43 | Utility.fipsAlgorithmPolicy = 0; 44 | } 45 | } 46 | } 47 | return Utility.fipsAlgorithmPolicy == 1; 48 | } 49 | } 50 | 51 | public static void VerifyNonNullArgument(string name, object value) 52 | { 53 | if (value == null) 54 | { 55 | throw new System.ArgumentNullException(name); 56 | } 57 | } 58 | 59 | public static void VerifyNonNullOrEmptyStringArgument(string name, string value) 60 | { 61 | if (string.IsNullOrEmpty(value)) 62 | { 63 | throw new System.ArgumentException(string.Format(System.Globalization.CultureInfo.InvariantCulture, "The parameter '{0}' cannot be a null or empty string", new object[] 64 | { 65 | name 66 | })); 67 | } 68 | } 69 | 70 | [System.Security.SecurityCritical] 71 | [System.Security.Permissions.RegistryPermission(System.Security.Permissions.SecurityAction.Assert, Read = "HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Lsa")] 72 | private static int GetFipsAlgorithmPolicyKeyFromRegistry() 73 | { 74 | int result = -1; 75 | using (Microsoft.Win32.RegistryKey registryKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("System\\CurrentControlSet\\Control\\Lsa", false)) 76 | { 77 | if (registryKey != null) 78 | { 79 | object value = registryKey.GetValue("FIPSAlgorithmPolicy"); 80 | if (value != null) 81 | { 82 | try 83 | { 84 | result = (int)value; 85 | } 86 | catch (System.InvalidCastException) 87 | { 88 | return -1; 89 | } 90 | } 91 | } 92 | } 93 | return result; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Tokens/X509AsymmetricSignatureProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens 4 | { 5 | internal class X509AsymmetricSignatureProvider : SignatureProvider 6 | { 7 | private bool _disposed; 8 | 9 | private RSACryptoServiceProviderProxy _rsaProxy; 10 | 11 | private string _hashAlgorithm; 12 | 13 | public X509AsymmetricSignatureProvider(System.IdentityModel.Tokens.X509AsymmetricSecurityKey x509Key) 14 | { 15 | Utility.VerifyNonNullArgument("x509Key", x509Key); 16 | System.Security.Cryptography.RSACryptoServiceProvider rSACryptoServiceProvider = x509Key.GetAsymmetricAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", true) as System.Security.Cryptography.RSACryptoServiceProvider; 17 | if (rSACryptoServiceProvider == null) 18 | { 19 | throw new System.InvalidOperationException("Could not get algorithm from X509AsymmetricSecurityKey"); 20 | } 21 | this.Initialize(rSACryptoServiceProvider); 22 | } 23 | 24 | public X509AsymmetricSignatureProvider(System.Security.Cryptography.RSACryptoServiceProvider rsa) 25 | { 26 | this.Initialize(rsa); 27 | } 28 | 29 | protected override void Dispose(bool disposing) 30 | { 31 | if (!this._disposed) 32 | { 33 | if (disposing) 34 | { 35 | if (this._hashAlgorithm != null) 36 | { 37 | this._hashAlgorithm = null; 38 | } 39 | if (this._rsaProxy != null) 40 | { 41 | this._rsaProxy.Dispose(); 42 | this._rsaProxy = null; 43 | } 44 | } 45 | this._disposed = true; 46 | } 47 | } 48 | 49 | private void Initialize(System.Security.Cryptography.RSACryptoServiceProvider rsa) 50 | { 51 | if (Utility.RequiresFipsCompliance) 52 | { 53 | System.Security.Cryptography.CryptoConfig.AddOID("2.16.840.1.101.3.4.2.1", new string[] 54 | { 55 | "SHA256CSP" 56 | }); 57 | System.Security.Cryptography.CryptoConfig.AddAlgorithm(typeof(SHA256CryptoServiceProvider), new string[] 58 | { 59 | "SHA256CSP" 60 | }); 61 | this._hashAlgorithm = "SHA256CSP"; 62 | } 63 | else 64 | { 65 | this._hashAlgorithm = "SHA256"; 66 | } 67 | this._rsaProxy = new RSACryptoServiceProviderProxy(rsa); 68 | } 69 | 70 | public override byte[] Sign(byte[] signingInput) 71 | { 72 | Utility.VerifyNonNullArgument("signingInput", signingInput); 73 | return this._rsaProxy.SignData(signingInput, this._hashAlgorithm); 74 | } 75 | 76 | public override bool Verify(byte[] signingInput, byte[] signature) 77 | { 78 | Utility.VerifyNonNullArgument("signingInput", signingInput); 79 | Utility.VerifyNonNullArgument("signature", signature); 80 | return this._rsaProxy.VerifyData(signingInput, this._hashAlgorithm, signature); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/S2S/Web/OAuth2ProtectedResourceUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SharePointPnP.IdentityModel.Extensions.S2S.Web 7 | { 8 | public static class OAuth2ProtectedResourceUtility 9 | { 10 | public static string ReadToken(string authorizationHeader) 11 | { 12 | string text = authorizationHeader.Trim(); 13 | if (text.StartsWith("Bearer", true, System.Globalization.CultureInfo.InvariantCulture)) 14 | { 15 | string[] array = authorizationHeader.Split(new char[] 16 | { 17 | ' ' 18 | }); 19 | if (array.Length == 2 && array[0].Equals("Bearer", System.StringComparison.OrdinalIgnoreCase)) 20 | { 21 | return array[1]; 22 | } 23 | } 24 | return null; 25 | } 26 | 27 | public static string WriteAuthorizationHeader(string token) 28 | { 29 | return string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1}", new object[] 30 | { 31 | "Bearer", 32 | token 33 | }); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SharePointPnP.IdentityModel.Extensions/SharePointPnP.IdentityModel.Extensions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | SharePointPnP.IdentityModel.Extensions 5 | 1.2.1 6 | SharePoint Patterns and Practices 7 | net45 8 | SharePointPnP.IdentityModel.Extensions 9 | SharePointPnP.IdentityModel.Extensions 10 | false 11 | false 12 | false 13 | 1.2.4 14 | 1.2.4.0 15 | 1.2.4.0 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | --------------------------------------------------------------------------------