1 //------------------------------------------------------------ 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //------------------------------------------------------------ 4 5 namespace System.IdentityModel 6 { 7 using System; 8 using System.IdentityModel.Tokens; 9 using System.Security.Claims; 10 using RST = System.IdentityModel.Protocols.WSTrust.RequestSecurityToken; 11 using RSTR = System.IdentityModel.Protocols.WSTrust.RequestSecurityTokenResponse; 12 using System.IdentityModel.Protocols.WSTrust; 13 using System.IdentityModel.Configuration; 14 15 /// <summary> 16 /// Abstract class for building WS-Security token services. 17 /// </summary> 18 public abstract class SecurityTokenService 19 { 20 /// <summary> 21 /// This class is used to maintain request state across asynchronous calls 22 /// within the security token service. 23 /// </summary> 24 protected class FederatedAsyncState 25 { 26 RST _request; 27 ClaimsPrincipal _claimsPrincipal; 28 SecurityTokenHandler _securityTokenHandler; 29 IAsyncResult _result; 30 31 /// <summary> 32 /// Copy constructor. 33 /// </summary> 34 /// <param name="federatedAsyncState">The input FederatedAsyncState instance.</param> 35 /// <exception cref="ArgumentNullException">The input 'FederatedAsyncState' is null.</exception> FederatedAsyncState(FederatedAsyncState federatedAsyncState)36 public FederatedAsyncState(FederatedAsyncState federatedAsyncState) 37 { 38 if (null == federatedAsyncState) 39 { 40 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("FederatedAsyncState"); 41 } 42 43 _request = federatedAsyncState.Request; 44 _claimsPrincipal = federatedAsyncState.ClaimsPrincipal; 45 _securityTokenHandler = federatedAsyncState.SecurityTokenHandler; 46 _result = federatedAsyncState.Result; 47 } 48 49 /// <summary> 50 /// Constructs a FederatedAsyncState instance with token request, principal, and the async result. 51 /// </summary> 52 /// <param name="request">The token request instance.</param> 53 /// <param name="principal">The identity of the token requestor.</param> 54 /// <param name="result">The async result.</param> 55 /// <exception cref="ArgumentNullException">When the given request or async result is null.</exception> FederatedAsyncState(RST request, ClaimsPrincipal principal, IAsyncResult result)56 public FederatedAsyncState(RST request, ClaimsPrincipal principal, IAsyncResult result) 57 { 58 if (null == request) 59 { 60 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); 61 } 62 63 if (null == result) 64 { 65 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result"); 66 } 67 68 _request = request; 69 _claimsPrincipal = principal; 70 _result = result; 71 } 72 73 /// <summary> 74 /// Gets the token request instance. 75 /// </summary> 76 public RST Request 77 { 78 get 79 { 80 return _request; 81 } 82 } 83 84 /// <summary> 85 /// Gets the ClaimsPrincipal instance. 86 /// </summary> 87 public ClaimsPrincipal ClaimsPrincipal 88 { 89 get 90 { 91 return _claimsPrincipal; 92 } 93 } 94 95 /// <summary> 96 /// Gets or sets the SecurityTokenHandler to be used during an async token-issuance call. 97 /// </summary> 98 public SecurityTokenHandler SecurityTokenHandler 99 { 100 get { return _securityTokenHandler; } 101 set { _securityTokenHandler = value; } 102 } 103 104 /// <summary> 105 /// Gets the async result. 106 /// </summary> 107 public IAsyncResult Result 108 { 109 get 110 { 111 return _result; 112 } 113 } 114 } 115 116 // 117 // STS settings 118 // 119 SecurityTokenServiceConfiguration _securityTokenServiceConfiguration; 120 121 ClaimsPrincipal _principal; 122 RequestSecurityToken _request; 123 SecurityTokenDescriptor _tokenDescriptor; 124 125 /// <summary> 126 /// Use this constructor to initialize scope provider and token issuer certificate. 127 /// </summary> 128 /// <param name="securityTokenServiceConfiguration">The SecurityTokenServiceConfiguration that will have the related settings for the STS.</param> SecurityTokenService(SecurityTokenServiceConfiguration securityTokenServiceConfiguration)129 protected SecurityTokenService(SecurityTokenServiceConfiguration securityTokenServiceConfiguration) 130 { 131 if (securityTokenServiceConfiguration == null) 132 { 133 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityTokenServiceConfiguration"); 134 } 135 136 _securityTokenServiceConfiguration = securityTokenServiceConfiguration; 137 } 138 139 /// <summary> 140 /// Async Cancel. 141 /// </summary> 142 /// <param name="principal">The identity of the token requestor.</param> 143 /// <param name="request">The security token request which includes request message as well as other client 144 /// related information such as authorization context.</param> 145 /// <param name="callback">The async call back.</param> 146 /// <param name="state">The state object.</param> 147 /// <returns>The async result.</returns> BeginCancel(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state)148 public virtual IAsyncResult BeginCancel(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state) 149 { 150 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null ? request.RequestType : "Cancel")))); 151 } 152 153 /// <summary> 154 /// Begins async call for GetScope routine. Default implementation will throw a NotImplementedExcetion. 155 /// Refer MSDN articles on Using an AsyncCallback Delegate to End an Asynchronous Operation. 156 /// </summary> 157 /// <param name="principal">The identity of the token requestor.</param> 158 /// <param name="request">The request.</param> 159 /// <param name="callback">The callback to be invoked when the user Asynchronous operation completed.</param> 160 /// <param name="state">The state object.</param> 161 /// <returns>IAsyncResult. Represents the status of an asynchronous operation. This will be passed into 162 /// EndGetScope.</returns> BeginGetScope(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state)163 protected virtual IAsyncResult BeginGetScope(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state) 164 { 165 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException(SR.GetString(SR.ID2081))); 166 } 167 168 /// <summary> 169 /// Begins the async call of the Issue request. 170 /// </summary> 171 /// <param name="principal">The <see cref="ClaimsPrincipal"/> to issue a token for.</param> 172 /// <param name="request">The security token request which includes request message as well as other client 173 /// related information such as authorization context.</param> 174 /// <param name="callback">The async call back.</param> 175 /// <param name="state">The state object.</param> 176 /// <returns>The async result.</returns> BeginIssue(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state)177 public virtual IAsyncResult BeginIssue(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state) 178 { 179 if (request == null) 180 { 181 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); 182 } 183 _principal = principal; 184 _request = request; 185 186 // 187 // Step 1: Validate the rst: check if this STS is capable of handling this 188 // rst 189 // 190 ValidateRequest(request); 191 192 // 193 // 194 195 196 197 FederatedAsyncState asyncState = new FederatedAsyncState(request, principal, new TypedAsyncResult<RSTR>(callback, state)); 198 199 BeginGetScope(principal, request, OnGetScopeComplete, asyncState); 200 201 return asyncState.Result; 202 } 203 204 /// <summary> 205 /// Async Renew. 206 /// </summary> 207 /// <param name="principal">The <see cref="ClaimsPrincipal"/> to renew.</param> 208 /// <param name="request">The security token request which includes request message as well as other client 209 /// related information such as authorization context.</param> 210 /// <param name="callback">The async call back.</param> 211 /// <param name="state">The state object.</param> 212 /// <returns>The async result.</returns> BeginRenew(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state)213 public virtual IAsyncResult BeginRenew(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state) 214 { 215 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null && request.RequestType != null ? request.RequestType : "Renew")))); 216 } 217 218 /// <summary> 219 /// Async Validate. 220 /// </summary> 221 /// <param name="principal">The <see cref="ClaimsPrincipal"/> to validate.</param> 222 /// <param name="request">The security token request which includes request message as well as other client 223 /// related information such as authorization context.</param> 224 /// <param name="callback">The async call back.</param> 225 /// <param name="state">The state object.</param> 226 /// <returns>The async result.</returns> BeginValidate(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state)227 public virtual IAsyncResult BeginValidate(ClaimsPrincipal principal, RST request, AsyncCallback callback, object state) 228 { 229 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null && request.RequestType != null ? request.RequestType : "Validate")))); 230 } 231 232 /// <summary> 233 /// Cancel. 234 /// </summary> 235 /// <param name="principal">The <see cref="ClaimsPrincipal"/> to cancel.</param> 236 /// <param name="request">The request.</param> 237 /// <returns>The response.</returns> Cancel(ClaimsPrincipal principal, RST request)238 public virtual RSTR Cancel(ClaimsPrincipal principal, RST request) 239 { 240 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null && request.RequestType != null ? request.RequestType : "Cancel")))); 241 } 242 243 /// <summary> 244 /// Creates an instance of a <see cref="SecurityTokenDescriptor"/>. 245 /// </summary> 246 /// <param name="request">The incoming token request.</param> 247 /// <param name="scope">The <see cref="Scope"/> object returned from <see cref="SecurityTokenService.GetScope"/>.</param> 248 /// <returns>The <see cref="SecurityTokenDescriptor"/>.</returns> 249 /// <remarks>Invoked during token issuance after <see cref="SecurityTokenService.GetScope"/>.</remarks> CreateSecurityTokenDescriptor(RST request, Scope scope)250 protected virtual SecurityTokenDescriptor CreateSecurityTokenDescriptor(RST request, Scope scope) 251 { 252 if (request == null) 253 { 254 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); 255 } 256 257 if (scope == null) 258 { 259 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("scope"); 260 } 261 262 SecurityTokenDescriptor d = new SecurityTokenDescriptor(); 263 d.AppliesToAddress = scope.AppliesToAddress; 264 d.ReplyToAddress = scope.ReplyToAddress; 265 d.SigningCredentials = scope.SigningCredentials; 266 if (null == d.SigningCredentials) 267 { 268 d.SigningCredentials = this.SecurityTokenServiceConfiguration.SigningCredentials; 269 } 270 271 272 273 // 274 // The encrypting credentials specified on the Scope object 275 // are invariant relative to a specific RP. Allowing the STS to 276 // cache the Scope for each RP. 277 // Our default implementation will generate the symmetric bulk 278 // encryption key on the fly. 279 // 280 if (scope.EncryptingCredentials != null && 281 scope.EncryptingCredentials.SecurityKey is AsymmetricSecurityKey 282 ) 283 { 284 if ((request.EncryptionAlgorithm == null || request.EncryptionAlgorithm == SecurityAlgorithms.Aes256Encryption) && 285 (request.SecondaryParameters == null || request.SecondaryParameters.EncryptionAlgorithm == null || request.SecondaryParameters.EncryptionAlgorithm == SecurityAlgorithms.Aes256Encryption) 286 ) 287 { 288 d.EncryptingCredentials = new EncryptedKeyEncryptingCredentials(scope.EncryptingCredentials, 256, SecurityAlgorithms.Aes256Encryption); 289 } 290 } 291 292 return d; 293 } 294 295 /// <summary> 296 /// Gets the STS's name. 297 /// </summary> 298 /// <returns>Returns the issuer name.</returns> GetIssuerName()299 protected virtual string GetIssuerName() 300 { 301 return SecurityTokenServiceConfiguration.TokenIssuerName; 302 } 303 304 /// <summary> 305 /// Checks the IssuerName for validity (non-null) 306 /// </summary> GetValidIssuerName()307 private string GetValidIssuerName() 308 { 309 string issuerName = GetIssuerName(); 310 311 if (string.IsNullOrEmpty(issuerName)) 312 { 313 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2083)); 314 } 315 316 return issuerName; 317 } 318 319 /// <summary> 320 /// Gets the proof token. 321 /// </summary> 322 /// <param name="request">The incoming token request.</param> 323 /// <param name="scope">The scope instance encapsulating information about the relying party.</param> 324 /// <returns>The newly created proof decriptor that could be either asymmetric proof descriptor or symmetric proof descriptor or null in the bearer token case.</returns> GetProofToken(RST request, Scope scope)325 protected virtual ProofDescriptor GetProofToken(RST request, Scope scope) 326 { 327 if (request == null) 328 { 329 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); 330 } 331 332 if (scope == null) 333 { 334 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("scope"); 335 } 336 337 EncryptingCredentials requestorWrappingCredentials = GetRequestorProofEncryptingCredentials(request); 338 339 if (scope.EncryptingCredentials != null && 340 !(scope.EncryptingCredentials.SecurityKey is AsymmetricSecurityKey)) 341 { 342 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 343 new SecurityTokenException(SR.GetString(SR.ID4179))); 344 } 345 346 EncryptingCredentials targetWrappingCredentials = scope.EncryptingCredentials; 347 348 // 349 // Generate the proof key 350 // 351 string keyType = (string.IsNullOrEmpty(request.KeyType)) ? KeyTypes.Symmetric : request.KeyType; 352 ProofDescriptor result = null; 353 354 if (StringComparer.Ordinal.Equals(keyType, KeyTypes.Asymmetric)) 355 { 356 // 357 // Asymmetric is only supported with UseKey 358 // 359 if (request.UseKey == null) 360 { 361 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3091))); 362 } 363 364 result = new AsymmetricProofDescriptor(request.UseKey.SecurityKeyIdentifier); 365 } 366 else if (StringComparer.Ordinal.Equals(keyType, KeyTypes.Symmetric)) 367 { 368 // 369 // Only support PSHA1. Overwrite STS to support custom key algorithm 370 // 371 if (request.ComputedKeyAlgorithm != null && !StringComparer.Ordinal.Equals(request.ComputedKeyAlgorithm, ComputedKeyAlgorithms.Psha1)) 372 { 373 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new RequestFailedException(SR.GetString(SR.ID2011, request.ComputedKeyAlgorithm))); 374 } 375 // 376 // We must wrap the symmetric key inside the security token 377 // 378 if (targetWrappingCredentials == null && scope.SymmetricKeyEncryptionRequired) 379 { 380 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new RequestFailedException(SR.GetString(SR.ID4007))); 381 } 382 383 // 384 // We are encrypting the proof token or the server entropy using client's encrypting credential if present, 385 // which will be used to encrypt the key during serialization. 386 // Otherwise, we can only send back the key in plain text. However, the current implementation of 387 // WSTrustServiceContract sets the rst.ProofEncryption = null by default. Therefore, the server entropy 388 // or the proof token will be sent in plain text no matter the client's entropy is sent encrypted or unencrypted. 389 // 390 if (request.KeySizeInBits.HasValue) 391 { 392 if (request.Entropy != null) 393 { 394 result = new SymmetricProofDescriptor(request.KeySizeInBits.Value, targetWrappingCredentials, requestorWrappingCredentials, 395 request.Entropy.GetKeyBytes(), request.EncryptWith); 396 } 397 else 398 { 399 result = new SymmetricProofDescriptor(request.KeySizeInBits.Value, targetWrappingCredentials, 400 requestorWrappingCredentials, request.EncryptWith); 401 } 402 } 403 else 404 { 405 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new RequestFailedException(SR.GetString(SR.ID2059))); 406 } 407 } 408 else if (StringComparer.Ordinal.Equals(keyType, KeyTypes.Bearer)) 409 { 410 // 411 // Intentionally empty, no proofDescriptor 412 // 413 } 414 415 return result; 416 } 417 418 /// <summary> 419 /// Get the Requestor's Proof encrypting credentials. 420 /// </summary> 421 /// <param name="request">RequestSecurityToken</param> 422 /// <returns>EncryptingCredentials</returns> 423 /// <exception cref="ArgumentNullException">Input argument 'request' is null.</exception> GetRequestorProofEncryptingCredentials(RST request)424 protected virtual EncryptingCredentials GetRequestorProofEncryptingCredentials(RST request) 425 { 426 if (request == null) 427 { 428 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); 429 } 430 431 if (request.ProofEncryption == null) 432 { 433 return null; 434 } 435 436 X509SecurityToken x509SecurityToken = request.ProofEncryption.GetSecurityToken() as X509SecurityToken; 437 if (x509SecurityToken != null) 438 { 439 return new X509EncryptingCredentials(x509SecurityToken); 440 } 441 442 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new RequestFailedException(SR.GetString(SR.ID2084, request.ProofEncryption.GetSecurityToken()))); 443 } 444 445 /// <summary> 446 /// Gets the lifetime of the issued token. 447 /// Normally called with the lifetime that arrived in the RST. 448 /// The algorithm for calculating the token lifetime is: 449 /// requestLifeTime (in) LifeTime (returned) 450 /// Created Expires Created Expires 451 /// null null DateTime.UtcNow DateTime.UtcNow + SecurityTokenServiceConfiguration.DefaultTokenLifetime 452 /// C null C C + SecurityTokenServiceConfiguration.DefaultTokenLifetime 453 /// null E DateTime.UtcNow E 454 /// C E C E 455 /// </summary> 456 /// <param name="requestLifetime">The requestor's desired life time.</param> GetTokenLifetime(Lifetime requestLifetime)457 protected virtual Lifetime GetTokenLifetime(Lifetime requestLifetime) 458 { 459 DateTime created; 460 DateTime expires; 461 462 if (requestLifetime == null) 463 { 464 created = DateTime.UtcNow; 465 expires = DateTimeUtil.Add(created, _securityTokenServiceConfiguration.DefaultTokenLifetime); 466 } 467 else 468 { 469 if (requestLifetime.Created.HasValue) 470 { 471 created = requestLifetime.Created.Value; 472 } 473 else 474 { 475 created = DateTime.UtcNow; 476 } 477 478 if (requestLifetime.Expires.HasValue) 479 { 480 expires = requestLifetime.Expires.Value; 481 } 482 else 483 { 484 expires = DateTimeUtil.Add(created, _securityTokenServiceConfiguration.DefaultTokenLifetime); 485 } 486 } 487 488 VerifyComputedLifetime(created, expires); 489 490 return new Lifetime(created, expires); 491 } 492 VerifyComputedLifetime(DateTime created, DateTime expires)493 private void VerifyComputedLifetime(DateTime created, DateTime expires) 494 { 495 496 DateTime utcNow = DateTime.UtcNow; 497 498 // if expires in past, throw 499 if (DateTimeUtil.Add(DateTimeUtil.ToUniversalTime(expires), _securityTokenServiceConfiguration.MaxClockSkew) < utcNow) 500 { 501 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2075, created, expires, utcNow))); 502 } 503 504 // if creation time specified is greater than one day in future, throw 505 if (DateTimeUtil.ToUniversalTime(created) > DateTimeUtil.Add(utcNow + TimeSpan.FromDays(1), _securityTokenServiceConfiguration.MaxClockSkew)) 506 { 507 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2076, created, expires, utcNow))); 508 } 509 510 // if expiration time is equal to or before creation time, throw. This would be hard to make happen as the Lifetime class checks this condition in the constructor 511 if (expires <= created) 512 { 513 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2077, created, expires))); 514 } 515 516 // if timespan is greater than allowed, throw 517 if ((expires - created) > _securityTokenServiceConfiguration.MaximumTokenLifetime) 518 { 519 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2078, created, expires, _securityTokenServiceConfiguration.MaximumTokenLifetime))); 520 } 521 522 return; 523 } 524 525 /// <summary> 526 /// Creates the RSTR and finally read the information from TokenDescriptor and apply 527 /// those to the RSTR. 528 /// </summary> 529 /// <param name="request">The RST from the request.</param> 530 /// <param name="tokenDescriptor">The token descriptor which contains the information for the issued token.</param> 531 /// <returns>The RSTR for the response, null if the token descriptor is null.</returns> GetResponse(RST request, SecurityTokenDescriptor tokenDescriptor)532 protected virtual RSTR GetResponse(RST request, SecurityTokenDescriptor tokenDescriptor) 533 { 534 if (tokenDescriptor != null) 535 { 536 RSTR rstr = new RSTR(request); 537 tokenDescriptor.ApplyTo(rstr); 538 539 // Set the replyTo address of the relying party (if any) in the outgoing RSTR from the generated 540 // token descriptor (STD) based on the table below: 541 // 542 // RST.ReplyTo STD.ReplyToAddress RSTR.ReplyTo 543 // =========== ==================== ============ 544 // Set Not Set Not Set 545 // Set Set Set to STD.ReplyToAddress 546 // Not Set Not Set Not Set 547 // Not Set Set Not Set 548 // 549 if (request.ReplyTo != null) 550 { 551 rstr.ReplyTo = tokenDescriptor.ReplyToAddress; 552 } 553 554 // 555 // Set the appliesTo address (if any) in the outgoing RSTR from the generated token descriptor. 556 // 557 if (!string.IsNullOrEmpty(tokenDescriptor.AppliesToAddress)) 558 { 559 rstr.AppliesTo = new EndpointReference(tokenDescriptor.AppliesToAddress); 560 } 561 562 return rstr; 563 } 564 else 565 { 566 return null; 567 } 568 } 569 570 /// <summary> 571 /// Async Cancel. 572 /// </summary> 573 /// <param name="result"></param> 574 /// <returns></returns> EndCancel(IAsyncResult result)575 public virtual RSTR EndCancel(IAsyncResult result) 576 { 577 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, "Cancel"))); 578 } 579 580 /// <summary> 581 /// Ends the Async call to BeginGetScope. Default implementation will throw a NotImplementedException. 582 /// Refer MSDN articles on Using an AsyncCallback Delegate to End an Asynchronous Operation. 583 /// </summary> 584 /// <param name="result">Typed Async result which contains the result. This is the same instance of 585 /// IAsyncResult that was returned by the BeginGetScope method.</param> 586 /// <returns>The scope.</returns> EndGetScope(IAsyncResult result)587 protected virtual Scope EndGetScope(IAsyncResult result) 588 { 589 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException(SR.GetString(SR.ID2081))); 590 } 591 592 /// <summary> 593 /// Ends the async call of Issue request. This would finally return the RequestSecurityTokenResponse. 594 /// </summary> 595 /// <param name="result">The async result returned from the BeginIssue method.</param> 596 /// <returns>The security token response.</returns> EndIssue(IAsyncResult result)597 public virtual RSTR EndIssue(IAsyncResult result) 598 { 599 if (result == null) 600 { 601 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result"); 602 } 603 604 if (!(result is TypedAsyncResult<RSTR>)) 605 { 606 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2012, typeof(TypedAsyncResult<RSTR>), result.GetType()))); 607 } 608 609 return TypedAsyncResult<RSTR>.End(result); 610 } 611 612 /// <summary> 613 /// Async Renew. 614 /// </summary> 615 /// <param name="result"></param> 616 /// <returns></returns> EndRenew(IAsyncResult result)617 public virtual RSTR EndRenew(IAsyncResult result) 618 { 619 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, "Renew"))); 620 } 621 622 /// <summary> 623 /// Async Validate. 624 /// </summary> 625 /// <param name="result"></param> 626 /// <returns></returns> EndValidate(IAsyncResult result)627 public virtual RSTR EndValidate(IAsyncResult result) 628 { 629 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, "Validate"))); 630 } 631 632 /// <summary> 633 /// Retrieves the relying party information. Override this method to provide a custom scope with 634 /// relying party related information. 635 /// </summary> 636 /// <param name="principal">The identity of the token requestor.</param> 637 /// <param name="request">The request message.</param> 638 /// <returns>A scope object based on the relying party related information.</returns> GetScope(ClaimsPrincipal principal, RST request)639 protected abstract Scope GetScope(ClaimsPrincipal principal, RST request); 640 641 /// <summary> 642 /// When overridden in a derived class, this method should return a collection of output subjects to be included in the issued token. 643 /// </summary> 644 /// <param name="principal">The ClaimsPrincipal that represents the identity of the requestor.</param> 645 /// <param name="request">The token request parameters that arrived in the call.</param> 646 /// <param name="scope">The scope information about the Relying Party.</param> 647 /// <returns>The ClaimsIdentity representing the collection of claims that will be placed in the issued security token.</returns> GetOutputClaimsIdentity(ClaimsPrincipal principal, RequestSecurityToken request, Scope scope)648 protected abstract ClaimsIdentity GetOutputClaimsIdentity(ClaimsPrincipal principal, RequestSecurityToken request, Scope scope); 649 650 /// <summary> 651 /// Begins async call for GetOutputSubjects routine. Default implementation will throw a NotImplementedExcetion. 652 /// Refer MSDN articles on Using an AsyncCallback Delegate to End an Asynchronous Operation. 653 /// </summary> 654 /// <param name="principal">The authorization context that represents the identity of the requestor.</param> 655 /// <param name="request">The token request parameters that arrived in the call.</param> 656 /// <param name="scope">The scope information about the Relying Party.</param> 657 /// <param name="callback">The callback to be invoked when the user Asynchronous operation completed.</param> 658 /// <param name="state">The state object.</param> 659 /// <returns>IAsyncResult. Represents the status of an asynchronous operation. This will be passed into 660 /// EndGetOutputClaimsIdentity.</returns> BeginGetOutputClaimsIdentity(ClaimsPrincipal principal, RequestSecurityToken request, Scope scope, AsyncCallback callback, object state)661 protected virtual IAsyncResult BeginGetOutputClaimsIdentity(ClaimsPrincipal principal, RequestSecurityToken request, Scope scope, AsyncCallback callback, object state) 662 { 663 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException(SR.GetString(SR.ID2081))); 664 } 665 666 /// <summary> 667 /// Ends the Async call to BeginGetOutputSubjects. Default implementation will throw a NotImplementedExcetion. 668 /// Refer MSDN articles on Using an AsyncCallback Delegate to End an Asynchronous Operation. 669 /// </summary> 670 /// <param name="result">Typed Async result which contains the result. This was the same IAsyncResult that 671 /// was returned by the BeginGetOutputClaimsIdentity.</param> 672 /// <returns>The claimsets collection that will be placed inside the issued token.</returns> EndGetOutputClaimsIdentity(IAsyncResult result)673 protected virtual ClaimsIdentity EndGetOutputClaimsIdentity(IAsyncResult result) 674 { 675 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException(SR.GetString(SR.ID2081))); 676 } 677 678 /// <summary> 679 /// Issues a Security Token. 680 /// </summary> 681 /// <param name="principal">The identity of the token requestor.</param> 682 /// <param name="request">The request.</param> 683 /// <returns>The response.</returns> Issue(ClaimsPrincipal principal, RST request)684 public virtual RSTR Issue(ClaimsPrincipal principal, RST request) 685 { 686 if (request == null) 687 { 688 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); 689 } 690 691 _principal = principal; 692 _request = request; 693 694 // 1. Do request validation 695 ValidateRequest(request); 696 697 // 2. Get the scope and populate into tokenDescriptor 698 Scope scope = GetScope(principal, request); 699 if (scope == null) 700 { 701 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2013)); 702 } 703 this.Scope = scope; 704 705 706 // Create the security token descriptor now that we have a scope. 707 this.SecurityTokenDescriptor = CreateSecurityTokenDescriptor(request, scope); 708 if (this.SecurityTokenDescriptor == null) 709 { 710 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2003)); 711 } 712 713 if (this.SecurityTokenDescriptor.SigningCredentials == null) 714 { 715 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2079)); 716 } 717 718 // 719 // If TokenEncryptionRequired is set to true, then we must encrypt the token. 720 // 721 if (this.Scope.TokenEncryptionRequired && this.SecurityTokenDescriptor.EncryptingCredentials == null) 722 { 723 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4184)); 724 } 725 726 // 3. Get the token-handler 727 SecurityTokenHandler securityTokenHandler = GetSecurityTokenHandler(request.TokenType); 728 if (securityTokenHandler == null) 729 { 730 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ID4010, request.TokenType))); 731 } 732 733 734 // 4. Get the issuer name and populate into tokenDescriptor 735 _tokenDescriptor.TokenIssuerName = GetValidIssuerName(); 736 737 // 5. Get the token lifetime and populate into tokenDescriptor 738 _tokenDescriptor.Lifetime = GetTokenLifetime(request.Lifetime); 739 740 // 6. Get the proof token and populate into tokenDescriptor 741 _tokenDescriptor.Proof = GetProofToken(request, scope); 742 743 // 7. Get the subjects and populate into tokenDescriptor 744 _tokenDescriptor.Subject = GetOutputClaimsIdentity(principal, request, scope); 745 746 // use the securityTokenHandler from Step 3 to create and setup the issued token information on the tokenDescriptor 747 // (actual issued token, AttachedReference and UnattachedReference) 748 // TokenType is preserved from the request if possible 749 if (!string.IsNullOrEmpty(request.TokenType)) 750 { 751 _tokenDescriptor.TokenType = request.TokenType; 752 } 753 else 754 { 755 string[] identifiers = securityTokenHandler.GetTokenTypeIdentifiers(); 756 if (identifiers == null || identifiers.Length == 0) 757 { 758 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4264, request.TokenType))); 759 } 760 _tokenDescriptor.TokenType = identifiers[0]; 761 } 762 _tokenDescriptor.Token = securityTokenHandler.CreateToken(_tokenDescriptor); 763 _tokenDescriptor.AttachedReference = securityTokenHandler.CreateSecurityTokenReference(_tokenDescriptor.Token, true); 764 _tokenDescriptor.UnattachedReference = securityTokenHandler.CreateSecurityTokenReference(_tokenDescriptor.Token, false); 765 766 // 9. Create the RSTR 767 RSTR rstr = GetResponse(request, _tokenDescriptor); 768 769 return rstr; 770 } 771 772 /// <summary> 773 /// Gets an appropriate SecurityTokenHandler for issuing a security token. 774 /// </summary> 775 /// <param name="requestedTokenType">The requested TokenType.</param> 776 /// <returns>The SecurityTokenHandler to be used for creating the issued security token.</returns> GetSecurityTokenHandler(string requestedTokenType)777 protected virtual SecurityTokenHandler GetSecurityTokenHandler(string requestedTokenType) 778 { 779 string tokenType = string.IsNullOrEmpty(requestedTokenType) ? _securityTokenServiceConfiguration.DefaultTokenType : requestedTokenType; 780 781 SecurityTokenHandler securityTokenHandler = _securityTokenServiceConfiguration.SecurityTokenHandlers[tokenType]; 782 return securityTokenHandler; 783 } 784 785 /// <summary> 786 /// This routine processes the stored data about the target resource 787 /// for who the Issued token is for. 788 /// </summary> 789 /// <param name="result">The async result.</param> 790 /// <exception cref="ArgumentNullException">When the given async result is null.</exception> OnGetScopeComplete(IAsyncResult result)791 void OnGetScopeComplete(IAsyncResult result) 792 { 793 if (null == result) 794 { 795 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result"); 796 } 797 798 FederatedAsyncState state = result.AsyncState as FederatedAsyncState; 799 if (state == null) 800 { 801 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2001))); 802 } 803 804 Exception unhandledException = null; 805 TypedAsyncResult<RSTR> typedResult = state.Result as TypedAsyncResult<RSTR>; 806 if (typedResult == null) 807 { 808 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2004, typeof(TypedAsyncResult<RSTR>), state.Result.GetType()))); 809 } 810 811 RST request = state.Request; 812 813 try 814 { 815 // 816 // 2. Retrieve the scope information 817 // 818 Scope scope = EndGetScope(result); 819 if (scope == null) 820 { 821 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2013)); 822 } 823 this.Scope = scope; 824 825 // 826 // Create a security token descriptor 827 // 828 this.SecurityTokenDescriptor = CreateSecurityTokenDescriptor(request, this.Scope); 829 if (this.SecurityTokenDescriptor == null) 830 { 831 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2003)); 832 } 833 834 if (this.SecurityTokenDescriptor.SigningCredentials == null) 835 { 836 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2079)); 837 } 838 839 // 840 // If TokenEncryptionRequired is set to true, then we must encrypt the token. 841 // 842 if (this.Scope.TokenEncryptionRequired && this.SecurityTokenDescriptor.EncryptingCredentials == null) 843 { 844 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4184)); 845 } 846 847 // 848 // Step 3: Retrieve the token handler to use for creating token and store it in the state 849 // 850 SecurityTokenHandler securityTokenHandler = GetSecurityTokenHandler(request == null ? null : request.TokenType); 851 if (securityTokenHandler == null) 852 { 853 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ID4010, request == null ? String.Empty : request.TokenType))); 854 } 855 state.SecurityTokenHandler = securityTokenHandler; 856 857 // 858 // Step 4: Logical issuer name 859 // 860 _tokenDescriptor.TokenIssuerName = GetValidIssuerName(); 861 862 // 863 // Step 5: Establish token lifetime 864 // 865 _tokenDescriptor.Lifetime = GetTokenLifetime(request == null ? null : request.Lifetime); 866 867 // 868 // Step 6: Compute the proof key 869 // 870 _tokenDescriptor.Proof = GetProofToken(request, this.Scope); 871 872 // 873 // Start the async call for generating the output subjects. 874 // 875 BeginGetOutputClaimsIdentity(state.ClaimsPrincipal, state.Request, scope, OnGetOutputClaimsIdentityComplete, state); 876 } 877 #pragma warning suppress 56500 878 catch (Exception e) 879 { 880 if (System.Runtime.Fx.IsFatal(e)) 881 throw; 882 883 unhandledException = e; 884 } 885 886 if (unhandledException != null) 887 { 888 // 889 // Complete the request in failure 890 // 891 typedResult.Complete(null, result.CompletedSynchronously, unhandledException); 892 } 893 } 894 895 /// <summary> 896 /// The callback that is invoked on the completion of the BeginGetOutputSubjects call. 897 /// </summary> 898 /// <param name="result">The async result.</param> 899 /// <exception cref="ArgumentNullException">When the given async result is null.</exception> OnGetOutputClaimsIdentityComplete(IAsyncResult result)900 void OnGetOutputClaimsIdentityComplete(IAsyncResult result) 901 { 902 if (null == result) 903 { 904 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result"); 905 } 906 907 FederatedAsyncState state = result.AsyncState as FederatedAsyncState; 908 if (state == null) 909 { 910 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2001))); 911 } 912 913 SecurityTokenHandler securityTokenHandler = state.SecurityTokenHandler; 914 if (securityTokenHandler == null) 915 { 916 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2016))); 917 } 918 919 Exception unhandledException = null; 920 RST request = state.Request; 921 RSTR response = null; 922 923 TypedAsyncResult<RSTR> typedResult = state.Result as TypedAsyncResult<RSTR>; 924 if (typedResult == null) 925 { 926 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID2004, typeof(TypedAsyncResult<RSTR>), state.Result.GetType()))); 927 } 928 929 try 930 { 931 // 932 // get token descriptor 933 // 934 if (_tokenDescriptor == null) 935 { 936 throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID2003)); 937 } 938 // Step 7: Retrieve the output claims to be included in the issued-token 939 _tokenDescriptor.Subject = EndGetOutputClaimsIdentity(result); 940 941 // 942 // Use the retrieved securityTokenHandler to create and setup the issued token information on the tokenDescriptor 943 // (actual issued token, AttachedReference and UnattachedReference) 944 // TokenType is preserved from the request if possible 945 if (!string.IsNullOrEmpty(request.TokenType)) 946 { 947 _tokenDescriptor.TokenType = request.TokenType; 948 } 949 else 950 { 951 string[] identifiers = securityTokenHandler.GetTokenTypeIdentifiers(); 952 if (identifiers == null || identifiers.Length == 0) 953 { 954 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4264, request.TokenType))); 955 } 956 _tokenDescriptor.TokenType = identifiers[0]; 957 } 958 _tokenDescriptor.Token = securityTokenHandler.CreateToken(_tokenDescriptor); 959 _tokenDescriptor.AttachedReference = securityTokenHandler.CreateSecurityTokenReference(_tokenDescriptor.Token, true); 960 _tokenDescriptor.UnattachedReference = securityTokenHandler.CreateSecurityTokenReference(_tokenDescriptor.Token, false); 961 962 // 9. Create the RSTR 963 response = GetResponse(request, _tokenDescriptor); 964 } 965 #pragma warning suppress 56500 966 catch (Exception e) 967 { 968 if (System.Runtime.Fx.IsFatal(e)) 969 throw; 970 971 unhandledException = e; 972 } 973 974 typedResult.Complete(response, typedResult.CompletedSynchronously, unhandledException); 975 } 976 977 /// <summary> 978 /// Gets the Owner configuration instance. 979 /// </summary> 980 public SecurityTokenServiceConfiguration SecurityTokenServiceConfiguration 981 { 982 get 983 { 984 return _securityTokenServiceConfiguration; 985 } 986 } 987 988 /// <summary> 989 /// Gets or sets the ClaimsPrincipal associated with the current instance. 990 /// </summary> 991 public ClaimsPrincipal Principal 992 { 993 get { return _principal; } 994 set { _principal = value; } 995 } 996 997 /// <summary> 998 /// Gets or sets the RequestSecurityToken associated with the current instance. 999 /// </summary> 1000 public RequestSecurityToken Request 1001 { 1002 get { return _request; } 1003 set { _request = value; } 1004 } 1005 1006 /// <summary> 1007 /// Gets or sets the Scope associated with the current instance. 1008 /// </summary> 1009 public Scope Scope 1010 { 1011 get; 1012 set; 1013 } 1014 1015 /// <summary> 1016 /// Gets or sets the SecurityTokenDescriptor associated with the current instance. 1017 /// </summary> 1018 protected SecurityTokenDescriptor SecurityTokenDescriptor 1019 { 1020 get { return _tokenDescriptor; } 1021 set 1022 { 1023 if (value == null) 1024 { 1025 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); 1026 } 1027 _tokenDescriptor = value; 1028 } 1029 } 1030 1031 /// <summary> 1032 /// Renew. 1033 /// </summary> 1034 /// <param name="principal">The <see cref="ClaimsPrincipal"/> to renew.</param> 1035 /// <param name="request">The request.</param> 1036 /// <returns>The response.</returns> Renew(ClaimsPrincipal principal, RST request)1037 public virtual RSTR Renew(ClaimsPrincipal principal, RST request) 1038 { 1039 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null && request.RequestType != null ? request.RequestType : "Renew")))); 1040 } 1041 1042 /// <summary> 1043 /// Validate. 1044 /// </summary> 1045 /// <param name="principal">The <see cref="ClaimsPrincipal"/> to validate.</param> 1046 /// <param name="request">The request.</param> 1047 /// <returns>The response.</returns> Validate(ClaimsPrincipal principal, RST request)1048 public virtual RSTR Validate(ClaimsPrincipal principal, RST request) 1049 { 1050 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID3141, (request != null && request.RequestType != null ? request.RequestType : "Validate")))); 1051 } 1052 1053 /// <summary> 1054 /// Validates the RequestSecurityToken encapsulated by this SecurityTokenService instance. 1055 /// </summary> ValidateRequest(RST request)1056 protected virtual void ValidateRequest(RST request) 1057 { 1058 // currently we only support RST/RSTR pattern 1059 if (request == null) 1060 { 1061 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2051))); 1062 } 1063 1064 // STS only support Issue for now 1065 if (request.RequestType != null && request.RequestType != RequestTypes.Issue) 1066 { 1067 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2052))); 1068 } 1069 1070 // key type must be one of the known types 1071 if (request.KeyType != null && !IsKnownType(request.KeyType)) 1072 { 1073 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2053))); 1074 } 1075 1076 // if key type is bearer key, we should fault if the KeySize element is present and its value is not equal to zero. 1077 if (StringComparer.Ordinal.Equals(request.KeyType, KeyTypes.Bearer) && request.KeySizeInBits.HasValue && (request.KeySizeInBits.Value != 0)) 1078 { 1079 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2050))); 1080 } 1081 1082 // token type must be supported for this STS 1083 if (GetSecurityTokenHandler(request.TokenType) == null) 1084 { 1085 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new UnsupportedTokenTypeBadRequestException(request.TokenType)); 1086 } 1087 1088 request.KeyType = (string.IsNullOrEmpty(request.KeyType)) ? KeyTypes.Symmetric : request.KeyType; 1089 1090 if (StringComparer.Ordinal.Equals(request.KeyType, KeyTypes.Symmetric)) 1091 { 1092 // 1093 // Check if the key size is within certain limit to prevent Dos attack 1094 // 1095 if (request.KeySizeInBits.HasValue) 1096 { 1097 if (request.KeySizeInBits.Value > _securityTokenServiceConfiguration.DefaultMaxSymmetricKeySizeInBits) 1098 { 1099 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidRequestException(SR.GetString(SR.ID2056, request.KeySizeInBits.Value, _securityTokenServiceConfiguration.DefaultMaxSymmetricKeySizeInBits))); 1100 } 1101 } 1102 else 1103 { 1104 request.KeySizeInBits = _securityTokenServiceConfiguration.DefaultSymmetricKeySizeInBits; 1105 } 1106 } 1107 } 1108 IsKnownType(string keyType)1109 static bool IsKnownType(string keyType) 1110 { 1111 return (StringComparer.Ordinal.Equals(keyType, KeyTypes.Symmetric) 1112 || StringComparer.Ordinal.Equals(keyType, KeyTypes.Asymmetric) 1113 || StringComparer.Ordinal.Equals(keyType, KeyTypes.Bearer)); 1114 } 1115 } 1116 } 1117