/// <summary>
        /// Authenicates a user based on the information in the HTTP request.
        /// </summary>
        /// <returns></returns>
        public override void Authenticate(AuthenticationRequest request, AuthenticationResponse response)
        {
            // Only execute the authentication if the user is not known yet

            if (response.Principal == null)
            {
                // Get OpenID provider's response from the http context
                using (var openid = new OpenIdRelyingParty())
                {
                    var openIDResponse = openid.GetResponse();

                    // TODO: figure out which OpenID provider sent the response
                    // and associate with the right authenticator

                    if (response != null)
                    {
                        switch (openIDResponse.Status)
                        {
                            case AuthenticationStatus.Authenticated:
                                response.SetPrincipal(CreatePrincipal(openIDResponse));
                                break;
                            case AuthenticationStatus.Canceled:
                            case AuthenticationStatus.Failed:
                                throw new System.Security.Authentication.AuthenticationException("OpenID authentication failed.", openIDResponse.Exception); // TODO
                            case AuthenticationStatus.ExtensionsOnly:
                            case AuthenticationStatus.SetupRequired:
                                throw new InvalidOperationException();
                            default:
                                throw new NotImplementedException();
                        }
                    }
                }
            }
        }
		public override void SetUp() {
			base.SetUp();

			var rp = CreateRelyingParty(true);
			Identifier identifier = this.GetMockIdentifier(ProtocolVersion.V20);
			this.authReq = (AuthenticationRequest)rp.CreateRequest(identifier, RPRealmUri, RPUri);
			this.sreg = new ClaimsRequest {
				Nickname = DemandLevel.Request,
				FullName = DemandLevel.Request,
				BirthDate = DemandLevel.Request,
				Email = DemandLevel.Require,
				Country = DemandLevel.Request,
				PostalCode = DemandLevel.Request,
				Gender = DemandLevel.Request,
				Language = DemandLevel.Request,
				TimeZone = DemandLevel.Request,
			};
		}
		/// <summary>
		/// Performs deferred request generation for the <see cref="Create"/> method.
		/// </summary>
		/// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
		/// <param name="relyingParty">The relying party.</param>
		/// <param name="realm">The realm.</param>
		/// <param name="returnToUrl">The return_to base URL.</param>
		/// <param name="serviceEndpoints">The discovered service endpoints on the Claimed Identifier.</param>
		/// <param name="createNewAssociationsAsNeeded">if set to <c>true</c>, associations that do not exist between this Relying Party and the asserting Providers are created before the authentication request is created.</param>
		/// <returns>
		/// A sequence of authentication requests, any of which constitutes a valid identity assertion on the Claimed Identifier.
		/// Never null, but may be empty.
		/// </returns>
		/// <remarks>
		/// All data validation and cleansing steps must have ALREADY taken place
		/// before calling this method.
		/// </remarks>
		private static IEnumerable<AuthenticationRequest> CreateInternal(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, IEnumerable<IdentifierDiscoveryResult> serviceEndpoints, bool createNewAssociationsAsNeeded) {
			// DO NOT USE CODE CONTRACTS IN THIS METHOD, since it uses yield return
			ErrorUtilities.VerifyArgumentNotNull(userSuppliedIdentifier, "userSuppliedIdentifier");
			ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
			ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
			ErrorUtilities.VerifyArgumentNotNull(serviceEndpoints, "serviceEndpoints");
			////Contract.Ensures(Contract.Result<IEnumerable<AuthenticationRequest>>() != null);

			// If shared associations are required, then we had better have an association store.
			ErrorUtilities.VerifyOperation(!relyingParty.SecuritySettings.RequireAssociation || relyingParty.AssociationManager.HasAssociationStore, OpenIdStrings.AssociationStoreRequired);

			Logger.Yadis.InfoFormat("Performing discovery on user-supplied identifier: {0}", userSuppliedIdentifier);
			IEnumerable<IdentifierDiscoveryResult> endpoints = FilterAndSortEndpoints(serviceEndpoints, relyingParty);

			// Maintain a list of endpoints that we could not form an association with.
			// We'll fallback to generating requests to these if the ones we CAN create
			// an association with run out.
			var failedAssociationEndpoints = new List<IdentifierDiscoveryResult>(0);

			foreach (var endpoint in endpoints) {
				Logger.OpenId.DebugFormat("Creating authentication request for user supplied Identifier: {0}", userSuppliedIdentifier);

				// The strategy here is to prefer endpoints with whom we can create associations.
				Association association = null;
				if (relyingParty.AssociationManager.HasAssociationStore) {
					// In some scenarios (like the AJAX control wanting ALL auth requests possible),
					// we don't want to create associations with every Provider.  But we'll use
					// associations where they are already formed from previous authentications.
					association = createNewAssociationsAsNeeded ? relyingParty.AssociationManager.GetOrCreateAssociation(endpoint) : relyingParty.AssociationManager.GetExistingAssociation(endpoint);
					if (association == null && createNewAssociationsAsNeeded) {
						Logger.OpenId.WarnFormat("Failed to create association with {0}.  Skipping to next endpoint.", endpoint.ProviderEndpoint);

						// No association could be created.  Add it to the list of failed association
						// endpoints and skip to the next available endpoint.
						failedAssociationEndpoints.Add(endpoint);
						continue;
					}
				}

				yield return new AuthenticationRequest(endpoint, realm, returnToUrl, relyingParty);
			}

			// Now that we've run out of endpoints that respond to association requests,
			// since we apparently are still running, the caller must want another request.
			// We'll go ahead and generate the requests to OPs that may be down -- 
			// unless associations are set as required in our security settings.
			if (failedAssociationEndpoints.Count > 0) {
				if (relyingParty.SecuritySettings.RequireAssociation) {
					Logger.OpenId.Warn("Associations could not be formed with some Providers.  Security settings require shared associations for authentication requests so these will be skipped.");
				} else {
					Logger.OpenId.Debug("Now generating requests for Provider endpoints that failed initial association attempts.");

					foreach (var endpoint in failedAssociationEndpoints) {
						Logger.OpenId.DebugFormat("Creating authentication request for user supplied Identifier: {0} at endpoint: {1}", userSuppliedIdentifier, endpoint.ProviderEndpoint.AbsoluteUri);

						// Create the auth request, but prevent it from attempting to create an association
						// because we've already tried.  Let's not have it waste time trying again.
						var authRequest = new AuthenticationRequest(endpoint, realm, returnToUrl, relyingParty);
						authRequest.associationPreference = AssociationPreference.IfAlreadyEstablished;
						yield return authRequest;
					}
				}
			}
		}
예제 #4
0
        /// <summary>
        /// Performs deferred request generation for the <see cref="CreateAsync" /> method.
        /// </summary>
        /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
        /// <param name="relyingParty">The relying party.</param>
        /// <param name="realm">The realm.</param>
        /// <param name="returnToUrl">The return_to base URL.</param>
        /// <param name="serviceEndpoints">The discovered service endpoints on the Claimed Identifier.</param>
        /// <param name="createNewAssociationsAsNeeded">if set to <c>true</c>, associations that do not exist between this Relying Party and the asserting Providers are created before the authentication request is created.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// A sequence of authentication requests, any of which constitutes a valid identity assertion on the Claimed Identifier.
        /// Never null, but may be empty.
        /// </returns>
        /// <remarks>
        /// All data validation and cleansing steps must have ALREADY taken place
        /// before calling this method.
        /// </remarks>
        private static async Task <IEnumerable <AuthenticationRequest> > CreateInternalAsync(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, IEnumerable <IdentifierDiscoveryResult> serviceEndpoints, bool createNewAssociationsAsNeeded, CancellationToken cancellationToken)
        {
            Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier");
            Requires.NotNull(relyingParty, "relyingParty");
            Requires.NotNull(realm, "realm");
            Requires.NotNull(serviceEndpoints, "serviceEndpoints");
            ////
            // If shared associations are required, then we had better have an association store.
            ErrorUtilities.VerifyOperation(!relyingParty.SecuritySettings.RequireAssociation || relyingParty.AssociationManager.HasAssociationStore, OpenIdStrings.AssociationStoreRequired);

            Logger.Yadis.InfoFormat("Performing discovery on user-supplied identifier: {0}", userSuppliedIdentifier);
            IEnumerable <IdentifierDiscoveryResult> endpoints = FilterAndSortEndpoints(serviceEndpoints, relyingParty);

            var authRequestResults = endpoints.Select(async endpoint => {
                Logger.OpenId.DebugFormat("Creating authentication request for user supplied Identifier: {0}", userSuppliedIdentifier);

                // The strategy here is to prefer endpoints with whom we can create associations.
                if (relyingParty.AssociationManager.HasAssociationStore)
                {
                    // In some scenarios (like the AJAX control wanting ALL auth requests possible),
                    // we don't want to create associations with every Provider.  But we'll use
                    // associations where they are already formed from previous authentications.
                    Association association = createNewAssociationsAsNeeded ? await relyingParty.AssociationManager.GetOrCreateAssociationAsync(endpoint, cancellationToken) : relyingParty.AssociationManager.GetExistingAssociation(endpoint);
                    if (association == null && createNewAssociationsAsNeeded)
                    {
                        Logger.OpenId.WarnFormat("Failed to create association with {0}.  Skipping to next endpoint.", endpoint.ProviderEndpoint);

                        // No association could be created.  Add it to the list of failed association
                        // endpoints and skip to the next available endpoint.
                        return(new KeyValuePair <IdentifierDiscoveryResult, AuthenticationRequest>(endpoint, null));
                    }
                }

                return(new KeyValuePair <IdentifierDiscoveryResult, AuthenticationRequest>(endpoint, new AuthenticationRequest(endpoint, realm, returnToUrl, relyingParty)));
            }).ToList();

            await Task.WhenAll(authRequestResults);

            var results = (from pair in authRequestResults where pair.Result.Value != null select pair.Result.Value).ToList();

            // Maintain a list of endpoints that we could not form an association with.
            // We'll fallback to generating requests to these if the ones we CAN create
            // an association with run out.
            var failedAssociationEndpoints = (from pair in authRequestResults where pair.Result.Value == null select pair.Result.Key).ToList();

            // Now that we've run out of endpoints that respond to association requests,
            // since we apparently are still running, the caller must want another request.
            // We'll go ahead and generate the requests to OPs that may be down --
            // unless associations are set as required in our security settings.
            if (failedAssociationEndpoints.Count > 0)
            {
                if (relyingParty.SecuritySettings.RequireAssociation)
                {
                    Logger.OpenId.Warn("Associations could not be formed with some Providers.  Security settings require shared associations for authentication requests so these will be skipped.");
                }
                else
                {
                    Logger.OpenId.Debug("Now generating requests for Provider endpoints that failed initial association attempts.");

                    foreach (var endpoint in failedAssociationEndpoints)
                    {
                        Logger.OpenId.DebugFormat("Creating authentication request for user supplied Identifier: {0} at endpoint: {1}", userSuppliedIdentifier, endpoint.ProviderEndpoint.AbsoluteUri);

                        // Create the auth request, but prevent it from attempting to create an association
                        // because we've already tried.  Let's not have it waste time trying again.
                        var authRequest = new AuthenticationRequest(endpoint, realm, returnToUrl, relyingParty);
                        authRequest.associationPreference = AssociationPreference.IfAlreadyEstablished;
                        results.Add(authRequest);
                    }
                }
            }

            return(results);
        }
        /// <summary>
        /// Performs deferred request generation for the <see cref="Create"/> method.
        /// </summary>
        /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
        /// <param name="relyingParty">The relying party.</param>
        /// <param name="realm">The realm.</param>
        /// <param name="returnToUrl">The return_to base URL.</param>
        /// <param name="serviceEndpoints">The discovered service endpoints on the Claimed Identifier.</param>
        /// <param name="createNewAssociationsAsNeeded">if set to <c>true</c>, associations that do not exist between this Relying Party and the asserting Providers are created before the authentication request is created.</param>
        /// <returns>
        /// A sequence of authentication requests, any of which constitutes a valid identity assertion on the Claimed Identifier.
        /// </returns>
        /// <remarks>
        /// All data validation and cleansing steps must have ALREADY taken place
        /// before calling this method.
        /// </remarks>
        private static IEnumerable<AuthenticationRequest> CreateInternal(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, IEnumerable<ServiceEndpoint> serviceEndpoints, bool createNewAssociationsAsNeeded)
        {
            Logger.Yadis.InfoFormat("Performing discovery on user-supplied identifier: {0}", userSuppliedIdentifier);
            IEnumerable<ServiceEndpoint> endpoints = FilterAndSortEndpoints(serviceEndpoints, relyingParty);

            // Maintain a list of endpoints that we could not form an association with.
            // We'll fallback to generating requests to these if the ones we CAN create
            // an association with run out.
            var failedAssociationEndpoints = new List<ServiceEndpoint>(0);

            foreach (var endpoint in endpoints) {
                Logger.OpenId.InfoFormat("Creating authentication request for user supplied Identifier: {0}", userSuppliedIdentifier);

                // The strategy here is to prefer endpoints with whom we can create associations.
                Association association = null;
                if (relyingParty.AssociationManager.HasAssociationStore) {
                    // In some scenarios (like the AJAX control wanting ALL auth requests possible),
                    // we don't want to create associations with every Provider.  But we'll use
                    // associations where they are already formed from previous authentications.
                    association = createNewAssociationsAsNeeded ? relyingParty.AssociationManager.GetOrCreateAssociation(endpoint.ProviderDescription) : relyingParty.AssociationManager.GetExistingAssociation(endpoint.ProviderDescription);
                    if (association == null && createNewAssociationsAsNeeded) {
                        Logger.OpenId.WarnFormat("Failed to create association with {0}.  Skipping to next endpoint.", endpoint.ProviderEndpoint);

                        // No association could be created.  Add it to the list of failed association
                        // endpoints and skip to the next available endpoint.
                        failedAssociationEndpoints.Add(endpoint);
                        continue;
                    }
                }

                yield return new AuthenticationRequest(endpoint, realm, returnToUrl, relyingParty);
            }

            // Now that we've run out of endpoints that respond to association requests,
            // since we apparently are still running, the caller must want another request.
            // We'll go ahead and generate the requests to OPs that may be down.
            if (failedAssociationEndpoints.Count > 0) {
                Logger.OpenId.WarnFormat("Now generating requests for Provider endpoints that failed initial association attempts.");

                foreach (var endpoint in failedAssociationEndpoints) {
                    Logger.OpenId.WarnFormat("Creating authentication request for user supplied Identifier: {0}", userSuppliedIdentifier);

                    // Create the auth request, but prevent it from attempting to create an association
                    // because we've already tried.  Let's not have it waste time trying again.
                    var authRequest = new AuthenticationRequest(endpoint, realm, returnToUrl, relyingParty);
                    authRequest.associationPreference = AssociationPreference.IfAlreadyEstablished;
                    yield return authRequest;
                }
            }
        }