/// <summary>
        /// Initializes a new instance of the <see cref="OAuthManager"/> class.
        /// </summary>
        /// <param name="log">Log</param>
        /// <param name="appsManager">apps manager</param>
        /// <param name="usersManager">users manager</param>
        /// <param name="identityProvider">OAuth identity provider</param>
        public OAuthManager(ILog log, IAppsManager appsManager, IUsersManager usersManager, IdentityProviders identityProvider)
            : base(log, appsManager, usersManager)
        {
            this.oauthIdentityProvider = identityProvider;
            switch (this.oauthIdentityProvider)
            {
            case IdentityProviders.Microsoft:
                this.serverIdentityProvider = IdentityProviderType.Microsoft;
                break;

            case IdentityProviders.Facebook:
                this.serverIdentityProvider = IdentityProviderType.Facebook;
                break;

            case IdentityProviders.Google:
                this.serverIdentityProvider = IdentityProviderType.Google;
                break;

            case IdentityProviders.Twitter:
                this.serverIdentityProvider = IdentityProviderType.Twitter;
                break;

            default:
                throw new InvalidOperationException("Code should never reach here.");
            }
        }
예제 #2
0
        private static void FetchMetadata(Logging.IInternalLogger logger, IdentityProviders identityProviders, string metadataLocation)
        {
            // Get new metadata files
            foreach (var identityProvider in identityProviders)
            {
                logger.DebugFormat("Attempting to fetch SAML metadata file for identity provider {0}", identityProvider.Id);
                var metadataEndpoint = identityProvider.Endpoints.FirstOrDefault(x => x.Type == EndpointType.Metadata);
                if (metadataEndpoint == null)
                {
                    continue;
                }

                var metadataEndpointUrl = metadataEndpoint.Url;
                var metadataFile        = Path.Combine(metadataLocation, identityProvider.Id + ".xml");

                // Fetch new file
                try {
                    var client = new WebClient();
                    client.DownloadFile(metadataEndpointUrl, metadataFile + ".new");

                    // Wipe old file
                    if (File.Exists(metadataFile))
                    {
                        File.Delete(metadataFile);
                    }

                    // Move new file into place
                    File.Move(metadataFile + ".new", metadataFile);
                    logger.DebugFormat("Successfully updated SAML metadata file for identity provider {0}", identityProvider.Id);
                }
                catch (WebException ex) {
                    logger.Warn(string.Format("Unable to fetch SAML metadata file for identity provider {0}", identityProvider.Id), ex);
                }
            }
        }
예제 #3
0
        private static void FetchMetadata(Logging.IInternalLogger logger, IdentityProviders identityProviders, string metadataLocation)
        {
            // Get new metadata files
            foreach (var identityProvider in identityProviders) {
                logger.DebugFormat("Attempting to fetch SAML metadata file for identity provider {0}", identityProvider.Id);
                var metadataEndpoint = identityProvider.Endpoints.FirstOrDefault(x => x.Type == EndpointType.Metadata);
                if (metadataEndpoint == null) {
                    continue;
                }

                var metadataEndpointUrl = metadataEndpoint.Url;
                var metadataFile = Path.Combine(metadataLocation, identityProvider.Id + ".xml");

                // Fetch new file
                try {
                    var client = new WebClient();
                    client.DownloadFile(metadataEndpointUrl, metadataFile + ".new");

                    // Wipe old file
                    if (File.Exists(metadataFile)) {
                        File.Delete(metadataFile);
                    }

                    // Move new file into place
                    File.Move(metadataFile + ".new", metadataFile);
                    logger.DebugFormat("Successfully updated SAML metadata file for identity provider {0}", identityProvider.Id);
                }
                catch (WebException ex) {
                    logger.Warn(string.Format("Unable to fetch SAML metadata file for identity provider {0}", identityProvider.Id), ex);
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Implements the <c>OAuth</c> Implicit Flow. In this flow, a user authorizes the IdentityProvider to issue an access token. The client receives the
        /// access token and uses it to fetch the user profile. For this, it calls the appropriate fetch profile API offered by the identity provider.
        /// The implicit flow is considered an insecure form of authentication because an identity provider allows anyone to retrieve a profile
        /// with the access token.
        /// </summary>
        /// <param name="idProvider">third party identity provider</param>
        /// <param name="userAccessToken">user access token</param>
        /// <returns>the user profile</returns>
        public async Task <UserProfile> ImplicitFlow(IdentityProviders idProvider, string userAccessToken)
        {
            switch (idProvider)
            {
            case IdentityProviders.Microsoft:
                return(new UserProfile
                {
                    IdProvider = IdentityProviders.Microsoft,
                    MicrosoftProfile = await this.MicrosoftImplicitFlow(userAccessToken),
                });

            case IdentityProviders.Facebook:
                return(new UserProfile
                {
                    IdProvider = IdentityProviders.Facebook,
                    FacebookProfile = await this.FacebookImplicitFlow(userAccessToken),
                });

            case IdentityProviders.Google:
                return(new UserProfile
                {
                    IdProvider = IdentityProviders.Google,
                    GoogleProfile = await this.GoogleImplicitFlow(userAccessToken),
                });
            }

            throw new OAuthException(OAuthErrors.NotImplemented_501);
        }
예제 #5
0
        private Saml2Configuration GetSamlConfiguration()
        {
            var myconfig = new Saml2Configuration
            {
                ServiceProvider = new ServiceProvider
                {
                    SigningCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(FileEmbeddedResource("SelfHostOwinSPExample.sts_dev_certificate.pfx"), "test1234", System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.MachineKeySet),
                    Server             = "https://localhost:44333/core",
                    Id = "https://localhost:44333/core"
                },
                AllowedAudienceUris = new System.Collections.Generic.List <Uri>(new[] { new Uri("https://localhost:44333/core") })
            };

            myconfig.ServiceProvider.Endpoints.AddRange(new[] {
                new ServiceProviderEndpoint(EndpointType.SignOn, "/core/saml2/login", "/core"),
                new ServiceProviderEndpoint(EndpointType.Logout, "/core/saml2/logout", "/core"),
                new ServiceProviderEndpoint(EndpointType.Metadata, "/core/saml2/metadata")
            });
            var idpSource = new IdentityProviders();

            idpSource.AddByMetadataDirectory("..\\..\\Metadata");
            //myconfig.IdentityProviders.AddByMetadataUrl(new Uri("https://tas.fhict.nl/identity/saml2/metadata"));
            idpSource.First().OmitAssertionSignatureCheck = true;
            myconfig.IdentityProvidersSource = idpSource;
            myconfig.LoggingFactoryType      = "SAML2.Logging.DebugLoggerFactory";
            return(myconfig);
        }
예제 #6
0
        /// <summary>
        /// Gets friends from a third-party identity provider. The definition of a "friend" depends on the third-party provider.,
        /// "Friends" are friends (for Facebook), following users (for Twitter), and contacts (for Google and Microsoft).
        /// The access token must have the appropriate scopes to retrieve the list of friends.
        /// </summary>
        /// <param name="idProvider">third party identity provider</param>
        /// <param name="userAccessToken">user access token</param>
        /// <returns>the user profile</returns>
        public async Task <List <UserProfile> > GetFriends(IdentityProviders idProvider, string userAccessToken)
        {
            // data field is a list of Facebook profiles. Convert them to our own user profiles
            List <UserProfile> userProfileList = new List <UserProfile>();

            // We only support Facebook for now
            switch (idProvider)
            {
            case IdentityProviders.Facebook:
                List <FacebookProfile> fbProfiles = await this.GetFacebookFriendsImplicitFlow(userAccessToken);

                foreach (var p in fbProfiles)
                {
                    var userProfile = new UserProfile
                    {
                        IdProvider      = idProvider,
                        FacebookProfile = p
                    };

                    userProfileList.Add(userProfile);
                }

                return(userProfileList);
            }

            throw new OAuthException(OAuthErrors.NotImplemented_501);
        }
예제 #7
0
        /// <summary>
        /// Implements the <c>OAuth</c> Authorization Code Flow. In this flow, a user authorizes the IdentityProvider to issue a code. The client receives the code
        /// and can exchange it for an access token. The access token is used to fetch the user profile.
        /// This flow is more secure the Implicit flow. This is because only the client can exchange the code for the access token. This is done by the library
        /// when called by the client (this should be done only by clients running as servers or in the cloud). It should not be done by clients running
        /// on user's mobile devices or browsers.
        /// </summary>
        /// <param name="idProvider">third party identity provider</param>
        /// <param name="userCode">user code</param>
        /// <param name="clientId">client application ID</param>
        /// <param name="clientSecret">client application secret</param>
        /// <param name="clientRedirectURI">client redirection URI; must not be null in case of Google</param>
        /// <param name="userRequestToken">request token; must not be null in case of Twitter</param>
        /// <returns>the user profile</returns>
        public async Task <UserProfile> AuthorizationCodeFlow(IdentityProviders idProvider, string userCode, string clientId, string clientSecret, string clientRedirectURI = null, string userRequestToken = null)
        {
            switch (idProvider)
            {
            case IdentityProviders.Google:
                if (string.IsNullOrEmpty(clientRedirectURI))
                {
                    // clientRedirectURI must be present
                    throw new OAuthException(OAuthErrors.BadRequest_400);
                }

                return(new UserProfile
                {
                    IdProvider = IdentityProviders.Google,
                    GoogleProfile = await this.GoogleAuthorizationCodeFlow(userCode, clientId, clientSecret, clientRedirectURI),
                });

            case IdentityProviders.Twitter:
                if (string.IsNullOrEmpty(userRequestToken))
                {
                    // userRequestToken must be present
                    throw new OAuthException(OAuthErrors.BadRequest_400);
                }

                return(new UserProfile
                {
                    IdProvider = IdentityProviders.Twitter,
                    TwitterProfile = await this.TwitterAuthorizationCodeFlow(userRequestToken, userCode, clientId, clientSecret),
                });
            }

            throw new OAuthException(OAuthErrors.NotImplemented_501);
        }
예제 #8
0
 internal AuthConfigData(ResourceIdentifier id, string name, ResourceType resourceType, SystemData systemData, AuthPlatform platform, GlobalValidation globalValidation, IdentityProviders identityProviders, ContainerAppLogin login, HttpSettings httpSettings) : base(id, name, resourceType, systemData)
 {
     Platform          = platform;
     GlobalValidation  = globalValidation;
     IdentityProviders = identityProviders;
     Login             = login;
     HttpSettings      = httpSettings;
 }
 private IdentityProviders ToIdentityProviders(IEnumerable<IdentityProvider> providers, IdentityProviderCollection config)
 {
     var idps = new IdentityProviders(providers)
     {
         Encodings = config.Encodings,
         SelectionUrl = config.SelectionUrl
     };
     idps.AddByMetadataDirectory(config.MetadataLocation);
     return idps;
 }
예제 #10
0
        private IdentityProviders ToIdentityProviders(IEnumerable <IdentityProvider> providers, IdentityProviderCollection config)
        {
            var idps = new IdentityProviders(providers)
            {
                Encodings    = config.Encodings,
                SelectionUrl = config.SelectionUrl
            };

            idps.AddByMetadataDirectory(config.MetadataLocation);
            return(idps);
        }
예제 #11
0
        /// <summary>
        /// Obtains third party request tokens. This code needs more revising.
        /// </summary>
        /// <param name="idProvider">external identity provider</param>
        /// <param name="clientId">the id of the application on behalf of which the request token is requested</param>
        /// <param name="clientSecret">the secret of the application on behalf of which the request token is requested</param>
        /// <param name="clientRedirectURI">the <c>oauth</c> callback of the application on behalf of which the request token is requested</param>
        /// <returns>request token</returns>
        public async Task <string> GetRequestToken(IdentityProviders idProvider, string clientId, string clientSecret, string clientRedirectURI)
        {
            // Implements simple dispatcher to private functions depending on the third party provider
            switch (idProvider)
            {
            case IdentityProviders.Twitter:
            {
                return(await this.GetTwitterRequestToken(clientId, clientSecret, clientRedirectURI));
            }
            }

            throw new OAuthException(OAuthErrors.NotImplemented_501);
        }
예제 #12
0
            //[ExpectedException(typeof(Saml20Exception), ExpectedMessage = "Assertion is no longer valid.")]
            public void CanDecryptFOBSAssertion()
            {
                // Arrange
                var doc           = AssertionUtil.LoadBase64EncodedXmlDocument(@"Assertions\fobs-assertion2");
                var encryptedList = doc.GetElementsByTagName(EncryptedAssertion.ElementName, Saml20Constants.Assertion);

                // Do some mock configuration.
                var idpSource = new IdentityProviders();
                var config    = new Saml2Configuration
                {
                    AllowedAudienceUris     = new System.Collections.Generic.List <Uri>(),
                    IdentityProvidersSource = idpSource
                };

                config.AllowedAudienceUris.Add(new Uri("https://saml.safewhere.net"));
                idpSource.AddByMetadataDirectory(@"Protocol\MetadataDocs\FOBS"); // Set it manually.

                var cert = new X509Certificate2(@"Certificates\SafewhereTest_SFS.pfx", "test1234");
                var encryptedAssertion = new Saml20EncryptedAssertion((RSA)cert.PrivateKey);

                encryptedAssertion.LoadXml((XmlElement)encryptedList[0]);

                // Act
                encryptedAssertion.Decrypt();

                // Retrieve metadata
                var assertion = new Saml20Assertion(encryptedAssertion.Assertion.DocumentElement, null, false, TestConfiguration.Configuration);
                var endp      = config.IdentityProvidersSource.GetById(assertion.Issuer);

                // Assert
                Assert.That(encryptedList.Count == 1);
                Assert.IsNotNull(endp, "Endpoint not found");
                Assert.IsNotNull(endp.Metadata, "Metadata not found");

                try
                {
                    assertion.CheckValid(AssertionUtil.GetTrustedSigners(assertion.Issuer));
                    Assert.Fail("Verification should fail. Token does not include its signing key.");
                }
                catch (InvalidOperationException)
                {
                }

                Assert.IsNull(assertion.SigningKey, "Signing key is already present on assertion. Modify test.");
                //Assert.IsTrue("We have tested this next test" == "");
                //Assert.That(assertion.CheckSignature(Saml20SignonHandler.GetTrustedSigners(endp.Metadata.GetKeys(KeyTypes.Signing), endp)));
                //Assert.IsNotNull(assertion.SigningKey, "Signing key was not set on assertion instance.");
            }
예제 #13
0
        public IEnumerable <ValidationResult> Validate(ValidationContext validationContext)
        {
            foreach (var application in Applications)
            {
                var name = application.IdentityProviderBinding.Name;
                if (!IdentityProviders.Any(i => i.Name == name))
                {
                    yield return(new ValidationResult($"No identity provider with name '{name}' configured.", new string[] { nameof(Applications) }));
                }

                if (application.AuthenticatorBindings != null)
                {
                    foreach (var authBinding in application.AuthenticatorBindings)
                    {
                        var authName = authBinding.Name;
                        if (Authenticators is null || !Authenticators.Any(a => a.Name == authName))
                        {
                            yield return(new ValidationResult($"No authenticator with name '{authName}' configured.", new string[] { nameof(Applications) }));
                        }
                    }
                }
            }
        }
예제 #14
0
        public Saml2Configuration TestEnvironmentConfiguration()
        {
            var myconfig = new Saml2Configuration
            {
                ServiceProvider = new ServiceProvider
                {
                    SigningCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2("../../Metadata-Test/certificate.pfx", "", System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.MachineKeySet),
                    Server             = "http://localhost:7777/identity",
                    Id = "http://localhost:7777/identity"
                }
            };

            myconfig.ServiceProvider.Endpoints.AddRange(new[] {
                new ServiceProviderEndpoint(EndpointType.SignOn, "/identity/login", "/identity", BindingType.Redirect),
                new ServiceProviderEndpoint(EndpointType.Logout, "/identity/logout", "/identity", BindingType.Redirect),
                new ServiceProviderEndpoint(EndpointType.Metadata, "/identity/metadata")
            });
            var idpSource = new IdentityProviders();

            idpSource.AddByMetadata("..\\..\\Metadata-Test\\uat.xml");
            myconfig.IdentityProvidersSource           = idpSource;
            SAML2.Logging.LoggerProvider.Configuration = myconfig;
            return(myconfig);
        }
예제 #15
0
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAutoMapper(typeof(Startup));
            services.AddRazorPages();
            services.AddDbContext <PostgresDbContext>(
                options => options.UseNpgsql(
                    this.Configuration.GetConnectionString("PostgreSQL")
                    ),
                ServiceLifetime.Transient);
            var identityConfig = new IdentityProviders();

            this.Configuration.GetSection(nameof(IdentityProviders)).Bind(identityConfig);
            services.AddAuthentication(o =>
            {
                o.DefaultScheme       = IdentityConstants.ApplicationScheme;
                o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
            })
            .AddIdentityProviders(identityConfig)
            .AddIdentityCookies();

            services.AddIdentityCore <ApplicationUser>(o =>
            {
                o.Stores.MaxLengthForKeys        = 128;
                o.SignIn.RequireConfirmedAccount = false;
            })
            .AddDefaultUI()
            .AddDefaultTokenProviders()
            .AddRoles <IdentityRole>()
            .AddRoleManager <RoleManager <IdentityRole> >()
            .AddEntityFrameworkStores <PostgresDbContext>();

            services.AddServerSideBlazor() // TODO change to client-side
            .AddCircuitOptions(options =>
            {
                if (this.env.IsDevelopment())
                {
                    options.DetailedErrors = true;
                }
            });
            services.AddWebOptimizer(pipeline =>
            {
                pipeline.AddBundle(
                    "/css/style.css",
                    "text/css; charset=UTF-8",
                    "/wwwroot/scss/style.scss")
                .UseContentRoot()
                .CompileScss()
                .Concatenate()
                .FingerprintUrls()
                .AddResponseHeader("X-Content-Type-Options", "nosniff")
                .MinifyCss();
            });

            services.AddTransient <BlazorHelper>();
            services.AddTransient <BlogPostService>();
            services.AddTransient <CategoryService>();

            // TODO Move to Api project
            services.AddTransient <CategoryController>();
            services.AddTransient <BlogPostController>();
            services.AddTransient <IRepository <BlogPostEntity>, Repository <BlogPostEntity> >();
            services.AddTransient <ICategoryRepository, CategoryRepository>();

            services.AddScoped <HeadState>();
        }
예제 #16
0
        internal static AuthConfigData DeserializeAuthConfigData(JsonElement element)
        {
            ResourceIdentifier           id                = default;
            string                       name              = default;
            ResourceType                 type              = default;
            SystemData                   systemData        = default;
            Optional <AuthPlatform>      platform          = default;
            Optional <GlobalValidation>  globalValidation  = default;
            Optional <IdentityProviders> identityProviders = default;
            Optional <ContainerAppLogin> login             = default;
            Optional <HttpSettings>      httpSettings      = default;

            foreach (var property in element.EnumerateObject())
            {
                if (property.NameEquals("id"))
                {
                    id = new ResourceIdentifier(property.Value.GetString());
                    continue;
                }
                if (property.NameEquals("name"))
                {
                    name = property.Value.GetString();
                    continue;
                }
                if (property.NameEquals("type"))
                {
                    type = property.Value.GetString();
                    continue;
                }
                if (property.NameEquals("systemData"))
                {
                    systemData = JsonSerializer.Deserialize <SystemData>(property.Value.ToString());
                    continue;
                }
                if (property.NameEquals("properties"))
                {
                    if (property.Value.ValueKind == JsonValueKind.Null)
                    {
                        property.ThrowNonNullablePropertyIsNull();
                        continue;
                    }
                    foreach (var property0 in property.Value.EnumerateObject())
                    {
                        if (property0.NameEquals("platform"))
                        {
                            if (property0.Value.ValueKind == JsonValueKind.Null)
                            {
                                property0.ThrowNonNullablePropertyIsNull();
                                continue;
                            }
                            platform = AuthPlatform.DeserializeAuthPlatform(property0.Value);
                            continue;
                        }
                        if (property0.NameEquals("globalValidation"))
                        {
                            if (property0.Value.ValueKind == JsonValueKind.Null)
                            {
                                property0.ThrowNonNullablePropertyIsNull();
                                continue;
                            }
                            globalValidation = GlobalValidation.DeserializeGlobalValidation(property0.Value);
                            continue;
                        }
                        if (property0.NameEquals("identityProviders"))
                        {
                            if (property0.Value.ValueKind == JsonValueKind.Null)
                            {
                                property0.ThrowNonNullablePropertyIsNull();
                                continue;
                            }
                            identityProviders = IdentityProviders.DeserializeIdentityProviders(property0.Value);
                            continue;
                        }
                        if (property0.NameEquals("login"))
                        {
                            if (property0.Value.ValueKind == JsonValueKind.Null)
                            {
                                property0.ThrowNonNullablePropertyIsNull();
                                continue;
                            }
                            login = ContainerAppLogin.DeserializeContainerAppLogin(property0.Value);
                            continue;
                        }
                        if (property0.NameEquals("httpSettings"))
                        {
                            if (property0.Value.ValueKind == JsonValueKind.Null)
                            {
                                property0.ThrowNonNullablePropertyIsNull();
                                continue;
                            }
                            httpSettings = HttpSettings.DeserializeHttpSettings(property0.Value);
                            continue;
                        }
                    }
                    continue;
                }
            }
            return(new AuthConfigData(id, name, type, systemData, platform.Value, globalValidation.Value, identityProviders.Value, login.Value, httpSettings.Value));
        }
예제 #17
0
        public static AuthenticationBuilder AddIdentityProviders(this AuthenticationBuilder authenticationBuilder, IdentityProviders config)
        {
            if (config.Facebook != null)
            {
                authenticationBuilder.AddFacebook(options =>
                {
                    options.AppId     = config.Facebook.AppId;
                    options.AppSecret = config.Facebook.AppSecret;
                });
            }

            if (config.Google != null)
            {
                authenticationBuilder.AddGoogle(options =>
                {
                    options.ClientId     = config.Google.ClientId;
                    options.ClientSecret = config.Google.ClientSecret;
                });
            }

            if (config.Twitter != null)
            {
                authenticationBuilder.AddTwitter(options =>
                {
                    options.ConsumerKey    = config.Twitter.ApiKey;
                    options.ConsumerSecret = config.Twitter.ApiSecret;
                });
            }

            return(authenticationBuilder);
        }
예제 #18
0
 public Saml2Configuration()
 {
     IdentityProviders = new IdentityProviders();
     AllowedAudienceUris = new List<System.Uri>();
     Metadata = new Metadata();
 }
예제 #19
0
 /// <summary>
 /// Invokes the IDP selection event handler.
 /// </summary>
 /// <param name="endpoints">The endpoints.</param>
 /// <returns>The <see cref="IdentityProvider"/>.</returns>
 public static IdentityProvider InvokeIDPSelectionEventHandler(IdentityProviders endpoints)
 {
     return(IdpSelectionEvent != null?IdpSelectionEvent(endpoints) : null);
 }
예제 #20
0
 /// <summary>
 /// Invokes the IDP selection event handler.
 /// </summary>
 /// <param name="endpoints">The endpoints.</param>
 /// <returns>The <see cref="IdentityProvider"/>.</returns>
 public static IdentityProvider InvokeIDPSelectionEventHandler(IdentityProviders endpoints)
 {
     return IdpSelectionEvent != null ? IdpSelectionEvent(endpoints) : null;
 }