Shopify OAuth provider - OWIN integration / implementation

This commit is contained in:
Jaspalsinh Chauhan
2015-08-13 00:25:57 +05:30
parent 9ccb01fd84
commit 0600b9b06c
15 changed files with 723 additions and 11 deletions

View File

@@ -265,6 +265,15 @@
<Compile Include="Salesforce\SalesforceAuthenticationHandler.cs" />
<Compile Include="Salesforce\SalesforceAuthenticationMiddleware.cs" />
<Compile Include="Salesforce\SalesforceAuthenticationOptions.cs" />
<Compile Include="Shopify\Constants.cs" />
<Compile Include="Shopify\Provider\IShopifyAuthenticationProvider.cs" />
<Compile Include="Shopify\Provider\ShopifyAuthenticatedContext.cs" />
<Compile Include="Shopify\Provider\ShopifyAuthenticationProvider.cs" />
<Compile Include="Shopify\Provider\ShopifyReturnEndpointContext.cs" />
<Compile Include="Shopify\ShopifyAuthenticationExtensions.cs" />
<Compile Include="Shopify\ShopifyAuthenticationHandler.cs" />
<Compile Include="Shopify\ShopifyAuthenticationMiddleware.cs" />
<Compile Include="Shopify\ShopifyAuthenticationOptions.cs" />
<Compile Include="Slack\Constants.cs" />
<Compile Include="Slack\Provider\ISlackAuthenticationProvider.cs" />
<Compile Include="Slack\Provider\SlackAuthenticatedContext.cs" />

View File

@@ -0,0 +1,7 @@
namespace Owin.Security.Providers.Shopify
{
internal static class Constants
{
public const string DefaultAuthenticationType = "Shopify";
}
}

View File

@@ -0,0 +1,24 @@
namespace Owin.Security.Providers.Shopify
{
using System.Threading.Tasks;
/// <summary>
/// Specifies callback methods which the <see cref="ShopifyAuthenticationMiddleware"/> invokes to enable developer control over the authentication process.
/// </summary>
public interface IShopifyAuthenticationProvider
{
/// <summary>
/// Invoked whenever Shopify shop successfully authenticates a user.
/// </summary>
/// <param name="context">Contains information about the login session as well as the shop <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
Task Authenticated(ShopifyAuthenticatedContext context);
/// <summary>
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
/// </summary>
/// <param name="context">Instance of return endpoint context.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
Task ReturnEndpoint(ShopifyReturnEndpointContext context);
}
}

View File

@@ -0,0 +1,78 @@
namespace Owin.Security.Providers.Shopify
{
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Provider;
using Newtonsoft.Json.Linq;
using System.Security.Claims;
public class ShopifyAuthenticatedContext : BaseContext
{
/// <summary>
/// Initializes a new instance of the <see cref="ShopifyAuthenticatedContext"/> class.
/// </summary>
/// <param name="context">The OWIN environment.</param>
/// <param name="shop">The JSON-serialized shop.</param>
/// <param name="accessToken">Shopify shop access token.</param>
public ShopifyAuthenticatedContext(IOwinContext context, JObject shop, string accessToken)
: base(context)
{
Shop = shop;
AccessToken = accessToken;
Id = TryGetValue(shop, "id");
var fullShopifyDomainName = TryGetValue(shop, "myshopify_domain");
UserName = string.IsNullOrWhiteSpace(fullShopifyDomainName) ? null : fullShopifyDomainName.Replace(".myshopify.com", "");
Email = TryGetValue(shop, "email");
ShopName = TryGetValue(shop, "name");
}
/// <summary>
/// Gets the JSON-serialized Shopify shop.
/// </summary>
/// <remarks>Contains the Shopify shop information obtained from the Shop endpoint. By default this is https://{shopname}.myshopify.com/admin/shop but it can be overridden in the options.</remarks>
public JObject Shop { get; private set; }
/// <summary>
/// Gets the Shopify shop access token
/// </summary>
public string AccessToken { get; private set; }
/// <summary>
/// Gets the Shopify shop Id.
/// </summary>
public string Id { get; private set; }
/// <summary>
/// Gets the Shopify shop domain name.
/// </summary>
/// <remarks>{shop_domain_name}.myshopify.com - without the ".myshopify.com" to be used as suggested username.</remarks>
public string UserName { get; private set; }
/// <summary>
/// Gets the Shopify shop primary email address.
/// </summary>
public string Email { get; private set; }
/// <summary>
/// Gets the Shopify shop name.
/// </summary>
public string ShopName { get; private set; }
/// <summary>
/// Gets the <see cref="ClaimsIdentity"/> representing the Shopify shop.
/// </summary>
public ClaimsIdentity Identity { get; set; }
/// <summary>
/// Gets or sets a property bag for common authentication properties
/// </summary>
public AuthenticationProperties Properties { get; set; }
private static string TryGetValue(JToken shop, string propertyName)
{
var propertyValue = shop?.First?.First?[propertyName];
return propertyValue?.ToString();
}
}
}

View File

@@ -0,0 +1,50 @@
namespace Owin.Security.Providers.Shopify
{
using System;
using System.Threading.Tasks;
/// <summary>
/// Default <see cref="IShopifyAuthenticationProvider"/> implementation.
/// </summary>
public class ShopifyAuthenticationProvider : IShopifyAuthenticationProvider
{
/// <summary>
/// Initializes a new instance of the <see cref="ShopifyAuthenticationProvider"/> class.
/// </summary>
public ShopifyAuthenticationProvider()
{
OnAuthenticated = context => Task.FromResult<object>(null);
OnReturnEndpoint = context => Task.FromResult<object>(null);
}
/// <summary>
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
/// </summary>
public Func<ShopifyAuthenticatedContext, Task> OnAuthenticated { get; set; }
/// <summary>
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
/// </summary>
public Func<ShopifyReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
/// <summary>
/// Invoked whenever Shopify shop successfully authenticates a user.
/// </summary>
/// <param name="context">Contains information about the login session as well as the shop <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
public virtual Task Authenticated(ShopifyAuthenticatedContext context)
{
return OnAuthenticated(context);
}
/// <summary>
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
/// </summary>
/// <param name="context">Instance of return endpoint context.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
public virtual Task ReturnEndpoint(ShopifyReturnEndpointContext context)
{
return OnReturnEndpoint(context);
}
}
}

View File

@@ -0,0 +1,22 @@
namespace Owin.Security.Providers.Shopify
{
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Provider;
/// <summary>
/// Provides context information to middleware providers.
/// </summary>
public class ShopifyReturnEndpointContext : ReturnEndpointContext
{
/// <summary>
/// Initializes a new instance of the <see cref="ShopifyReturnEndpointContext"/> class.
/// </summary>
/// <param name="context">OWIN environment.</param>
/// <param name="ticket">The authentication ticket.</param>
public ShopifyReturnEndpointContext(IOwinContext context, AuthenticationTicket ticket)
: base(context, ticket)
{
}
}
}

View File

@@ -0,0 +1,45 @@
namespace Owin.Security.Providers.Shopify
{
using System;
public static class ShopifyAuthenticationExtensions
{
/// <summary>
/// Use Shopify Shop OAuth authentication.
/// </summary>
/// <param name="app">Instance of <see cref="IAppBuilder"/>.</param>
/// <param name="options">Shopify overrided authentication options.</param>
/// <returns>Returns instance of <see cref="IAppBuilder"/>.</returns>
public static IAppBuilder UseShopifyAuthentication(this IAppBuilder app, ShopifyAuthenticationOptions options)
{
if (null == app)
{
throw new ArgumentNullException(nameof(app));
}
if (null == options)
{
throw new ArgumentNullException(nameof(options));
}
app.Use(typeof(ShopifyAuthenticationMiddleware), app, options);
return app;
}
/// <summary>
/// Use Shopify Shop OAuth authentication with default authentication options.
/// </summary>
/// <param name="app">Instance of <see cref="IAppBuilder"/>.</param>
/// <param name="apiKey">Shopify App - API key.</param>
/// <param name="apiSecret">Shopify App - API secret.</param>
/// <returns>Returns instance of <see cref="IAppBuilder"/>.</returns>
public static IAppBuilder UseShopifyAuthentication(this IAppBuilder app, string apiKey, string apiSecret)
{
return app.UseShopifyAuthentication(new ShopifyAuthenticationOptions
{
ApiKey = apiKey,
ApiSecret = apiSecret
});
}
}
}

View File

@@ -0,0 +1,230 @@
namespace Owin.Security.Providers.Shopify
{
using Microsoft.Owin.Infrastructure;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Threading.Tasks;
public class ShopifyAuthenticationHandler : AuthenticationHandler<ShopifyAuthenticationOptions>
{
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
private readonly ILogger logger;
private readonly HttpClient httpClient;
public ShopifyAuthenticationHandler(HttpClient httpClient, ILogger logger)
{
this.httpClient = httpClient;
this.logger = logger;
}
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
AuthenticationProperties properties = null;
try
{
string code = null;
string state = null;
var query = Request.Query;
var values = query.GetValues("code");
if (null != values && 1 == values.Count)
{
code = values[0];
}
values = query.GetValues("state");
if (null != values && 1 == values.Count)
{
state = values[0];
}
properties = Options.StateDataFormat.Unprotect(state);
if (null == properties)
{
return null;
}
//// OAuth2 10.12 CSRF
if (!ValidateCorrelationId(properties, logger))
{
return new AuthenticationTicket(null, properties);
}
var currentShopifyShopName = properties.Dictionary["ShopName"];
if (string.IsNullOrWhiteSpace(currentShopifyShopName))
{
return null;
}
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<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("redirect_uri", redirectUri),
new KeyValuePair<string, string>("client_id", Options.ApiKey),
new KeyValuePair<string, string>("client_secret", Options.ApiSecret)
};
//// Request the token
var requestMessage = new HttpRequestMessage(HttpMethod.Post, string.Format(CultureInfo.CurrentCulture, Options.Endpoints.TokenEndpoint, currentShopifyShopName));
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
requestMessage.Content = new FormUrlEncodedContent(body);
var tokenResponse = await httpClient.SendAsync(requestMessage);
tokenResponse.EnsureSuccessStatusCode();
var text = await tokenResponse.Content.ReadAsStringAsync();
//// Deserializes the token response
dynamic response = JsonConvert.DeserializeObject<dynamic>(text);
var accessToken = (string)response.access_token;
//// Get the Shopify shop information
var shopRequest = new HttpRequestMessage(HttpMethod.Get, string.Format(CultureInfo.CurrentCulture, Options.Endpoints.ShopInfoEndpoint, currentShopifyShopName) + "?access_token=" + Uri.EscapeDataString(accessToken));
shopRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var shopResponse = await httpClient.SendAsync(shopRequest, Request.CallCancelled);
shopResponse.EnsureSuccessStatusCode();
text = await shopResponse.Content.ReadAsStringAsync();
var shopifyShop = JObject.Parse(text);
var context = new ShopifyAuthenticatedContext(Context, shopifyShop, 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));
}
if (!string.IsNullOrEmpty(context.UserName))
{
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.UserName, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Email))
{
context.Identity.AddClaim(new Claim(ClaimTypes.Email, context.Email, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.ShopName))
{
context.Identity.AddClaim(new Claim("urn:shopify:shopname", context.ShopName, XmlSchemaString, Options.AuthenticationType));
}
context.Properties = properties;
await Options.Provider.Authenticated(context);
return new AuthenticationTicket(context.Identity, context.Properties);
}
catch (Exception exception)
{
logger.WriteError(exception.Message);
}
return new AuthenticationTicket(null, properties);
}
protected override Task ApplyResponseChallengeAsync()
{
if (Response.StatusCode != 401)
{
return Task.FromResult<object>(null);
}
var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
if (challenge == null)
{
return Task.FromResult<object>(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);
var scope = string.Join(",", Options.Scope);
var state = Options.StateDataFormat.Protect(properties);
var authorizationEndpoint =
string.Format(CultureInfo.CurrentCulture, Options.Endpoints.AuthorizationEndpoint, challenge.Properties.Dictionary["ShopName"]) +
"?client_id=" + Uri.EscapeDataString(Options.ApiKey) +
"&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
"&scope=" + Uri.EscapeDataString(scope) +
"&state=" + Uri.EscapeDataString(state);
Response.Redirect(authorizationEndpoint);
return Task.FromResult<object>(null);
}
public override async Task<bool> InvokeAsync()
{
return await InvokeReplyPathAsync();
}
private async Task<bool> InvokeReplyPathAsync()
{
if (!Options.CallbackPath.HasValue || Options.CallbackPath != Request.Path)
{
return false;
}
//// TODO: error responses (I have no idea what this error responses TODO means :o)
var ticket = await AuthenticateAsync();
if (null == ticket)
{
logger.WriteWarning("Invalid return state, unable to redirect.");
Response.StatusCode = 500;
return true;
}
var context = new ShopifyReturnEndpointContext(Context, ticket)
{
SignInAsAuthenticationType = Options.SignInAsAuthenticationType,
RedirectUri = ticket.Properties.RedirectUri
};
await Options.Provider.ReturnEndpoint(context);
if (null != context.SignInAsAuthenticationType && null != context.Identity)
{
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 || null == context.RedirectUri)
{
return context.IsRequestCompleted;
}
var redirectUri = context.RedirectUri;
if (null == context.Identity)
{
//// 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;
}
}
}

View File

@@ -0,0 +1,89 @@
namespace Owin.Security.Providers.Shopify
{
using Microsoft.Owin;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler;
using Microsoft.Owin.Security.DataProtection;
using Microsoft.Owin.Security.Infrastructure;
using Properties;
using System;
using System.Globalization;
using System.Net.Http;
public class ShopifyAuthenticationMiddleware : AuthenticationMiddleware<ShopifyAuthenticationOptions>
{
private readonly HttpClient httpClient;
private readonly ILogger logger;
public ShopifyAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, ShopifyAuthenticationOptions options)
: base(next, options)
{
if (string.IsNullOrWhiteSpace(Options.ApiKey))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "ApiKey"));
}
if (string.IsNullOrWhiteSpace(Options.ApiSecret))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "ApiSecret"));
}
this.logger = app.CreateLogger<ShopifyAuthenticationMiddleware>();
if (null == Options.Provider)
{
Options.Provider = new ShopifyAuthenticationProvider();
}
if (null == Options.StateDataFormat)
{
var dataProtector = app.CreateDataProtector(typeof(ShopifyAuthenticationMiddleware).FullName, Options.AuthenticationType, "v1");
Options.StateDataFormat = new PropertiesDataFormat(dataProtector);
}
if (string.IsNullOrWhiteSpace(Options.SignInAsAuthenticationType))
{
Options.SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType();
}
this.httpClient = new HttpClient(ResolveHttpMessageHandler(Options))
{
Timeout = Options.BackchannelTimeout,
MaxResponseContentBufferSize = 1024 * 1024 * 10
};
this.httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft Owin Shopify middleware");
this.httpClient.DefaultRequestHeaders.ExpectContinue = false;
}
/// <summary>
/// Provides the <see cref="T:Microsoft.Owin.Security.Infrastructure.AuthenticationHandler" /> object for processing authentication-related requests.
/// </summary>
/// <returns>An <see cref="T:Microsoft.Owin.Security.Infrastructure.AuthenticationHandler" /> configured with the <see cref="T:Owin.Security.Providers.Shopify.ShopifyAuthenticationOptions" /> supplied to the constructor.</returns>
protected override AuthenticationHandler<ShopifyAuthenticationOptions> CreateHandler()
{
return new ShopifyAuthenticationHandler(httpClient, logger);
}
private static HttpMessageHandler ResolveHttpMessageHandler(ShopifyAuthenticationOptions options)
{
var handler = options.BackchannelHttpHandler ?? new WebRequestHandler();
//// If they provided a validator, apply it or fail.
if (null == options.BackchannelCertificateValidator)
{
return handler;
}
//// Set the cert validate callback
var webRequestHandler = handler as WebRequestHandler;
if (null == webRequestHandler)
{
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
}
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
return handler;
}
}
}

View File

@@ -0,0 +1,129 @@
namespace Owin.Security.Providers.Shopify
{
using Microsoft.Owin;
using Microsoft.Owin.Security;
using System;
using System.Collections.Generic;
using System.Net.Http;
public class ShopifyAuthenticationOptions : AuthenticationOptions
{
public class ShopifyAuthenticationEndpoints
{
/// <summary>
/// Endpoint which is used to redirect users to request Shopify shop access.
/// </summary>
/// <remarks>Defaults to https://{shop}.myshopify.com/admin/oauth/authorize.</remarks>
public string AuthorizationEndpoint { get; set; }
/// <summary>
/// Endpoint which is used to exchange code for access token
/// </summary>
/// <remarks>Defaults to https://{shop}.myshopify.com/admin/oauth/access_token.</remarks>
public string TokenEndpoint { get; set; }
/// <summary>
/// Endpoint which is used to obtain shop information after authentication
/// </summary>
/// <remarks>Defaults to https://{shop}.myshopify.com/admin/shop.</remarks>
public string ShopInfoEndpoint { get; set; }
}
private const string DefaultAuthorizationEndPoint = "https://{0}.myshopify.com/admin/oauth/authorize";
private const string DefaultTokenEndpoint = "https://{0}.myshopify.com/admin/oauth/access_token";
private const string DefaultShopInfoEndpoint = "https://{0}.myshopify.com/admin/shop";
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used in back channel communications belong to Shopify.
/// </summary>
/// <value>The pinned certificate validator.</value>
/// <remarks>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.</remarks>
public ICertificateValidator BackchannelCertificateValidator { get; set; }
/// <summary>
/// The HttpMessageHandler used to communicate with Shopify. This cannot be set at the same time as BackchannelCertificateValidator unless the value can be downcast to a WebRequestHandler.
/// </summary>
public HttpMessageHandler BackchannelHttpHandler { get; set; }
/// <summary>
/// Gets or sets timeout value in milliseconds for back channel communications with Shopify.
/// </summary>
/// <value>The back channel timeout in milliseconds.</value>
public TimeSpan BackchannelTimeout { get; set; }
/// <summary>
/// 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-shopify".
/// </summary>
public PathString CallbackPath { get; set; }
/// <summary>
/// Get or sets the text that the user can display on a sign in user interface.
/// </summary>
public string Caption
{
get
{
return Description.Caption;
}
set
{
Description.Caption = value;
}
}
/// <summary>
/// Gets or sets the Shopify app API key.
/// </summary>
public string ApiKey { get; set; }
/// <summary>
/// Gets or sets the Shopify app API secret.
/// </summary>
public string ApiSecret { get; set; }
/// <summary>
/// Gets the sets of OAuth endpoints used to authenticate against Shopify shop.
/// </summary>
public ShopifyAuthenticationEndpoints Endpoints { get; set; }
/// <summary>
/// Gets or sets the <see cref="IShopifyAuthenticationProvider" /> used in the authentication events
/// </summary>
public IShopifyAuthenticationProvider Provider { get; set; }
/// <summary>
/// A list of permissions to request.
/// </summary>
public IList<string> Scope { get; private set; }
/// <summary>
/// Gets or sets the name of another authentication middleware which will be responsible for actually issuing a shop <see cref="System.Security.Claims.ClaimsIdentity" />.
/// </summary>
public string SignInAsAuthenticationType { get; set; }
/// <summary>
/// Gets or sets the type used to secure data handled by the middleware.
/// </summary>
public ISecureDataFormat<AuthenticationProperties> StateDataFormat { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ShopifyAuthenticationOptions" /> class.
/// </summary>
public ShopifyAuthenticationOptions()
: base("Shopify")
{
Caption = Constants.DefaultAuthenticationType;
CallbackPath = new PathString("/signin-shopify");
AuthenticationMode = AuthenticationMode.Passive;
Scope = new List<string> { "read_content" };
BackchannelTimeout = TimeSpan.FromSeconds(60);
Endpoints = new ShopifyAuthenticationEndpoints
{
AuthorizationEndpoint = DefaultAuthorizationEndPoint,
TokenEndpoint = DefaultTokenEndpoint,
ShopInfoEndpoint = DefaultShopInfoEndpoint
};
}
}
}

View File

@@ -30,6 +30,7 @@ using Owin.Security.Providers.SoundCloud;
using Owin.Security.Providers.Spotify;
using Owin.Security.Providers.StackExchange;
using Owin.Security.Providers.Steam;
using Owin.Security.Providers.Shopify;
using Owin.Security.Providers.TripIt;
using Owin.Security.Providers.Twitch;
using Owin.Security.Providers.Untappd;
@@ -53,7 +54,7 @@ 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.UseDeviantArtAuthentication("id", "secret");
//app.UseUntappdAuthentication("id", "secret");
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
@@ -185,6 +186,8 @@ namespace OwinOAuthProvidersDemo
//};
//app.UseSalesforceAuthentication(salesforceOptions);
////app.UseShopifyAuthentication("", "");
//app.UseArcGISOnlineAuthentication(
// clientId: "",
// clientSecret: "");
@@ -201,7 +204,6 @@ namespace OwinOAuthProvidersDemo
// clientId: "",
// clientSecret: "");
//app.UseBattleNetAuthentication(new BattleNetAuthenticationOptions
//{
// ClientId = "",

View File

@@ -182,15 +182,26 @@ namespace OwinOAuthProvidersDemo.Controllers
return View(model);
}
//////
////// POST: /Account/ExternalLogin
////[HttpPost]
////[AllowAnonymous]
////[ValidateAntiForgeryToken]
////public ActionResult ExternalLogin(string provider, string returnUrl)
////{
//// // Request a redirect to the external login provider
//// return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
////}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
public ActionResult ExternalLogin(string provider, string returnUrl, string shopName = "")
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }), null, shopName);
}
//
@@ -224,10 +235,10 @@ namespace OwinOAuthProvidersDemo.Controllers
// POST: /Account/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
public ActionResult LinkLogin(string provider, string shopName)
{
// Request a redirect to the external login provider to link a login for the current user
return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId());
return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId(), shopName);
}
//
@@ -324,6 +335,8 @@ namespace OwinOAuthProvidersDemo.Controllers
#region Helpers
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
// Used for Shopify external login to provide shopname while building endpoints.
private const string ShopNameKey = "ShopName";
private IAuthenticationManager AuthenticationManager
{
@@ -380,28 +393,36 @@ namespace OwinOAuthProvidersDemo.Controllers
private class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null)
public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null, null)
{
}
public ChallengeResult(string provider, string redirectUri, string userId)
public ChallengeResult(string provider, string redirectUri, string userId, string shopName)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
ShopName = shopName;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public string ShopName { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
properties.Dictionary[XsrfKey] = this.UserId;
}
if (!string.IsNullOrWhiteSpace(this.ShopName))
{
properties.Dictionary[ShopNameKey] = this.ShopName;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
}

View File

@@ -24,6 +24,10 @@
<p>
@foreach (AuthenticationDescription p in loginProviders)
{
if (p.AuthenticationType.Equals("Shopify"))
{
@Html.TextBox("shopName")
}
<button type="submit" class="btn btn-default" id="@p.AuthenticationType" name="provider" value="@p.AuthenticationType" title="Log in using your @p.Caption account">@p.AuthenticationType</button>
}
</p>

View File

@@ -24,6 +24,7 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http
- PayPal
- Reddit
- Salesforce
- Shopify
- Slack
- SoundCloud
- Spotify
@@ -41,7 +42,7 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http
- Wargaming
## Implementation Guides
For guides on how to implement these providers, please visit my blog, [Be a Big Rockstar](http://www.beabigrockstar.com).
For above listed provider implementation guide, visit Jerrie Pelser's blog - [Be a Big Rockstar](http://www.beabigrockstar.com)
## Installation
To use these providers you will need to install the ```Owin.Security.Providers``` NuGet package.
@@ -71,6 +72,7 @@ A big thanks goes out to all these contributors without whom this would not have
* Anthony Ruffino (https://github.com/AnthonyRuffino)
* Tommy Parnell (https://github.com/tparnell8)
* Maxime Roussin-Bélanger (https://github.com/Lorac)
* Jaspalsinh Chauhan (https://github.com/jsinh)
For most accurate and up to date list of contributors please see https://github.com/RockstarLabs/OwinOAuthProviders/graphs/contributors

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014 Jerrie Pelser
Copyright (c) 2014, 2015 Jerrie Pelser
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal