From f5ccfa5fdf7376d0b215418c56784ca420f9f617 Mon Sep 17 00:00:00 2001 From: jasondaicoder Date: Fri, 9 Dec 2016 23:57:58 +1100 Subject: [PATCH] added provider for wso2 (#194) * added provider for wso2 * corrected some wrong comments * fixed wrong pacakges reference path issues * Provided enough comments for the extension methods --- OwinOAuthProviders.sln | 6 + .../App_Start/Startup.Auth.cs | 431 +++++++++--------- src/Owin.Security.Providers.WSO2/Constants.cs | 8 + .../Owin.Security.Providers.WSO2.csproj | 92 ++++ .../Properties/AssemblyInfo.cs | 36 ++ .../Provider/IWSO2AuthenticationProvider.cs | 17 + .../Provider/WSO2ApplyRedirectContext.cs | 39 ++ .../Provider/WSO2AuthenticatedContext.cs | 48 ++ .../Provider/WSO2AuthenticationProvider.cs | 55 +++ .../Provider/WSO2ReturnEndPointContext.cs | 23 + .../WSO2AuthenticationExtensions.cs | 46 ++ .../WSO2AuthenticationHandler.cs | 245 ++++++++++ .../WSO2AuthenticationMiddleware.cs | 71 +++ .../WSO2AuthenticationOptions.cs | 90 ++++ .../packages.config | 7 + 15 files changed, 1000 insertions(+), 214 deletions(-) create mode 100644 src/Owin.Security.Providers.WSO2/Constants.cs create mode 100644 src/Owin.Security.Providers.WSO2/Owin.Security.Providers.WSO2.csproj create mode 100644 src/Owin.Security.Providers.WSO2/Properties/AssemblyInfo.cs create mode 100644 src/Owin.Security.Providers.WSO2/Provider/IWSO2AuthenticationProvider.cs create mode 100644 src/Owin.Security.Providers.WSO2/Provider/WSO2ApplyRedirectContext.cs create mode 100644 src/Owin.Security.Providers.WSO2/Provider/WSO2AuthenticatedContext.cs create mode 100644 src/Owin.Security.Providers.WSO2/Provider/WSO2AuthenticationProvider.cs create mode 100644 src/Owin.Security.Providers.WSO2/Provider/WSO2ReturnEndPointContext.cs create mode 100644 src/Owin.Security.Providers.WSO2/WSO2AuthenticationExtensions.cs create mode 100644 src/Owin.Security.Providers.WSO2/WSO2AuthenticationHandler.cs create mode 100644 src/Owin.Security.Providers.WSO2/WSO2AuthenticationMiddleware.cs create mode 100644 src/Owin.Security.Providers.WSO2/WSO2AuthenticationOptions.cs create mode 100644 src/Owin.Security.Providers.WSO2/packages.config diff --git a/OwinOAuthProviders.sln b/OwinOAuthProviders.sln index 804f7d7..eb63188 100644 --- a/OwinOAuthProviders.sln +++ b/OwinOAuthProviders.sln @@ -104,6 +104,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Box EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Baidu", "src\Owin.Security.Providers.Baidu\Owin.Security.Providers.Baidu.csproj", "{E2759807-4D7C-4288-AAC8-F5B7B4616680}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.WSO2", "src\Owin.Security.Providers.WSO2\Owin.Security.Providers.WSO2.csproj", "{8FD3A9CB-E684-42C0-A8BF-7746FDD3D43C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -314,6 +316,10 @@ Global {E2759807-4D7C-4288-AAC8-F5B7B4616680}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2759807-4D7C-4288-AAC8-F5B7B4616680}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2759807-4D7C-4288-AAC8-F5B7B4616680}.Release|Any CPU.Build.0 = Release|Any CPU + {8FD3A9CB-E684-42C0-A8BF-7746FDD3D43C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8FD3A9CB-E684-42C0-A8BF-7746FDD3D43C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FD3A9CB-E684-42C0-A8BF-7746FDD3D43C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8FD3A9CB-E684-42C0-A8BF-7746FDD3D43C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs index f2d68ae..f209dd4 100755 --- a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs +++ b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs @@ -18,274 +18,277 @@ namespace OwinOAuthProvidersDemo }); // Use a cookie to temporarily store information about a user logging in with a third party login provider app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); - //app.UseDeviantArtAuthentication("id", "secret"); - //app.UseUntappdAuthentication("id", "secret"); - // Uncomment the following lines to enable logging in with third party login providers - //app.UseMicrosoftAccountAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseDeviantArtAuthentication("id", "secret"); + //app.UseUntappdAuthentication("id", "secret"); + // Uncomment the following lines to enable logging in with third party login providers + //app.UseMicrosoftAccountAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UseTwitterAuthentication( - // consumerKey: "", - // consumerSecret: ""); + //app.UseTwitterAuthentication( + // consumerKey: "", + // consumerSecret: ""); - //app.UseFacebookAuthentication( - // appId: "", - // appSecret: ""); + //app.UseFacebookAuthentication( + // appId: "", + // appSecret: ""); - //app.UseGoogleAuthentication(); + //app.UseGoogleAuthentication(); - //app.UseLinkedInAuthentication("", ""); + //app.UseLinkedInAuthentication("", ""); - //app.UseYahooAuthentication("", ""); + //app.UseYahooAuthentication("", ""); - //app.UseTripItAuthentication("", ""); + //app.UseTripItAuthentication("", ""); - //app.UseGitHubAuthentication("", ""); + //app.UseGitHubAuthentication("", ""); - //app.UseBufferAuthentication("", ""); + //app.UseBufferAuthentication("", ""); - //app.UseRedditAuthentication("", ""); + //app.UseRedditAuthentication("", ""); - //app.UseStackExchangeAuthentication( - // clientId: "", - // clientSecret: "", - // key: ""); + //app.UseStackExchangeAuthentication( + // clientId: "", + // clientSecret: "", + // key: ""); - //app.UseInstagramInAuthentication("", ""); + //app.UseInstagramInAuthentication("", ""); - //var options = new GooglePlusAuthenticationOptions - //{ - // ClientId = "", - // ClientSecret = "", - // RequestOfflineAccess = true, - // Provider = new GooglePlusAuthenticationProvider - // { - // OnAuthenticated = async context => System.Diagnostics.Debug.WriteLine(String.Format("Refresh Token: {0}", context.RefreshToken)) - // } - //}; - //options.MomentTypes.Add("http://schemas.google.com/AddActivity"); - //options.MomentTypes.Add("http://schemas.google.com/CheckInActivity"); - //options.MomentTypes.Add("http://schemas.google.com/BuyActivity"); - //app.UseGooglePlusAuthentication(options); + //var options = new GooglePlusAuthenticationOptions + //{ + // ClientId = "", + // ClientSecret = "", + // RequestOfflineAccess = true, + // Provider = new GooglePlusAuthenticationProvider + // { + // OnAuthenticated = async context => System.Diagnostics.Debug.WriteLine(String.Format("Refresh Token: {0}", context.RefreshToken)) + // } + //}; + //options.MomentTypes.Add("http://schemas.google.com/AddActivity"); + //options.MomentTypes.Add("http://schemas.google.com/CheckInActivity"); + //options.MomentTypes.Add("http://schemas.google.com/BuyActivity"); + //app.UseGooglePlusAuthentication(options); - /* + /* * Twitch sign-ins use /signin-Twitch as the URL for authentication * */ - ////Simple Twitch Sign-in - //app.UseTwitchAuthentication("", ""); + ////Simple Twitch Sign-in + //app.UseTwitchAuthentication("", ""); - ////More complex Twitch Sign-in - //var opt = new TwitchAuthenticationOptions() - //{ - // ClientId = "", - // ClientSecret = "", - // Provider = new TwitchAuthenticationProvider() - // { - // OnAuthenticated = async z => - // { - //// Getting the twitch users picture - // z.Identity.AddClaim(new Claim("Picture", z.User.GetValue("logo").ToString())); - // } - //// You should be able to access these claims with HttpContext.GetOwinContext().Authentication.GetExternalLoginInfoAsync().Claims in your Account Controller - // // Commonly used in the ExternalLoginCallback() in AccountController.cs - // /* + ////More complex Twitch Sign-in + //var opt = new TwitchAuthenticationOptions() + //{ + // ClientId = "", + // ClientSecret = "", + // Provider = new TwitchAuthenticationProvider() + // { + // OnAuthenticated = async z => + // { + //// Getting the twitch users picture + // z.Identity.AddClaim(new Claim("Picture", z.User.GetValue("logo").ToString())); + // } + //// You should be able to access these claims with HttpContext.GetOwinContext().Authentication.GetExternalLoginInfoAsync().Claims in your Account Controller + // // Commonly used in the ExternalLoginCallback() in AccountController.cs + // /* - // if (user != null) - // { - // var claim = (await AuthenticationManager.GetExternalLoginInfoAsync()).ExternalIdentity.Claims.First( - // a => a.Type == "Picture"); - // user.Claims.Add(new IdentityUserClaim() { ClaimType = claim.Type, ClaimValue = claim.Value }); - // await SignInAsync(user, isPersistent: false); - // return RedirectToLocal(returnUrl); - // } - // */ - // } - //}; - //app.UseTwitchAuthentication(opt); + // if (user != null) + // { + // var claim = (await AuthenticationManager.GetExternalLoginInfoAsync()).ExternalIdentity.Claims.First( + // a => a.Type == "Picture"); + // user.Claims.Add(new IdentityUserClaim() { ClaimType = claim.Type, ClaimValue = claim.Value }); + // await SignInAsync(user, isPersistent: false); + // return RedirectToLocal(returnUrl); + // } + // */ + // } + //}; + //app.UseTwitchAuthentication(opt); - //app.UseOpenIDAuthentication("http://me.yahoo.com/", "Yahoo"); + //app.UseOpenIDAuthentication("http://me.yahoo.com/", "Yahoo"); - //app.UseOpenIDAuthentication("https://openid.stackexchange.com/", "StackExchange"); + //app.UseOpenIDAuthentication("https://openid.stackexchange.com/", "StackExchange"); - //app.UseOpenIDAuthentication("https://www.google.com/accounts/o8/id", "Google"); + //app.UseOpenIDAuthentication("https://www.google.com/accounts/o8/id", "Google"); - //app.UseSteamAuthentication(applicationKey: ""); + //app.UseSteamAuthentication(applicationKey: ""); - //app.UseOpenIDAuthentication("http://orange.fr", "Orange"); - // Use OpenId provider login uri instead of discovery uri - //app.UseOpenIDAuthentication("http://openid.orange.fr/server", "Orange", true); + //app.UseOpenIDAuthentication("http://orange.fr", "Orange"); + // Use OpenId provider login uri instead of discovery uri + //app.UseOpenIDAuthentication("http://openid.orange.fr/server", "Orange", true); - //app.UseSalesforceAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseSalesforceAuthentication( + // clientId: "", + // clientSecret: ""); - //in scenarios where a sandbox URL needs to be used - //var salesforceOptions = new SalesforceAuthenticationOptions - //{ - // Endpoints = - // new SalesforceAuthenticationOptions.SalesforceAuthenticationEndpoints - // { - // AuthorizationEndpoint = - // "https://ap1.salesforce.com/services/oauth2/authorize", - // TokenEndpoint = "https://ap1.salesforce.com/services/oauth2/token" - // }, - // ClientId = "", - // ClientSecret = "", - // Provider = new SalesforceAuthenticationProvider() - // { - // OnAuthenticated = async context => - // { - // System.Diagnostics.Debug.WriteLine(context.AccessToken); - // System.Diagnostics.Debug.WriteLine(context.RefreshToken); - // System.Diagnostics.Debug.WriteLine(context.OrganizationId); - // } - // } - //}; - //app.UseSalesforceAuthentication(salesforceOptions); + //in scenarios where a sandbox URL needs to be used + //var salesforceOptions = new SalesforceAuthenticationOptions + //{ + // Endpoints = + // new SalesforceAuthenticationOptions.SalesforceAuthenticationEndpoints + // { + // AuthorizationEndpoint = + // "https://ap1.salesforce.com/services/oauth2/authorize", + // TokenEndpoint = "https://ap1.salesforce.com/services/oauth2/token" + // }, + // ClientId = "", + // ClientSecret = "", + // Provider = new SalesforceAuthenticationProvider() + // { + // OnAuthenticated = async context => + // { + // System.Diagnostics.Debug.WriteLine(context.AccessToken); + // System.Diagnostics.Debug.WriteLine(context.RefreshToken); + // System.Diagnostics.Debug.WriteLine(context.OrganizationId); + // } + // } + //}; + //app.UseSalesforceAuthentication(salesforceOptions); - ////app.UseShopifyAuthentication("", ""); + ////app.UseShopifyAuthentication("", ""); - //app.UseArcGISOnlineAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseArcGISOnlineAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UseWordPressAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseWordPressAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UseDropboxAuthentication( - // appKey: "", - // appSecret: ""); + //app.UseDropboxAuthentication( + // appKey: "", + // appSecret: ""); - //app.UseHealthGraphAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseHealthGraphAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UseBoxAuthentication( - // appKey: "", - // appSecret: ""); + //app.UseBoxAuthentication( + // appKey: "", + // appSecret: ""); - //app.UseBaiduAuthentication( - // apiKey: "", - // secretKey: ""); + //app.UseBaiduAuthentication( + // apiKey: "", + // secretKey: ""); - //app.UseBattleNetAuthentication(new BattleNetAuthenticationOptions - //{ - // ClientId = "", - // ClientSecret = "" - //}); - //app.UseBattleNetAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseBattleNetAuthentication(new BattleNetAuthenticationOptions + //{ + // ClientId = "", + // ClientSecret = "" + //}); + //app.UseBattleNetAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UseAsanaAuthentication("", ""); + //app.UseAsanaAuthentication("", ""); - //app.UseEveOnlineAuthentication("", ""); + //app.UseEveOnlineAuthentication("", ""); - //app.UseSoundCloudAuthentication("", ""); + //app.UseSoundCloudAuthentication("", ""); - //app.UseFoursquareAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseFoursquareAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UsePayPalAuthentication( - // clientId: "", - // clientSecret: "", - // isSandbox: false); + //app.UsePayPalAuthentication( + // clientId: "", + // clientSecret: "", + // isSandbox: false); - //app.UseWargamingAccountAuthentication("", WargamingAuthenticationOptions.Region.NorthAmerica); + //app.UseWargamingAccountAuthentication("", WargamingAuthenticationOptions.Region.NorthAmerica); - //app.UseFlickrAuthentication("", ""); - //app.UseVisualStudioAuthentication( - // appId: "", - // appSecret: ""); + //app.UseFlickrAuthentication("", ""); + //app.UseVisualStudioAuthentication( + // appId: "", + // appSecret: ""); - //app.UseSpotifyAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseSpotifyAuthentication( + // clientId: "", + // clientSecret: ""); - //var options = new SlackAuthenticationOptions - //{ - // ClientId = "", - // ClientSecret = "", - // TeamId = "" // optional - //}; - //options.Scope.Add("identify"); - //app.UseSlackAuthentication(options); + //var options = new SlackAuthenticationOptions + //{ + // ClientId = "", + // ClientSecret = "", + // TeamId = "" // optional + //}; + //options.Scope.Add("identify"); + //app.UseSlackAuthentication(options); - //app.UseGitterAuthentication( - // clientId: "", - // clientSecret: "" - //); + //app.UseGitterAuthentication( + // clientId: "", + // clientSecret: "" + //); - //app.UseImgurAuthentication( - // new ImgurAuthenticationOptions - // { - // ClientId = "", - // ClientSecret = "" - // }); + //app.UseImgurAuthentication( + // new ImgurAuthenticationOptions + // { + // ClientId = "", + // ClientSecret = "" + // }); - //var options = new BacklogAuthenticationOptions - //{ - // ClientId = "", - // ClientSecret = "", - // ContractName = "", - // CallbackPath = new PathString(""), // ex.new PathString("/OauthTokenRequest") - // Provider = new BacklogAuthenticationProvider - // { - // OnAuthenticated = async context => await System.Threading.Tasks.Task.Run(()=> { System.Diagnostics.Debug.WriteLine(String.Format("Refresh Token: {0}", context.RefreshToken)); }) - // } - //}; + //var options = new BacklogAuthenticationOptions + //{ + // ClientId = "", + // ClientSecret = "", + // ContractName = "", + // CallbackPath = new PathString(""), // ex.new PathString("/OauthTokenRequest") + // Provider = new BacklogAuthenticationProvider + // { + // OnAuthenticated = async context => await System.Threading.Tasks.Task.Run(()=> { System.Diagnostics.Debug.WriteLine(String.Format("Refresh Token: {0}", context.RefreshToken)); }) + // } + //}; - //app.UseBacklogAuthentication(options); + //app.UseBacklogAuthentication(options); - //var cosignOptions = new CosignAuthenticationOptions - //{ - // AuthenticationType = "Cosign", - // SignInAsAuthenticationType = signInAsType, - // CosignServer = "weblogin.umich.edu", - // CosignServicePort = 6663, - // IdentityServerHostInstance = "core1", - // ClientServer = "cosignservername" - //}; - //app.UseCosignAuthentication(cosignOptions); + //var cosignOptions = new CosignAuthenticationOptions + //{ + // AuthenticationType = "Cosign", + // SignInAsAuthenticationType = signInAsType, + // CosignServer = "weblogin.umich.edu", + // CosignServicePort = 6663, + // IdentityServerHostInstance = "core1", + // ClientServer = "cosignservername" + //}; + //app.UseCosignAuthentication(cosignOptions); - //app.UseVimeoAuthentication("", ""); + //app.UseVimeoAuthentication("", ""); - //app.UseFitbitAuthentication(new FitbitAuthenticationOptions - //{ - // ClientId = "", - // ClientSecret = "" - //}); + //app.UseFitbitAuthentication(new FitbitAuthenticationOptions + //{ + // ClientId = "", + // ClientSecret = "" + //}); - //app.UseOnshapeAuthentication( - // appKey: "", - // appSecret: ""); - // - // - //app.UseOnshapeAuthentication(new OnshapeAuthenticationOptions() - //{ - // AppKey = "", - // AppSecret = "", - // CallbackPath = new PathString("/oauthRedirect"), - // Hostname = "partner.dev.onshape.com" - //}); + //app.UseOnshapeAuthentication( + // appKey: "", + // appSecret: ""); + // + // + //app.UseOnshapeAuthentication(new OnshapeAuthenticationOptions() + //{ + // AppKey = "", + // AppSecret = "", + // CallbackPath = new PathString("/oauthRedirect"), + // Hostname = "partner.dev.onshape.com" + //}); - //app.UseVKontakteAuthentication("", ""); + //app.UseVKontakteAuthentication("", ""); - //app.UseXingAuthentication("", ""); + //app.UseXingAuthentication("", ""); - //app.UseDoYouBuzzAuthentication("", ""); - //app.("", ""); - //app.UseOrcidAuthentication("",""); + //app.UseDoYouBuzzAuthentication("", ""); + //app.("", ""); + //app.UseOrcidAuthentication("",""); - //app.UseDiscordAuthentication("", ""); - //app.UseGeniAuthentication("", ""); - //app.UseMyHeritageAuthentication("", ""); - } - } + //app.UseDiscordAuthentication("", ""); + //app.UseGeniAuthentication("", ""); + //app.UseMyHeritageAuthentication("", ""); + + //app.UseWSO2Authentication("", "", ""); + + } + } } \ No newline at end of file diff --git a/src/Owin.Security.Providers.WSO2/Constants.cs b/src/Owin.Security.Providers.WSO2/Constants.cs new file mode 100644 index 0000000..3873ee2 --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/Constants.cs @@ -0,0 +1,8 @@ +namespace Owin.Security.Providers.WSO2 +{ + internal static class Constants + { + public const string DefaultAuthenticationType = "WSO2"; + + } +} diff --git a/src/Owin.Security.Providers.WSO2/Owin.Security.Providers.WSO2.csproj b/src/Owin.Security.Providers.WSO2/Owin.Security.Providers.WSO2.csproj new file mode 100644 index 0000000..60281d9 --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/Owin.Security.Providers.WSO2.csproj @@ -0,0 +1,92 @@ + + + + + Debug + AnyCPU + {8FD3A9CB-E684-42C0-A8BF-7746FDD3D43C} + Library + Properties + OWin.Security.Providers.WSO2 + OWin.Security.Providers.WSO2 + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll + True + + + ..\..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll + True + + + ..\..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll + True + + + ..\..\packages\Owin.1.0\lib\net40\Owin.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(PostBuildEventDependsOn); + PostBuildMacros; + + + \ No newline at end of file diff --git a/src/Owin.Security.Providers.WSO2/Properties/AssemblyInfo.cs b/src/Owin.Security.Providers.WSO2/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1f1b4c3 --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the follOwing +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Owin.Security.Providers.WSO2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Owin.Security.Providers.WSO2")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The follOwing GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8fd3a9cb-e684-42c0-a8bf-7746fdd3d43c")] + +// Version information for an assembly consists of the follOwing four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Owin.Security.Providers.WSO2/Provider/IWSO2AuthenticationProvider.cs b/src/Owin.Security.Providers.WSO2/Provider/IWSO2AuthenticationProvider.cs new file mode 100644 index 0000000..8d359f8 --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/Provider/IWSO2AuthenticationProvider.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; + +namespace Owin.Security.Providers.WSO2 +{ + public interface IWSO2AuthenticationProvider + { + Task Authenticated(WSO2AuthenticatedContext context); + + Task ReturnEndpoint(WSO2ReturnEndpointContext context); + + /// + /// Called when a Challenge causes a redirect to authorize endpoint in the wso2 middleware + /// + /// Contains redirect URI and of the challenge + void ApplyRedirect(WSO2ApplyRedirectContext context); + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.WSO2/Provider/WSO2ApplyRedirectContext.cs b/src/Owin.Security.Providers.WSO2/Provider/WSO2ApplyRedirectContext.cs new file mode 100644 index 0000000..5be933e --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/Provider/WSO2ApplyRedirectContext.cs @@ -0,0 +1,39 @@ +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Provider; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Owin.Security.Providers.WSO2 +{ + public class WSO2ApplyRedirectContext : BaseContext + { + /// + /// Creates a new context object. + /// + /// The Owin request context + /// The wso2 middleware options + /// The authenticaiton properties of the challenge + /// The initial redirect URI + public WSO2ApplyRedirectContext(IOwinContext context, WSO2AuthenticationOptions options, + AuthenticationProperties properties, string redirectUri) + : base(context, options) + { + RedirectUri = redirectUri; + Properties = properties; + } + + /// + /// Gets the URI used for the redirect operation. + /// + public string RedirectUri { get; private set; } + + /// + /// Gets the authentication properties of the challenge + /// + public AuthenticationProperties Properties { get; private set; } + } +} diff --git a/src/Owin.Security.Providers.WSO2/Provider/WSO2AuthenticatedContext.cs b/src/Owin.Security.Providers.WSO2/Provider/WSO2AuthenticatedContext.cs new file mode 100644 index 0000000..9461cd5 --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/Provider/WSO2AuthenticatedContext.cs @@ -0,0 +1,48 @@ +using Microsoft.Owin.Security.Provider; +using System.Security.Claims; +using Microsoft.Owin; +using Newtonsoft.Json.Linq; +using Microsoft.Owin.Security; + +namespace Owin.Security.Providers.WSO2 +{ + public class WSO2AuthenticatedContext : BaseContext + { + public WSO2AuthenticatedContext(IOwinContext context, JObject user, string accessToken) + : base(context) + { + User = user; + AccessToken = accessToken; + + Id = TryGetValue(user, "sub"); + } + + /// + /// Gets the WSO2 user + /// + public JObject User { get; private set; } + + public string Id { get; private set;} + + /// + /// Gets the access token + /// + public string AccessToken { get; private set; } + + /// + /// Gets the representing the user + /// + public ClaimsIdentity Identity { get; set; } + + /// + /// Gets or sets a property bag for common authentication properties + /// + public AuthenticationProperties Properties { get; set; } + + private static string TryGetValue(JObject user, string propertyName) + { + JToken value; + return user.TryGetValue(propertyName, out value) ? value.ToString() : null; + } + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.WSO2/Provider/WSO2AuthenticationProvider.cs b/src/Owin.Security.Providers.WSO2/Provider/WSO2AuthenticationProvider.cs new file mode 100644 index 0000000..c014d8d --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/Provider/WSO2AuthenticationProvider.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading.Tasks; + +namespace Owin.Security.Providers.WSO2 +{ + public class WSO2AuthenticationProvider : IWSO2AuthenticationProvider + { + public WSO2AuthenticationProvider() + { + OnAuthenticated = context => Task.FromResult(null); + OnReturnEndpoint = context => Task.FromResult(null); + OnApplyRedirect = context => + context.Response.Redirect(context.RedirectUri); + } + + /// + /// Gets or sets the function that is invoked when the Authenticated method is invoked. + /// + public Func OnAuthenticated { get; set; } + + /// + /// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked. + /// + public Func OnReturnEndpoint { get; set; } + + /// + /// Gets or sets the delegate that is invoked when the ApplyRedirect method is invoked. + /// + public Action OnApplyRedirect { get; set; } + + /// + /// Invoked whenever it successfully authenticates a user + /// + /// Contains information about the login session as well as the user . + /// A representing the completed operation. + public virtual Task Authenticated(WSO2AuthenticatedContext context) { + return OnAuthenticated(context); + } + + /// + /// Invoked prior to the being saved in a local cookie and the browser being redirected to the originally requested URL. + /// + /// + /// A representing the completed operation. + public virtual Task ReturnEndpoint(WSO2ReturnEndpointContext context) { + return OnReturnEndpoint(context); + } + + public virtual void ApplyRedirect(WSO2ApplyRedirectContext context) + { + OnApplyRedirect(context); + } + + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.WSO2/Provider/WSO2ReturnEndPointContext.cs b/src/Owin.Security.Providers.WSO2/Provider/WSO2ReturnEndPointContext.cs new file mode 100644 index 0000000..005abaf --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/Provider/WSO2ReturnEndPointContext.cs @@ -0,0 +1,23 @@ +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Provider; + +namespace Owin.Security.Providers.WSO2 +{ + /// + /// Provides context information to middleware providers. + /// + public class WSO2ReturnEndpointContext : ReturnEndpointContext + { + /// + /// + /// + /// Owin environment + /// The authentication ticket + public WSO2ReturnEndpointContext( + IOwinContext context, + AuthenticationTicket ticket) + : base(context, ticket) { + } + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.WSO2/WSO2AuthenticationExtensions.cs b/src/Owin.Security.Providers.WSO2/WSO2AuthenticationExtensions.cs new file mode 100644 index 0000000..3970c0e --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/WSO2AuthenticationExtensions.cs @@ -0,0 +1,46 @@ +using System; + +namespace Owin.Security.Providers.WSO2 +{ + /// + /// Extension methods for using + /// + public static class WSO2AuthenticationExtensions + { + /// + /// Authenticate users using WSO2 OAuth 2.0 + /// + /// The passed to the configuration method + /// Middleware configuration options + /// The updated + public static IAppBuilder UseWSO2Authentication(this IAppBuilder app, WSO2AuthenticationOptions options) + { + if (app == null) + throw new ArgumentNullException(nameof(app)); + if (options == null) + throw new ArgumentNullException(nameof(options)); + + app.Use(typeof(WSO2AuthenticationMiddleware), app, options); + + return app; + } + + /// + /// Authenticate users using WSO2 OAuth 2.0 + /// + /// The passed to the configuration method + /// The WSO2 Identity Server base url, should be like https://localhost:9443/ + /// The WSO2 assigned client id + /// The WSO2 assigned client secret + /// The updated + public static IAppBuilder UseWSO2Authentication(this IAppBuilder app, string baseUrl, string clientId, string clientSecret) + { + return app.UseWSO2Authentication(new WSO2AuthenticationOptions + { + BaseUrl = baseUrl.TrimEnd('/') + "/", + ClientId = clientId, + ClientSecret = clientSecret + }); + } + } +} diff --git a/src/Owin.Security.Providers.WSO2/WSO2AuthenticationHandler.cs b/src/Owin.Security.Providers.WSO2/WSO2AuthenticationHandler.cs new file mode 100644 index 0000000..0f6c40b --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/WSO2AuthenticationHandler.cs @@ -0,0 +1,245 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.Owin.Logging; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Infrastructure; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Microsoft.Owin.Infrastructure; + +namespace Owin.Security.Providers.WSO2 +{ + internal class WSO2AuthenticationHandler : AuthenticationHandler + { + private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string"; + + private const string AuthorizeEndpoint = "oauth2/authorize"; + private const string TokenEndpoint = "oauth2/token"; + + private const string TokenRevocationEndpoint = "oauth2/revoke"; + + private const string UserInfoEndpoint = "oauth2/userinfo"; + + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + + public WSO2AuthenticationHandler(HttpClient httpClient, ILogger logger) + { + _httpClient = httpClient; + _logger = logger; + } + + protected override async Task AuthenticateCoreAsync() + { + AuthenticationProperties properties = null; + + try + { + _httpClient.DefaultRequestHeaders.Remove("Authorization"); + + string code = null; + string state = null; + + var query = Request.Query; + var values = query.GetValues("code"); + if (values != null && values.Count == 1) + { + code = values[0]; + } + values = query.GetValues("state"); + if (values != null && values.Count == 1) + { + state = values[0]; + } + + properties = Options.StateDataFormat.Unprotect(state); + if (properties == null) + { + return null; + } + + // OAuth2 10.12 CSRF + if (!ValidateCorrelationId(properties, _logger)) + { + return new AuthenticationTicket(null, properties); + } + + // Check for error + if (Request.Query.Get("error") != null) + return new AuthenticationTicket(null, properties); + + var requestPrefix = Request.Scheme + "://" + Request.Host; + var redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath; + + // Build up the body for the token request + var body = new List> + { + new KeyValuePair("grant_type", "authorization_code"), + new KeyValuePair("code", code), + new KeyValuePair("redirect_uri", redirectUri), + new KeyValuePair("client_id", Options.ClientId), + new KeyValuePair("client_secret", Options.ClientSecret) + }; + + // Request the token + var tokenResponse = + await _httpClient.PostAsync(Options.BaseUrl + TokenEndpoint, new FormUrlEncodedContent(body)); + tokenResponse.EnsureSuccessStatusCode(); + var text = await tokenResponse.Content.ReadAsStringAsync(); + + // Deserializes the token response + dynamic response = JsonConvert.DeserializeObject(text); + var accessToken = (string)response.access_token; + + // Get the WSO2 user + _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); + + var graphResponse = await _httpClient.GetAsync( + Options.BaseUrl + UserInfoEndpoint + "?schema=openid"); + graphResponse.EnsureSuccessStatusCode(); + text = await graphResponse.Content.ReadAsStringAsync(); + var user = JObject.Parse(text); + + var context = new WSO2AuthenticatedContext(Context, user, accessToken) + { + Identity = new ClaimsIdentity( + Options.AuthenticationType, + ClaimsIdentity.DefaultNameClaimType, + ClaimsIdentity.DefaultRoleClaimType) + }; + if (!string.IsNullOrEmpty(context.Id)) + { + context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id, XmlSchemaString, Options.AuthenticationType)); + } + + context.Properties = properties; + + await Options.Provider.Authenticated(context); + + return new AuthenticationTicket(context.Identity, context.Properties); + } + catch (Exception ex) + { + _logger.WriteError(ex.Message); + } + return new AuthenticationTicket(null, properties); + } + + protected override Task ApplyResponseChallengeAsync() + { + if (Response.StatusCode != 401) + { + return Task.FromResult(null); + } + + var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode); + + if (challenge == null) return Task.FromResult(null); + + var baseUri = + Request.Scheme + + Uri.SchemeDelimiter + + Request.Host + + Request.PathBase; + + var currentUri = + baseUri + + Request.Path + + Request.QueryString; + + var redirectUri = + baseUri + + Options.CallbackPath; + + var properties = challenge.Properties; + if (string.IsNullOrEmpty(properties.RedirectUri)) + { + properties.RedirectUri = currentUri; + } + + // OAuth2 10.12 CSRF + GenerateCorrelationId(properties); + + // hard code for now. + var scope = "openid email profile"; + + // allow scopes to be specified via the authentication properties for this request, when specified they will already be comma separated + if (properties.Dictionary.ContainsKey("scope")) + { + scope = properties.Dictionary["scope"]; + } + + var state = Options.StateDataFormat.Protect(properties); + + var authorizationEndpoint = + Options.BaseUrl + + AuthorizeEndpoint + + "?response_type=code" + + "&client_id=" + Uri.EscapeDataString(Options.ClientId) + + "&redirect_uri=" + Uri.EscapeDataString(redirectUri) + + "&scope=" + Uri.EscapeDataString(scope) + + "&state=" + Uri.EscapeDataString(state); + + + var redirectContext = new WSO2ApplyRedirectContext( + Context, Options, + properties, authorizationEndpoint); + Options.Provider.ApplyRedirect(redirectContext); + + return Task.FromResult(null); + } + + public override async Task InvokeAsync() + { + return await InvokeReplyPathAsync(); + } + + private async Task InvokeReplyPathAsync() + { + if (!Options.CallbackPath.HasValue || Options.CallbackPath != Request.Path) return false; + // TODO: error responses + + var ticket = await AuthenticateAsync(); + if (ticket == null) + { + _logger.WriteWarning("Invalid return state, unable to redirect."); + Response.StatusCode = 500; + return true; + } + + var context = new WSO2ReturnEndpointContext(Context, ticket) + { + SignInAsAuthenticationType = Options.SignInAsAuthenticationType, + RedirectUri = ticket.Properties.RedirectUri + }; + + await Options.Provider.ReturnEndpoint(context); + + if (context.SignInAsAuthenticationType != null && + context.Identity != null) + { + var grantIdentity = context.Identity; + if (!string.Equals(grantIdentity.AuthenticationType, context.SignInAsAuthenticationType, StringComparison.Ordinal)) + { + grantIdentity = new ClaimsIdentity(grantIdentity.Claims, context.SignInAsAuthenticationType, grantIdentity.NameClaimType, grantIdentity.RoleClaimType); + } + Context.Authentication.SignIn(context.Properties, grantIdentity); + } + + if (context.IsRequestCompleted || context.RedirectUri == null) return context.IsRequestCompleted; + var redirectUri = context.RedirectUri; + if (context.Identity == null) + { + // add a redirect hint that sign-in failed in some way + redirectUri = WebUtilities.AddQueryString(redirectUri, "error", "access_denied"); + } + Response.Redirect(redirectUri); + context.RequestCompleted(); + + return context.IsRequestCompleted; + } + } +} diff --git a/src/Owin.Security.Providers.WSO2/WSO2AuthenticationMiddleware.cs b/src/Owin.Security.Providers.WSO2/WSO2AuthenticationMiddleware.cs new file mode 100644 index 0000000..3d1542f --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/WSO2AuthenticationMiddleware.cs @@ -0,0 +1,71 @@ +using System; +using System.Net.Http; +using Microsoft.Owin; +using Microsoft.Owin.Logging; +using Microsoft.Owin.Security.DataHandler; +using Microsoft.Owin.Security.DataProtection; +using Microsoft.Owin.Security.Infrastructure; +using Owin; +using Microsoft.Owin.Security; + +namespace Owin.Security.Providers.WSO2 +{ + public class WSO2AuthenticationMiddleware : AuthenticationMiddleware + { + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + + public WSO2AuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, WSO2AuthenticationOptions options) : base(next, options) + { + if (string.IsNullOrWhiteSpace(Options.BaseUrl)) + throw new ArgumentException("Base url can not be null."); + if (string.IsNullOrWhiteSpace(Options.ClientId)) + throw new ArgumentException("Client id can not be null."); + if (string.IsNullOrWhiteSpace(Options.ClientSecret)) + throw new ArgumentException("Client secret can not be null."); + + _logger = app.CreateLogger(); + + if (Options.Provider == null) + Options.Provider = new WSO2AuthenticationProvider(); + + if (Options.StateDataFormat == null) + { + var dataProtector = app.CreateDataProtector( + typeof (WSO2AuthenticationMiddleware).FullName, + Options.AuthenticationType, "v1"); + Options.StateDataFormat = new PropertiesDataFormat(dataProtector); + } + + if (string.IsNullOrEmpty(Options.SignInAsAuthenticationType)) + Options.SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(); + + _httpClient = new HttpClient(ResolveHttpMessageHandler(Options)) + { + Timeout = Options.BackchannelTimeout, + MaxResponseContentBufferSize = 1024*1024*10 + }; + } + + protected override AuthenticationHandler CreateHandler() + { + return new WSO2AuthenticationHandler(_httpClient, _logger); + } + + private static HttpMessageHandler ResolveHttpMessageHandler(WSO2AuthenticationOptions options) + { + var handler = options.BackchannelHttpHandler ?? new WebRequestHandler(); + + // If they provided a validator, apply it or fail. + if (options.BackchannelCertificateValidator == null) return handler; + // Set the cert validate callback + var webRequestHandler = handler as WebRequestHandler; + if (webRequestHandler == null) + { + throw new InvalidOperationException("An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler."); + } + webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate; + + return handler; } + } +} diff --git a/src/Owin.Security.Providers.WSO2/WSO2AuthenticationOptions.cs b/src/Owin.Security.Providers.WSO2/WSO2AuthenticationOptions.cs new file mode 100644 index 0000000..dc83b57 --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/WSO2AuthenticationOptions.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Owin; +using Microsoft.Owin.Security; + +namespace Owin.Security.Providers.WSO2 +{ + public class WSO2AuthenticationOptions : AuthenticationOptions + { + public WSO2AuthenticationOptions() : base(Constants.DefaultAuthenticationType) + { + Caption = Constants.DefaultAuthenticationType; + CallbackPath = new PathString("/signin-wso2"); + AuthenticationMode = AuthenticationMode.Passive; + BackchannelTimeout = TimeSpan.FromSeconds(60); + } + + /// + /// Gets or sets the a pinned certificate validator to use to validate the endpoints used + /// in back channel communications belong to WSO2 + /// + /// + /// The pinned certificate validator. + /// + /// + /// If this property is null then the default certificate checks are performed, + /// validating the subject name and if the signing chain is a trusted party. + /// + public ICertificateValidator BackchannelCertificateValidator { get; set; } + + /// + /// The HttpMessageHandler used to communicate with WSO2. + /// This cannot be set at the same time as BackchannelCertificateValidator unless the value + /// can be downcast to a WebRequestHandler. + /// + public HttpMessageHandler BackchannelHttpHandler { get; set; } + + /// + /// Gets or sets timeout value in milliseconds for back channel communications with WSO2. + /// + /// + /// The back channel timeout in milliseconds. + /// + public TimeSpan BackchannelTimeout { get; set; } + + /// + /// Get or sets the text that the user can display on a sign in user interface. + /// + public string Caption + { + get { return Description.Caption; } + set { Description.Caption = value; } + } + + public string ClientId { get; set; } + + public string ClientSecret { get; set;} + + public string BaseUrl { get; set; } + + /// + /// The request path within the application's base path where the user-agent will be returned. + /// The middleware will process this request when it arrives. + /// Default value is "/signin-wso2". + /// + public PathString CallbackPath { get; set; } + + public IWSO2AuthenticationProvider Provider { get; set;} + + /// + /// Gets or sets the type used to secure data handled by the middleware. + /// + public ISecureDataFormat StateDataFormat { get; set; } + + /// + /// A list of permissions to request. + /// + public IList Scope { get; private set; } + + /// + /// Gets or sets the name of another authentication middleware which will be responsible for actually issuing a user + /// . + /// + public string SignInAsAuthenticationType { get; set; } + } +} diff --git a/src/Owin.Security.Providers.WSO2/packages.config b/src/Owin.Security.Providers.WSO2/packages.config new file mode 100644 index 0000000..cbfe6a2 --- /dev/null +++ b/src/Owin.Security.Providers.WSO2/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file