Created
June 22, 2012 21:45
-
-
Save codeprogression/2975398 to your computer and use it in GitHub Desktop.
OAuth2 Authentication (Consumer) for NancyFX
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class OAuth2AuthenticationClient | |
{ | |
public static void Enable(IPipelines pipelines, OAuth2AuthenticationClientConfiguration clientConfiguration) | |
{ | |
if (!clientConfiguration.IsValid) | |
throw new OAuth2AuthenticationClientConfiguration.InvalidConfigurationException(); | |
pipelines.BeforeRequest.AddItemToStartOfPipeline(GetLoadAuthenticationHook(clientConfiguration)); | |
pipelines.AfterRequest.AddItemToEndOfPipeline(GetRedirectToProviderHook(clientConfiguration)); | |
} | |
public static void Enable(NancyModule module, OAuth2AuthenticationClientConfiguration clientConfiguration) | |
{ | |
if (!clientConfiguration.IsValid) | |
throw new OAuth2AuthenticationClientConfiguration.InvalidConfigurationException(); | |
module.RequiresAuthentication(); | |
module.Before.AddItemToStartOfPipeline(GetLoadAuthenticationHook(clientConfiguration)); | |
module.After.AddItemToEndOfPipeline(GetRedirectToProviderHook(clientConfiguration)); | |
} | |
static Func<NancyContext, Response> GetLoadAuthenticationHook(OAuth2AuthenticationClientConfiguration clientConfiguration) | |
{ | |
if (clientConfiguration == null) | |
{ | |
throw new ArgumentNullException("clientConfiguration"); | |
} | |
return context => | |
{ | |
//need implementation here | |
return null; | |
}; | |
} | |
static bool EnsureIsValidAuthorization(NancyContext context) | |
{ | |
return ((string)context.Request.Query.State).Replace(" 0", "+") == context.Request.Session["state"].ToString(); | |
} | |
static Action<NancyContext> GetRedirectToProviderHook(OAuth2AuthenticationClientConfiguration clientConfiguration) | |
{ | |
return context => | |
{ | |
if (context.Response.StatusCode == HttpStatusCode.Unauthorized) | |
{ | |
var sessionKey = clientConfiguration.GetNewStateValue(); | |
context.Request.Session["state"] = sessionKey; | |
context.Response = context.GetRedirect(clientConfiguration.GetRequestUri(sessionKey)); | |
} | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class OAuth2AuthenticationClientConfiguration | |
{ | |
/// <summary> | |
/// Gets or sets the cryptography configuration | |
/// </summary> | |
public CryptographyConfiguration CryptographyConfiguration { get; set; } | |
/// <summary> | |
/// Initializes a new instance of the <see cref="OAuth2AuthenticationClientConfiguration"/> class. | |
/// </summary> | |
public OAuth2AuthenticationClientConfiguration() | |
: this(CryptographyConfiguration.Default) | |
{ | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="OAuth2AuthenticationClientConfiguration"/> class. | |
/// </summary> | |
/// <param name="cryptographyConfiguration">Cryptography configuration</param> | |
public OAuth2AuthenticationClientConfiguration(CryptographyConfiguration cryptographyConfiguration) | |
{ | |
CryptographyConfiguration = cryptographyConfiguration; | |
} | |
/// <summary> | |
/// A unique value used by your application in order to prevent cross-site request forgery (CSRF). | |
/// The value should be random, unguessable, specific for the particular request and kept secret | |
/// in the client (perhaps server-side session). | |
/// | |
/// This method provides a HMAC encoded string from a random 9-byte key | |
/// </summary> | |
public string GetNewStateValue() | |
{ | |
return | |
Convert.ToBase64String( | |
CryptographyConfiguration.HmacProvider.GenerateHmac(new RandomKeyGenerator().GetBytes(9))); | |
} | |
/// <summary> | |
/// The endpoint to send an OAuth2 authorization request. | |
/// </summary> | |
public string AuthorizationEndpoint { get; set; } | |
/// <summary> | |
/// The value provided when you registered your application with the provider. | |
/// </summary> | |
public string ClientId { get; set; } | |
/// <summary> | |
/// The value provided when you registered your application with the provider. | |
/// </summary> | |
public string ClientSecret { get; set; } | |
/// <summary> | |
/// The location the user should be returned to after they approve access to your app. | |
/// This value typically needs to be registered in advance with the provider. | |
/// </summary> | |
public string RedirectUri { get; set; } | |
/// <summary> | |
/// The data your application is requesting access to. | |
/// Usually space-delimited strings. (Depending on provider, may be comma-delimited, e.g. Facebook) | |
/// </summary> | |
public string Scope { get; set; } | |
/// <summary> | |
/// Specifies the application flow. | |
/// For a server-side web application flow, use `code` | |
/// </summary> | |
public string ResponseType { get; set; } | |
string State { get; set; } | |
public NameValueCollection CustomRequestParameters { get; set; } | |
public bool IsValid | |
{ | |
get | |
{ | |
return !string.IsNullOrEmpty(AuthorizationEndpoint) | |
&& !string.IsNullOrEmpty(CallbackRoute) | |
&& !string.IsNullOrEmpty(ClientId) | |
&& !string.IsNullOrEmpty(ClientSecret) | |
&& !string.IsNullOrEmpty(ResponseType) | |
&& !string.IsNullOrEmpty(RedirectUri); | |
} | |
} | |
/// <summary> | |
/// The route that an OAuth provider can redirect authorization code to | |
/// </summary> | |
public string CallbackRoute { get; set; } | |
/// <summary> | |
/// | |
/// </summary> | |
/// <param name="sessionKey"></param> | |
/// <returns></returns> | |
public virtual string GetRequestUri(string sessionKey) | |
{ | |
var query = HttpUtility.ParseQueryString(""); | |
query.Add("client_id",ClientId); | |
query.Add("redirect_uri", RedirectUri); | |
query.Add("scope", Scope); | |
query.Add("response_type", ResponseType); | |
query.Add("state", sessionKey); | |
if (CustomRequestParameters!=null) | |
query.Add(CustomRequestParameters); | |
var uri = new UriBuilder(AuthorizationEndpoint) | |
{ | |
Query = EncodeQueryString(query) | |
}; | |
return uri.ToString(); | |
} | |
static string EncodeQueryString(NameValueCollection collection) | |
{ | |
int count = collection.Count; | |
if (count == 0) | |
return ""; | |
var sb = new StringBuilder(); | |
string[] keys = collection.AllKeys; | |
for (int i = 0; i < count; i++) | |
{ | |
sb.AppendFormat("{0}={1}&", keys[i], collection[keys[i]]); | |
} | |
if (sb.Length > 0) | |
sb.Length--; | |
return sb.ToString(); | |
} | |
public class InvalidConfigurationException : Exception | |
{ | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment