files tree made better.

This commit is contained in:
2019-01-01 16:28:47 +00:00
parent cb96933a25
commit 5b8e9b3975
1633 changed files with 18220 additions and 41869 deletions

View File

@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.Text;
using GoogleTranslateNET.Misc;
using GoogleTranslateNET.Objects.Error;
using GoogleTranslateNET.Objects.LanguageDetection;
using GoogleTranslateNET.Objects.SupportedLanguages;
using GoogleTranslateNET.Objects.Translation;
using RestSharp;
using RestSharp.Deserializers;
using System.Linq;
namespace GoogleTranslateNET
{
public class GoogleTranslate
{
private string _key;
private static RestClient _client = new RestClient("https://www.googleapis.com/language/translate/v2");
public GoogleTranslate(string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key is required.", "key");
_key = key;
}
/// <summary>
/// When true, the output from google is in human readable format.
/// Default: Not set, Google defaults to true
/// </summary>
public bool? PrettyPrint { get; set; }
/// <summary>
/// When true, queries will be sent using HTTP POST instead of GET.
/// GET queries have a limit of 2000 characters, POST queries have a limit of 5000 characters.
/// Default: Not set, Google defaults to false
/// </summary>
public bool? LargeQuery { get; set; }
/// <summary>
/// Translates a text from one language to another.
/// You can input multiple texts and get them translated all at once.
/// Warning: Setting source and destination languages to the same language will result in an error.
/// </summary>
/// <param name="sourceLanguage">The language to translate from. Set it to Language.Automatic to let Google Translate determine the language.</param>
/// <param name="destinationLanaguage">The language to translate to.</param>
/// <param name="text">The text to translate. You may input more than one text.</param>
/// <returns>The translated text.</returns>
public List<Translation> Translate(Language sourceLanguage, Language destinationLanaguage, params string[] text)
{
//https://www.googleapis.com/language/translate/v2?key=key&q=hello%20world&source=en&target=de
RestRequest request = CreateRequest(string.Empty);
CheckRequest(text);
//Required
foreach (string q in text)
{
request.AddParameter("q", q);
}
request.AddParameter("target", destinationLanaguage.GetStringValue());
//Optional
if (sourceLanguage != Language.Automatic)
request.AddParameter("source", sourceLanguage.GetStringValue());
//Output
TranslateResult results = GetResponse<TranslateResult>(request);
return results.Data.Translations;
}
/// <summary>
/// Gives you a list of supported languages.
/// </summary>
/// <param name="targetLanguage">When defined, gives you a list of languages that translates into the target language.</param>
/// <returns>A list of supported languages</returns>
public List<TranslationLanaguage> GetSupportedLanguages(Language targetLanguage = Language.Unknown)
{
//https://www.googleapis.com/language/translate/v2/languages?key=key&target=zh-TW
RestRequest request = CreateRequest("languages");
//Optional
if (targetLanguage != Language.Unknown)
request.AddParameter("target", targetLanguage.GetStringValue());
//Output
SupportedLanguageResult results = GetResponse<SupportedLanguageResult>(request);
return results.Data.Languages;
}
/// <summary>
/// Detects the languages that might be used in the text.
/// You can send more than one text in a single request to detect multiple texts.
/// </summary>
/// <param name="text">The text to use when detecting languages.</param>
/// <returns>A list of languages that might be used in the text.</returns>
public List<LanguageDetection> DetectLanguage(params string[] text)
{
//https://www.googleapis.com/language/translate/v2/detect?key=key&q=google+translate+is+fast
RestRequest request = CreateRequest("detect");
CheckRequest(text);
//Required
foreach (string q in text)
{
request.AddParameter("q", q);
}
//Output
LanguageDetectionResult results = GetResponse<LanguageDetectionResult>(request);
//Flatten the results from Google Translate API
List<LanguageDetection> detections = new List<LanguageDetection>();
foreach (List<LanguageDetection> languageDetections in results.Data.Detections)
{
detections.AddRange(languageDetections);
}
return detections;
}
private void CheckRequest(IEnumerable<string> requestContent)
{
//Compute the total size of the content
int sum = requestContent.Sum(item => item.Length);
if (((LargeQuery.HasValue && !LargeQuery.Value) || !LargeQuery.HasValue) && sum >= 2000)
{
throw new ArgumentException("Your text content is larger than 2000 characters. Set LargeQuery to 'true' to enable support up to 5000 characters.");
}
if (sum > 5000)
throw new ArgumentException("Your text content is larger than 5000 characters. Google Translate only allow up to 5000 characters");
}
private RestRequest CreateRequest(string function)
{
RestRequest request;
if (LargeQuery.HasValue && LargeQuery.Value)
{
request = new RestRequest(function, Method.POST);
//To use POST, you must use the X-HTTP-Method-Override header to tell the Translate API to treat the request as a GET (use X-HTTP-Method-Override: GET).
request.AddHeader("X-HTTP-Method-Override", "GET");
}
else
{
request = new RestRequest(function, Method.GET);
}
request.AddParameter("key", _key);
if (PrettyPrint.HasValue)
request.AddParameter("prettyprint", PrettyPrint.ToString().ToLower());
return request;
}
private T GetResponse<T>(RestRequest request)
{
RestResponse response = (RestResponse)_client.Execute(request);
JsonDeserializer deserializer = new JsonDeserializer();
T results = deserializer.Deserialize<T>(response);
//Try to deserialize it as an error - it is a hack since I'm using generics here.
ErrorResponse errorResponse = deserializer.Deserialize<ErrorResponse>(response);
if (errorResponse.Error != null)
throw new Exception(GetErrorText(errorResponse.Error));
return results;
}
private string GetErrorText(Error error)
{
if (error != null)
{
StringBuilder sb = new StringBuilder();
sb.Append(error.Message);
if (error.Errors.Count >= 1)
{
ErrorData errorData = error.Errors.First();
sb.Append("Reason: " + errorData.Reason);
}
return sb.ToString();
}
return "There was an error. Unable to determine the cause.";
}
}
}

View File

@ -0,0 +1,118 @@
using GoogleTranslateNET.Misc;
namespace GoogleTranslateNET
{
public enum Language
{
Unknown,
Automatic,
[StringValue("af")]
Afrikaans,
[StringValue("sq")]
Albanian,
[StringValue("ar")]
Arabic,
[StringValue("be")]
Belarusian,
[StringValue("bg")]
Bulgarian,
[StringValue("ca")]
Catalan,
[StringValue("zh")]
ChineseSimplified,
[StringValue("zh-TW")]
ChineseTraditional,
[StringValue("hr")]
Croatian,
[StringValue("cs")]
Czech,
[StringValue("da")]
Danish,
[StringValue("nl")]
Dutch,
[StringValue("en")]
English,
[StringValue("eo")]
Esperanto,
[StringValue("et")]
Estonian,
[StringValue("tl")]
Filipino,
[StringValue("fi")]
Finnish,
[StringValue("fr")]
French,
[StringValue("gl")]
Galician,
[StringValue("de")]
German,
[StringValue("el")]
Greek,
[StringValue("ht")]
HaitianCreole,
[StringValue("iw")]
Hebrew,
[StringValue("hi")]
Hindi,
[StringValue("hu")]
Hungarian,
[StringValue("is")]
Icelandic,
[StringValue("id")]
Indonesian,
[StringValue("ga")]
Irish,
[StringValue("it")]
Italian,
[StringValue("ja")]
Japanese,
[StringValue("ko")]
Korean,
[StringValue("lv")]
Latvian,
[StringValue("lt")]
Lithuanian,
[StringValue("mk")]
Macedonian,
[StringValue("ms")]
Malay,
[StringValue("mt")]
Maltese,
[StringValue("no")]
Norwegian,
[StringValue("fa")]
Persian,
[StringValue("pl")]
Polish,
[StringValue("pt")]
Portuguese,
[StringValue("ro")]
Romanian,
[StringValue("ru")]
Russian,
[StringValue("sr")]
Serbian,
[StringValue("sk")]
Slovak,
[StringValue("sl")]
Slovenian,
[StringValue("es")]
Spanish,
[StringValue("sw")]
Swahili,
[StringValue("sv")]
Swedish,
[StringValue("th")]
Thai,
[StringValue("tr")]
Turkish,
[StringValue("uk")]
Ukrainian,
[StringValue("vi")]
Vietnamese,
[StringValue("cy")]
Welsh,
[StringValue("yi")]
Yiddish
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Reflection;
namespace GoogleTranslateNET.Misc
{
public static class ExtensionMethods
{
/// <summary>
/// Will get the string value for a given enums value, this will
/// only work if you assign the StringValue attribute to
/// the items in your enum.
/// Source: http://weblogs.asp.net/stefansedich/archive/2008/03/12/enum-with-string-values-in-c.aspx
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string GetStringValue(this Enum value)
{
// Get the type
Type type = value.GetType();
// Get fieldinfo for this type
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the stringvalue attributes
StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
typeof(StringValueAttribute), false) as StringValueAttribute[];
// Return the first if there was a match.
return attribs.Length > 0 ? attribs[0].StringValue : null;
}
}
}

View File

@ -0,0 +1,18 @@
using System;
namespace GoogleTranslateNET.Misc
{
/// <summary>
/// This attribute is used to represent a string value
/// for a value in an enum.
/// </summary>
public class StringValueAttribute : Attribute
{
public string StringValue { get; private set; }
public StringValueAttribute(string value)
{
StringValue = value;
}
}
}

View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace GoogleTranslateNET.Objects.Error
{
public class Error
{
public int Code { get; set; }
public string Message { get; set; }
public List<ErrorData> Errors { get; set; }
}
}

View File

@ -0,0 +1,11 @@
namespace GoogleTranslateNET.Objects.Error
{
public class ErrorData
{
public string Domain { get; set; }
public string Reason { get; set; }
public string Message { get; set; }
public string LocationType { get; set; }
public string Location { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace GoogleTranslateNET.Objects.Error
{
public class ErrorResponse
{
public Error Error { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace GoogleTranslateNET.Objects.LanguageDetection
{
public class LanguageDetection
{
public string Language { get; set; }
public bool IsReliable { get; set; }
public float Confidence { get; set; }
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace GoogleTranslateNET.Objects.LanguageDetection
{
public class LanguageDetectionData
{
public List<List<LanguageDetection>> Detections { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace GoogleTranslateNET.Objects.LanguageDetection
{
public class LanguageDetectionResult
{
public LanguageDetectionData Data { get; set; }
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace GoogleTranslateNET.Objects.SupportedLanguages
{
public class SupportedLanguageData
{
public List<TranslationLanaguage> Languages { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace GoogleTranslateNET.Objects.SupportedLanguages
{
public class SupportedLanguageResult
{
public SupportedLanguageData Data { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace GoogleTranslateNET.Objects.SupportedLanguages
{
public class TranslationLanaguage
{
public string Language { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace GoogleTranslateNET.Objects.Translation
{
public class TranslateResult
{
public TranslationData Data { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace GoogleTranslateNET.Objects.Translation
{
public class Translation
{
public string TranslatedText { get; set; }
public string DetectedSourceLanguage { get; set; }
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace GoogleTranslateNET.Objects.Translation
{
public class TranslationData
{
public List<Translation> Translations { get; set; }
}
}

View File

@ -0,0 +1,59 @@
{
"version": "1.0.5-*",
"title": "Yavsc Google Translate [DNX]",
"description": "Google Translate for DNX",
"authors": [
"Paul Schneider <paul@pschneider.fr>"
],
"packOptions": {
"repository": {
"type": "git",
"url": "https://github.com/pazof/yavsc"
},
"licenseUrl": "https://github.com/pazof/yavsc/blob/vnext/LICENSE",
"requireLicenseAcceptance": true,
"owners": [
"Paul Schneider <paul@pschneider.fr>"
],
"summary": "CSharp Google Tranlate API",
"projectUrl": "http://yavsc.pschneider.fr",
"tags": [
"Translation"
]
},
"tooling": {
"defaultNamespace": "Yavsc"
},
"dependencies": {
"Newtonsoft.Json": "9.0.1",
"Gapi.net45": "1.0.1",
"RestSharp": "103.4.0"
},
"frameworks": {
"dnx451": {
"frameworkAssemblies": {
"System.ComponentModel.DataAnnotations": "4.0.0"
}
},
"net46": {
"frameworkAssemblies": {
"System.ComponentModel.DataAnnotations": "4.0.0"
}
},
"net461": {
"frameworkAssemblies": {
"System.ComponentModel.DataAnnotations": "4.0.0"
}
},
"net452": {
"frameworkAssemblies": {
"System.ComponentModel.DataAnnotations": "4.0.0"
}
},
"net451": {
"frameworkAssemblies": {
"System.ComponentModel.DataAnnotations": "4.0.0"
}
}
}
}

View File

@ -0,0 +1,32 @@
using Microsoft.AspNet.Http.Authentication;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Exposes the security.Challenge environment value as a strong type.
/// </summary>
public class AuthenticationResponseChallenge
{
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationResponseChallenge"/> class
/// </summary>
/// <param name="authenticationTypes"></param>
/// <param name="properties"></param>
public AuthenticationResponseChallenge(string[] authenticationTypes, AuthenticationProperties properties)
{
AuthenticationTypes = authenticationTypes;
Properties = properties ?? new AuthenticationProperties();
}
/// <summary>
/// List of the authentication types that should send a challenge in the response.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "By design")]
public string[] AuthenticationTypes { get; private set; }
/// <summary>
/// Dictionary used to store state values about the authentication session.
/// </summary>
public AuthenticationProperties Properties { get; private set; }
}
}

View File

@ -0,0 +1,57 @@
using Microsoft.AspNet.Http.Authentication;
using System;
using System.Linq;
using System.Security.Claims;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Exposes the security.SignIn environment value as a strong type.
/// </summary>
public class AuthenticationResponseGrant
{
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationResponseGrant"/> class.
/// </summary>
/// <param name="identity"></param>
/// <param name="properties"></param>
public AuthenticationResponseGrant(ClaimsIdentity identity, AuthenticationProperties properties)
{
Principal = new ClaimsPrincipal(identity);
Identity = identity;
Properties = properties;
}
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationResponseGrant"/> class.
/// </summary>
/// <param name="principal"></param>
/// <param name="properties"></param>
public AuthenticationResponseGrant(ClaimsPrincipal principal, AuthenticationProperties properties)
{
if (principal == null)
{
throw new ArgumentNullException("principal");
}
Principal = principal;
Identity = principal.Identities.FirstOrDefault();
Properties = properties;
}
/// <summary>
/// The identity associated with the user sign in.
/// </summary>
public ClaimsIdentity Identity { get; private set; }
/// <summary>
/// The security principal associated with the user sign in.
/// </summary>
public ClaimsPrincipal Principal { get; private set; }
/// <summary>
/// Dictionary used to store state values about the authentication session.
/// </summary>
public AuthenticationProperties Properties { get; private set; }
}
}

View File

@ -0,0 +1,43 @@
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Authentication;
using System;
namespace OAuth.AspNet.AuthServer
{
public class AuthenticationTokenCreateContext : BaseContext
{
private readonly ISecureDataFormat<AuthenticationTicket> _secureDataFormat;
public AuthenticationTokenCreateContext(HttpContext context, ISecureDataFormat<AuthenticationTicket> secureDataFormat, AuthenticationTicket ticket) : base(context)
{
if (secureDataFormat == null)
throw new ArgumentNullException(nameof(secureDataFormat));
if (ticket == null)
throw new ArgumentNullException(nameof(ticket));
_secureDataFormat = secureDataFormat;
Ticket = ticket;
}
public string Token { get; protected set; }
public AuthenticationTicket Ticket { get; protected set; }
public string SerializeTicket()
{
return _secureDataFormat.Protect(Ticket);
}
public void SetToken(string tokenValue)
{
if (tokenValue == null)
throw new ArgumentNullException(nameof(tokenValue));
Token = tokenValue;
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.Threading.Tasks;
namespace OAuth.AspNet.AuthServer
{
public class AuthenticationTokenProvider : IAuthenticationTokenProvider
{
public Action<AuthenticationTokenCreateContext> OnCreate { get; set; }
public Func<AuthenticationTokenCreateContext, Task> OnCreateAsync { get; set; }
public Action<AuthenticationTokenReceiveContext> OnReceive { get; set; }
public Func<AuthenticationTokenReceiveContext, Task> OnReceiveAsync { get; set; }
public virtual void Create(AuthenticationTokenCreateContext context)
{
if (OnCreateAsync != null && OnCreate == null)
{
throw new InvalidOperationException("Authentication token did not provide an OnCreate method.");
}
if (OnCreate != null)
{
OnCreate.Invoke(context);
}
}
public virtual async Task CreateAsync(AuthenticationTokenCreateContext context)
{
if (OnCreateAsync != null && OnCreate == null)
{
throw new InvalidOperationException("Authentication token did not provide an OnCreate method.");
}
if (OnCreateAsync != null)
{
await OnCreateAsync.Invoke(context);
}
else
{
Create(context);
}
}
public virtual void Receive(AuthenticationTokenReceiveContext context)
{
if (OnReceiveAsync != null && OnReceive == null)
{
throw new InvalidOperationException("Authentication token did not provide an OnReceive method.");
}
if (OnReceive != null)
{
OnReceive.Invoke(context);
}
}
public virtual async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
if (OnReceiveAsync != null && OnReceive == null)
{
throw new InvalidOperationException("Authentication token did not provide an OnReceive method.");
}
if (OnReceiveAsync != null)
{
await OnReceiveAsync.Invoke(context);
}
else
{
Receive(context);
}
}
}
}

View File

@ -0,0 +1,43 @@
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Authentication;
using System;
namespace OAuth.AspNet.AuthServer
{
public class AuthenticationTokenReceiveContext : BaseContext
{
private readonly ISecureDataFormat<AuthenticationTicket> _secureDataFormat;
public AuthenticationTokenReceiveContext(HttpContext context, ISecureDataFormat<AuthenticationTicket> secureDataFormat, string token) : base(context)
{
if (secureDataFormat == null)
throw new ArgumentNullException(nameof(secureDataFormat));
if (token == null)
throw new ArgumentNullException(nameof(token));
_secureDataFormat = secureDataFormat;
Token = token;
}
public string Token { get; protected set; }
public AuthenticationTicket Ticket { get; protected set; }
public void DeserializeTicket(string protectedData)
{
Ticket = _secureDataFormat.Unprotect(protectedData);
}
public void SetTicket(AuthenticationTicket ticket)
{
if (ticket == null)
throw new ArgumentNullException(nameof(ticket));
Ticket = ticket;
}
}
}

View File

@ -0,0 +1,138 @@
using Microsoft.AspNet.Http;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Data object representing the information contained in the query string of an Authorize endpoint request.
/// </summary>
public class AuthorizeEndpointRequest
{
/// <summary>
/// Creates a new instance populated with values from the query string parameters.
/// </summary>
/// <param name="parameters">Query string parameters from a request.</param>
public AuthorizeEndpointRequest(IReadableStringCollection parameters)
{
if (parameters == null)
{
throw new ArgumentNullException("parameters");
}
Scope = new List<string>();
foreach (var parameter in parameters)
{
AddParameter(parameter.Key, parameters[parameter.Key]);
}
}
/// <summary>
/// The "response_type" query string parameter of the Authorize request. Known values are "code" and "token".
/// </summary>
public string ResponseType { get; set; }
/// <summary>
/// The "response_mode" query string parameter of the Authorize request. Known values are "query", "fragment" and "form_post"
/// See also, http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
/// </summary>
public string ResponseMode { get; set; }
/// <summary>
/// The "client_id" query string parameter of the Authorize request.
/// </summary>
public string ClientId { get; set; }
/// <summary>
/// The "redirect_uri" query string parameter of the Authorize request. May be absent if the server should use the
/// redirect uri known to be registered to the client id.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "By design")]
public string RedirectUri { get; set; }
/// <summary>
/// The "scope" query string parameter of the Authorize request. May be absent if the server should use default scopes.
/// </summary>
public IList<string> Scope { get; private set; }
/// <summary>
/// The "scope" query string parameter of the Authorize request. May be absent if the client does not require state to be
/// included when returning to the RedirectUri.
/// </summary>
public string State { get; set; }
/// <summary>
/// True if the "response_type" query string parameter is "code".
/// See also, http://tools.ietf.org/html/rfc6749#section-4.1.1
/// </summary>
public bool IsAuthorizationCodeGrantType
{
get { return ContainsGrantType(Constants.ResponseTypes.Code); }
}
/// <summary>
/// True if the "response_type" query string parameter is "token".
/// See also, http://tools.ietf.org/html/rfc6749#section-4.2.1
/// </summary>
public bool IsImplicitGrantType
{
get { return ContainsGrantType(Constants.ResponseTypes.Token); }
}
public bool IsFormPostResponseMode
{
get { return string.Equals(ResponseMode, Constants.ResponseModes.FormPost, StringComparison.Ordinal); }
}
/// <summary>
/// True if the "response_type" query string contains the passed responseType.
/// See also, http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html
/// </summary>
/// <param name="responseType">The responseType that is expected within the "response_type" query string</param>
/// <returns>True if the "response_type" query string contains the passed responseType.</returns>
public bool ContainsGrantType(string responseType)
{
var parts = ResponseType.Split(' ');
foreach (var part in parts)
{
if (string.Equals(part, responseType, StringComparison.Ordinal))
{
return true;
}
}
return false;
}
private void AddParameter(string name, string value)
{
if (string.Equals(name, Constants.Parameters.ResponseType, StringComparison.Ordinal))
{
ResponseType = value;
}
else if (string.Equals(name, Constants.Parameters.ClientId, StringComparison.Ordinal))
{
ClientId = value;
}
else if (string.Equals(name, Constants.Parameters.RedirectUri, StringComparison.Ordinal))
{
RedirectUri = value;
}
else if (string.Equals(name, Constants.Parameters.Scope, StringComparison.Ordinal))
{
Scope = value.Split(' ');
}
else if (string.Equals(name, Constants.Parameters.State, StringComparison.Ordinal))
{
State = value;
}
else if (string.Equals(name, Constants.Parameters.ResponseMode, StringComparison.Ordinal))
{
ResponseMode = value;
}
}
}
}

View File

@ -0,0 +1,40 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Http;
using System;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Base class for OAuth server endpoint contexts
/// </summary>
public class BaseOAuthEndpointContext : BaseContext
{
#region Constructors
/// <summary>
/// Creates a new instance of <see cref="BaseOAuthEndpointContext"/>.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> to use for this endpoint context.</param>
/// <param name="options">The <see cref="OAuthAuthorizationServerOptions"/> to use for this endpoint context.</param>
public BaseOAuthEndpointContext(HttpContext context, OAuthAuthorizationServerOptions options) : base(context)
{
if (options == null)
throw new ArgumentNullException(nameof(options));
Options = options;
}
#endregion
#region Public Members
/// <summary>
/// Gets the OAuth server options.
/// </summary>
public OAuthAuthorizationServerOptions Options { get; }
#endregion
}
}

View File

@ -0,0 +1,26 @@
using Microsoft.AspNet.Http;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Base class used for certain event contexts
/// </summary>
public abstract class BaseValidatingClientContext : BaseValidatingContext<OAuthAuthorizationServerOptions>
{
/// <summary>
/// Initializes base class used for certain event contexts
/// </summary>
protected BaseValidatingClientContext(HttpContext context, OAuthAuthorizationServerOptions options, string clientId) : base(context, options)
{
ClientId = clientId;
}
/// <summary>
/// The "client_id" parameter for the current request. The Authorization Server application is responsible for
/// validating this value identifies a registered client.
/// </summary>
public string ClientId { get; protected set; }
}
}

View File

@ -0,0 +1,113 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Http;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Base class used for certain event contexts
/// </summary>
public abstract class BaseValidatingContext<TOptions> : BaseContext
{
/// <summary>
/// Initializes base class used for certain event contexts
/// </summary>
protected BaseValidatingContext(HttpContext context, TOptions options) : base(context)
{
Options = options;
}
/// <summary>
/// The context options.
/// </summary>
public TOptions Options { get; private set; }
/// <summary>
/// True if application code has called any of the Validate methods on this context.
/// </summary>
public bool IsValidated { get; private set; }
/// <summary>
/// True if application code has called any of the SetError methods on this context.
/// </summary>
public bool HasError { get; private set; }
/// <summary>
/// The error argument provided when SetError was called on this context. This is eventually
/// returned to the client app as the OAuth "error" parameter.
/// </summary>
public string Error { get; private set; }
/// <summary>
/// The optional errorDescription argument provided when SetError was called on this context. This is eventually
/// returned to the client app as the OAuth "error_description" parameter.
/// </summary>
public string ErrorDescription { get; private set; }
/// <summary>
/// The optional errorUri argument provided when SetError was called on this context. This is eventually
/// returned to the client app as the OAuth "error_uri" parameter.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "error_uri is a string value in the protocol")]
public string ErrorUri { get; private set; }
/// <summary>
/// Marks this context as validated by the application. IsValidated becomes true and HasError becomes false as a result of calling.
/// </summary>
/// <returns>True if the validation has taken effect.</returns>
public virtual bool Validated()
{
IsValidated = true;
HasError = false;
return true;
}
/// <summary>
/// Marks this context as not validated by the application. IsValidated and HasError become false as a result of calling.
/// </summary>
public virtual void Rejected()
{
IsValidated = false;
HasError = false;
}
/// <summary>
/// Marks this context as not validated by the application and assigns various error information properties.
/// HasError becomes true and IsValidated becomes false as a result of calling.
/// </summary>
/// <param name="error">Assigned to the Error property</param>
public void SetError(string error)
{
SetError(error, null);
}
/// <summary>
/// Marks this context as not validated by the application and assigns various error information properties.
/// HasError becomes true and IsValidated becomes false as a result of calling.
/// </summary>
/// <param name="error">Assigned to the Error property</param>
/// <param name="errorDescription">Assigned to the ErrorDescription property</param>
public void SetError(string error, string errorDescription)
{
SetError(error, errorDescription, null);
}
/// <summary>
/// Marks this context as not validated by the application and assigns various error information properties.
/// HasError becomes true and IsValidated becomes false as a result of calling.
/// </summary>
/// <param name="error">Assigned to the Error property</param>
/// <param name="errorDescription">Assigned to the ErrorDescription property</param>
/// <param name="errorUri">Assigned to the ErrorUri property</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "error_uri is a string value in the protocol")]
public void SetError(string error, string errorDescription, string errorUri)
{
Error = error;
ErrorDescription = errorDescription;
ErrorUri = errorUri;
Rejected();
HasError = true;
}
}
}

View File

@ -0,0 +1,54 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Authentication;
using System.Security.Claims;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Base class used for certain event contexts
/// </summary>
public abstract class BaseValidatingTicketContext<TOptions> : BaseValidatingContext<TOptions> where TOptions : AuthenticationOptions
{
/// <summary>
/// Initializes base class used for certain event contexts
/// </summary>
protected BaseValidatingTicketContext(HttpContext context, TOptions options, AuthenticationTicket ticket) : base(context, options)
{
Ticket = ticket;
}
/// <summary>
/// Contains the identity and properties for the application to authenticate. If the Validated method
/// is invoked with an AuthenticationTicket or ClaimsIdentity argument, that new value is assigned to
/// this property in addition to changing IsValidated to true.
/// </summary>
public AuthenticationTicket Ticket { get; private set; }
/// <summary>
/// Replaces the ticket information on this context and marks it as as validated by the application.
/// IsValidated becomes true and HasError becomes false as a result of calling.
/// </summary>
/// <param name="ticket">Assigned to the Ticket property</param>
/// <returns>True if the validation has taken effect.</returns>
public bool Validated(AuthenticationTicket ticket)
{
Ticket = ticket;
return Validated();
}
/// <summary>
/// Alters the ticket information on this context and marks it as as validated by the application.
/// IsValidated becomes true and HasError becomes false as a result of calling.
/// </summary>
/// <param name="principal">Assigned to the Ticket.Identity property</param>
/// <returns>True if the validation has taken effect.</returns>
public bool Validated(ClaimsPrincipal principal)
{
AuthenticationProperties properties = Ticket != null ? Ticket.Properties : new AuthenticationProperties();
return Validated(new AuthenticationTicket(principal, properties, Options.AuthenticationScheme));
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Threading.Tasks;
namespace OAuth.AspNet.AuthServer
{
internal static class DefaultBehavior
{
internal static readonly Func<OAuthValidateAuthorizeRequestContext, Task> ValidateAuthorizeRequest = context =>
{
context.Validated();
return Task.FromResult<object>(null);
};
internal static readonly Func<OAuthValidateTokenRequestContext, Task> ValidateTokenRequest = context =>
{
context.Validated();
return Task.FromResult<object>(null);
};
internal static readonly Func<OAuthGrantAuthorizationCodeContext, Task> GrantAuthorizationCode = context =>
{
if (context.Ticket != null && context.Ticket.Principal != null && context.Ticket.Principal.Identity.IsAuthenticated)
{
context.Validated();
}
return Task.FromResult<object>(null);
};
internal static readonly Func<OAuthGrantRefreshTokenContext, Task> GrantRefreshToken = context =>
{
if (context.Ticket != null && context.Ticket.Principal != null && context.Ticket.Principal.Identity.IsAuthenticated)
{
context.Validated();
}
return Task.FromResult<object>(null);
};
}
}

View File

@ -0,0 +1,14 @@
using System.Threading.Tasks;
namespace OAuth.AspNet.AuthServer
{
public interface IAuthenticationTokenProvider
{
void Create(AuthenticationTokenCreateContext context);
Task CreateAsync(AuthenticationTokenCreateContext context);
void Receive(AuthenticationTokenReceiveContext context);
Task ReceiveAsync(AuthenticationTokenReceiveContext context);
}
}

View File

@ -0,0 +1,172 @@
using System.Threading.Tasks;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Interface for OAuthAuthorizationServerOptions.Provider property used by Authorization
/// Server to communicate with the web application while processing requests.
/// </summary>
public interface IOAuthAuthorizationServerProvider
{
/// <summary>
/// Called to determine if an incoming request is treated as an Authorize or Token
/// endpoint. If Options.AuthorizeEndpointPath or Options.TokenEndpointPath
/// are assigned values, then handling this event is optional and context.IsAuthorizeEndpoint and context.IsTokenEndpoint
/// will already be true if the request path matches.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task MatchEndpoint(OAuthMatchContext context);
/// <summary>
/// Called to validate that the context.ClientId is a registered "client_id", and that the context.RedirectUri a "redirect_uri"
/// registered for that client. This only occurs when processing the Authorize endpoint. The application MUST implement this
/// call, and it MUST validate both of those factors before calling context.Validated. If the context.Validated method is called
/// with a given redirectUri parameter, then IsValidated will only become true if the incoming redirect URI matches the given redirect URI.
/// If context.Validated is not called the request will not proceed further.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context);
/// <summary>
/// Called to validate that the origin of the request is a registered "client_id", and that the correct credentials for that client are
/// present on the request. If the web application accepts Basic authentication credentials,
/// context.TryGetBasicCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request header. If the web
/// application accepts "client_id" and "client_secret" as form encoded POST parameters,
/// context.TryGetFormCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request body.
/// If context.Validated is not called the request will not proceed further.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context);
/// <summary>
/// Called for each request to the Authorize endpoint to determine if the request is valid and should continue.
/// The default behavior when using the OAuthAuthorizationServerProvider is to assume well-formed requests, with
/// validated client redirect URI, should continue processing. An application may add any additional constraints.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context);
/// <summary>
/// Called for each request to the Token endpoint to determine if the request is valid and should continue.
/// The default behavior when using the OAuthAuthorizationServerProvider is to assume well-formed requests, with
/// validated client credentials, should continue processing. An application may add any additional constraints.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task ValidateTokenRequest(OAuthValidateTokenRequestContext context);
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "authorization_code". This occurs after the Authorize
/// endpoint as redirected the user-agent back to the client with a "code" parameter, and the client is exchanging that for an "access_token".
/// The claims and properties
/// associated with the authorization code are present in the context.Ticket. The application must call context.Validated to instruct the Authorization
/// Server middleware to issue an access token based on those claims and properties. The call to context.Validated may be given a different
/// AuthenticationTicket or ClaimsIdentity in order to control which information flows from authorization code to access token.
/// The default behavior when using the OAuthAuthorizationServerProvider is to flow information from the authorization code to
/// the access token unmodified.
/// See also http://tools.ietf.org/html/rfc6749#section-4.1.3
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context);
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "refresh_token". This occurs if your application has issued a "refresh_token"
/// along with the "access_token", and the client is attempting to use the "refresh_token" to acquire a new "access_token", and possibly a new "refresh_token".
/// To issue a refresh token the an Options.RefreshTokenProvider must be assigned to create the value which is returned. The claims and properties
/// associated with the refresh token are present in the context.Ticket. The application must call context.Validated to instruct the
/// Authorization Server middleware to issue an access token based on those claims and properties. The call to context.Validated may
/// be given a different AuthenticationTicket or ClaimsIdentity in order to control which information flows from the refresh token to
/// the access token. The default behavior when using the OAuthAuthorizationServerProvider is to flow information from the refresh token to
/// the access token unmodified.
/// See also http://tools.ietf.org/html/rfc6749#section-6
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task GrantRefreshToken(OAuthGrantRefreshTokenContext context);
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "password". This occurs when the user has provided name and password
/// credentials directly into the client application's user interface, and the client application is using those to acquire an "access_token" and
/// optional "refresh_token". If the web application supports the
/// resource owner credentials grant type it must validate the context.Username and context.Password as appropriate. To issue an
/// access token the context.Validated must be called with a new ticket containing the claims about the resource owner which should be associated
/// with the access token. The application should take appropriate measures to ensure that the endpoint isn<73>t abused by malicious callers. .
/// The default behavior is to reject this grant type.
/// See also http://tools.ietf.org/html/rfc6749#section-4.3.2
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context);
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "client_credentials". This occurs when a registered client
/// application wishes to acquire an "access_token" to interact with protected resources on it's own behalf, rather than on behalf of an authenticated user.
/// If the web application supports the client credentials it may assume the context.ClientId has been validated by the ValidateClientAuthentication call.
/// To issue an access token the context.Validated must be called with a new ticket containing the claims about the client application which should be associated
/// with the access token. The application should take appropriate measures to ensure that the endpoint isn<73>t abused by malicious callers.
/// The default behavior is to reject this grant type.
/// See also http://tools.ietf.org/html/rfc6749#section-4.4.2
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task GrantClientCredentials(OAuthGrantClientCredentialsContext context);
/// <summary>
/// Called when a request to the Token andpoint arrives with a "grant_type" of any other value. If the application supports custom grant types
/// it is entirely responsible for determining if the request should result in an access_token. If context.Validated is called with ticket
/// information the response body is produced in the same way as the other standard grant types. If additional response parameters must be
/// included they may be added in the final TokenEndpoint call.
/// See also http://tools.ietf.org/html/rfc6749#section-4.5
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task GrantCustomExtension(OAuthGrantCustomExtensionContext context);
/// <summary>
/// Called at the final stage of an incoming Authorize endpoint request before the execution continues on to the web application component
/// responsible for producing the html response. Anything present in the OWIN pipeline following the Authorization Server may produce the
/// response for the Authorize page. If running on IIS any ASP.NET technology running on the server may produce the response for the
/// Authorize page. If the web application wishes to produce the response directly in the AuthorizeEndpoint call it may write to the
/// context.Response directly and should call context.RequestCompleted to stop other handlers from executing. If the web application wishes
/// to grant the authorization directly in the AuthorizeEndpoint call it cay call context.OwinContext.Authentication.SignIn with the
/// appropriate ClaimsIdentity and should call context.RequestCompleted to stop other handlers from executing.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context);
/// <summary>
/// Called at the final stage of a successful Token endpoint request. An application may implement this call in order to do any final
/// modification of the claims being used to issue access or refresh tokens. This call may also be used in order to add additional
/// response parameters to the Token endpoint's json response body.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task TokenEndpoint(OAuthTokenEndpointContext context);
/// <summary>
/// Called before the AuthorizationEndpoint redirects its response to the caller. The response could be the
/// token, when using implicit flow or the AuthorizationEndpoint when using authorization code flow.
/// An application may implement this call in order to do any final modification of the claims being used
/// to issue access or refresh tokens. This call may also be used in order to add additional
/// response parameters to the authorization endpoint's response.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context);
/// <summary>
/// Called before the TokenEndpoint redirects its response to the caller.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task TokenEndpointResponse(OAuthTokenEndpointResponseContext context);
}
}

View File

@ -0,0 +1,7 @@
SOURCE_DIR=$(HOME)/workspace/yavsc
MAKEFILE_DIR=$(SOURCE_DIR)/scripts/build/make
include $(MAKEFILE_DIR)/versioning.mk
include $(MAKEFILE_DIR)/dnx.mk
all: $(BINTARGETPATH)

View File

@ -0,0 +1,808 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.AspNet.Http.Features.Authentication;
using Microsoft.AspNet.WebUtilities;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace OAuth.AspNet.AuthServer
{
public class OAuthAuthorizationServerHandler : AuthenticationHandler<OAuthAuthorizationServerOptions>
{
#region non-Public Members
private AuthorizeEndpointRequest _authorizeEndpointRequest;
private OAuthValidateClientRedirectUriContext _clientContext;
private Task SendErrorAsJsonAsync(BaseValidatingContext<OAuthAuthorizationServerOptions> validatingContext)
{
string error = validatingContext.HasError ? validatingContext.Error : Constants.Errors.InvalidRequest;
string errorDescription = validatingContext.HasError ? validatingContext.ErrorDescription : null;
string errorUri = validatingContext.HasError ? validatingContext.ErrorUri : null;
string body;
MemoryStream stream, memoryStream = null;
StreamWriter streamWriter = null;
try
{
stream = memoryStream = new MemoryStream();
streamWriter = new StreamWriter(memoryStream);
using (var writer = new JsonTextWriter(streamWriter))
{
memoryStream = null;
streamWriter = null;
writer.WriteStartObject();
writer.WritePropertyName(Constants.Parameters.Error);
writer.WriteValue(error);
if (!string.IsNullOrEmpty(errorDescription))
{
writer.WritePropertyName(Constants.Parameters.ErrorDescription);
writer.WriteValue(errorDescription);
}
if (!string.IsNullOrEmpty(errorUri))
{
writer.WritePropertyName(Constants.Parameters.ErrorUri);
writer.WriteValue(errorUri);
}
writer.WriteEndObject();
writer.Flush();
body = Encoding.UTF8.GetString(stream.ToArray());
}
}
finally
{
if (memoryStream != null)
memoryStream.Dispose();
}
Response.StatusCode = 400;
Response.ContentType = "application/json;charset=UTF-8";
Response.Headers["Cache-Control"] = "no-cache";
Response.Headers["Pragma"] = "no-cache";
Response.Headers["Expires"] = "-1";
Response.Headers["Content-Length"] = body.Length.ToString(CultureInfo.InvariantCulture);
return Response.WriteAsync(body, Context.RequestAborted);
}
private async Task<bool> SendErrorPageAsync(string error, string errorDescription, string errorUri)
{
Response.StatusCode = 400;
Response.Headers["Cache-Control"] = "no-cache";
Response.Headers["Pragma"] = "no-cache";
Response.Headers["Expires"] = "-1";
if (Options.ApplicationCanDisplayErrors)
{
Context.Items["oauth.Error"] = error;
Context.Items["oauth.ErrorDescription"] = errorDescription;
Context.Items["oauth.ErrorUri"] = errorUri;
// request is not handled - pass through to application for rendering
return false;
}
var memory = new MemoryStream();
string body;
using (var writer = new StreamWriter(memory))
{
writer.WriteLine("error: {0}", error);
if (!string.IsNullOrEmpty(errorDescription))
{
writer.WriteLine("error_description: {0}", errorDescription);
}
if (!string.IsNullOrEmpty(errorUri))
{
writer.WriteLine("error_uri: {0}", errorUri);
}
writer.Flush();
body = Encoding.UTF8.GetString(memory.ToArray());
}
Response.ContentType = "text/plain;charset=UTF-8";
Response.Headers["Content-Length"] = body.Length.ToString(CultureInfo.InvariantCulture);
await Response.WriteAsync(body, Context.RequestAborted);
// request is handled, does not pass on to application
return true;
}
private Task<bool> SendErrorRedirectAsync(OAuthValidateClientRedirectUriContext clientContext, BaseValidatingContext<OAuthAuthorizationServerOptions> validatingContext)
{
if (clientContext == null)
{
throw new ArgumentNullException("clientContext");
}
string error = validatingContext.HasError ? validatingContext.Error : Constants.Errors.InvalidRequest;
string errorDescription = validatingContext.HasError ? validatingContext.ErrorDescription : null;
string errorUri = validatingContext.HasError ? validatingContext.ErrorUri : null;
if (!clientContext.IsValidated)
{
// write error in response body if client_id or redirect_uri have not been validated
return SendErrorPageAsync(error, errorDescription, errorUri);
}
// redirect with error if client_id and redirect_uri have been validated
string location = QueryHelpers.AddQueryString(clientContext.RedirectUri, Constants.Parameters.Error, error);
if (!string.IsNullOrEmpty(errorDescription))
{
location = QueryHelpers.AddQueryString(location, Constants.Parameters.ErrorDescription, errorDescription);
}
if (!string.IsNullOrEmpty(errorUri))
{
location = QueryHelpers.AddQueryString(location, Constants.Parameters.ErrorUri, errorUri);
}
Response.Redirect(location);
// request is handled, does not pass on to application
return Task.FromResult(true);
}
private static AuthenticationTicket ReturnOutcome(OAuthValidateTokenRequestContext validatingContext, BaseValidatingContext<OAuthAuthorizationServerOptions> grantContext, AuthenticationTicket ticket, string defaultError)
{
if (!validatingContext.IsValidated)
return null;
if (!grantContext.IsValidated)
{
if (grantContext.HasError)
{
validatingContext.SetError(grantContext.Error, grantContext.ErrorDescription, grantContext.ErrorUri);
}
else
{
validatingContext.SetError(defaultError);
}
return null;
}
if (ticket == null)
{
validatingContext.SetError(defaultError);
return null;
}
return ticket;
}
private async Task<AuthenticationTicket> InvokeTokenEndpointAuthorizationCodeGrantAsync(OAuthValidateTokenRequestContext validatingContext, DateTimeOffset currentUtc)
{
TokenEndpointRequest tokenEndpointRequest = validatingContext.TokenRequest;
var authorizationCodeContext = new AuthenticationTokenReceiveContext(Context, Options.AuthorizationCodeFormat, tokenEndpointRequest.AuthorizationCodeGrant.Code);
await Options.AuthorizationCodeProvider.ReceiveAsync(authorizationCodeContext);
AuthenticationTicket ticket = authorizationCodeContext.Ticket;
if (ticket == null)
{
Logger.LogError("invalid authorization code");
validatingContext.SetError(Constants.Errors.InvalidGrant);
return null;
}
if (!ticket.Properties.ExpiresUtc.HasValue ||
ticket.Properties.ExpiresUtc < currentUtc)
{
Logger.LogError("expired authorization code");
validatingContext.SetError(Constants.Errors.InvalidGrant);
return null;
}
string clientId;
if (!ticket.Properties.Items.TryGetValue(Constants.Extra.ClientId, out clientId) ||
!string.Equals(clientId, validatingContext.ClientContext.ClientId, StringComparison.Ordinal))
{
Logger.LogError("authorization code does not contain matching client_id");
validatingContext.SetError(Constants.Errors.InvalidGrant);
return null;
}
string redirectUri;
if (ticket.Properties.Items.TryGetValue(Constants.Extra.RedirectUri, out redirectUri))
{
ticket.Properties.Items.Remove(Constants.Extra.RedirectUri);
if (!string.Equals(redirectUri, tokenEndpointRequest.AuthorizationCodeGrant.RedirectUri, StringComparison.Ordinal))
{
Logger.LogError("authorization code does not contain matching redirect_uri");
validatingContext.SetError(Constants.Errors.InvalidGrant);
return null;
}
}
await Options.Provider.ValidateTokenRequest(validatingContext);
var grantContext = new OAuthGrantAuthorizationCodeContext(
Context, Options, ticket);
if (validatingContext.IsValidated)
{
await Options.Provider.GrantAuthorizationCode(grantContext);
}
return ReturnOutcome(validatingContext, grantContext, grantContext.Ticket, Constants.Errors.InvalidGrant);
}
private async Task<AuthenticationTicket> InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(OAuthValidateTokenRequestContext validatingContext, DateTimeOffset currentUtc)
{
TokenEndpointRequest tokenEndpointRequest = validatingContext.TokenRequest;
await Options.Provider.ValidateTokenRequest(validatingContext);
var grantContext = new OAuthGrantResourceOwnerCredentialsContext(
Context,
Options,
validatingContext.ClientContext.ClientId,
tokenEndpointRequest.ResourceOwnerPasswordCredentialsGrant.UserName,
tokenEndpointRequest.ResourceOwnerPasswordCredentialsGrant.Password,
tokenEndpointRequest.ResourceOwnerPasswordCredentialsGrant.Scope
);
if (validatingContext.IsValidated)
await Options.Provider.GrantResourceOwnerCredentials(grantContext);
return ReturnOutcome(validatingContext, grantContext, grantContext.Ticket, Constants.Errors.InvalidGrant);
}
private async Task<AuthenticationTicket> InvokeTokenEndpointClientCredentialsGrantAsync(OAuthValidateTokenRequestContext validatingContext, DateTimeOffset currentUtc)
{
TokenEndpointRequest tokenEndpointRequest = validatingContext.TokenRequest;
await Options.Provider.ValidateTokenRequest(validatingContext);
if (!validatingContext.IsValidated)
return null;
var grantContext = new OAuthGrantClientCredentialsContext(Context, Options, validatingContext.ClientContext.ClientId, tokenEndpointRequest.ClientCredentialsGrant.Scope);
await Options.Provider.GrantClientCredentials(grantContext);
return ReturnOutcome(validatingContext, grantContext, grantContext.Ticket, Constants.Errors.UnauthorizedClient);
}
private async Task<AuthenticationTicket> InvokeTokenEndpointRefreshTokenGrantAsync(OAuthValidateTokenRequestContext validatingContext, DateTimeOffset currentUtc)
{
TokenEndpointRequest tokenEndpointRequest = validatingContext.TokenRequest;
var refreshTokenContext = new AuthenticationTokenReceiveContext(Context, Options.RefreshTokenFormat, tokenEndpointRequest.RefreshTokenGrant.RefreshToken);
await Options.RefreshTokenProvider.ReceiveAsync(refreshTokenContext);
AuthenticationTicket ticket = refreshTokenContext.Ticket;
if (ticket == null)
{
Logger.LogError("invalid refresh token");
validatingContext.SetError(Constants.Errors.InvalidGrant);
return null;
}
if (!ticket.Properties.ExpiresUtc.HasValue || ticket.Properties.ExpiresUtc < currentUtc)
{
Logger.LogError("expired refresh token");
validatingContext.SetError(Constants.Errors.InvalidGrant);
return null;
}
await Options.Provider.ValidateTokenRequest(validatingContext);
var grantContext = new OAuthGrantRefreshTokenContext(Context, Options, ticket, validatingContext.ClientContext.ClientId);
if (validatingContext.IsValidated)
await Options.Provider.GrantRefreshToken(grantContext);
return ReturnOutcome(validatingContext, grantContext, grantContext.Ticket, Constants.Errors.InvalidGrant);
}
private async Task<AuthenticationTicket> InvokeTokenEndpointCustomGrantAsync(OAuthValidateTokenRequestContext validatingContext, DateTimeOffset currentUtc)
{
TokenEndpointRequest tokenEndpointRequest = validatingContext.TokenRequest;
await Options.Provider.ValidateTokenRequest(validatingContext);
var grantContext = new OAuthGrantCustomExtensionContext(Context, Options, validatingContext.ClientContext.ClientId, tokenEndpointRequest.GrantType, tokenEndpointRequest.CustomExtensionGrant.Parameters);
if (validatingContext.IsValidated)
await Options.Provider.GrantCustomExtension(grantContext);
return ReturnOutcome(validatingContext, grantContext, grantContext.Ticket, Constants.Errors.UnsupportedGrantType);
}
private async Task<bool> InvokeAuthorizeEndpointAsync()
{
var authorizeRequest = new AuthorizeEndpointRequest(Request.Query);
var clientContext = new OAuthValidateClientRedirectUriContext(Context, Options, authorizeRequest.ClientId, authorizeRequest.RedirectUri);
if (!string.IsNullOrEmpty(authorizeRequest.RedirectUri))
{
bool acceptableUri = true;
Uri validatingUri;
if (!Uri.TryCreate(authorizeRequest.RedirectUri, UriKind.Absolute, out validatingUri))
{
// The redirection endpoint URI MUST be an absolute URI
// http://tools.ietf.org/html/rfc6749#section-3.1.2
acceptableUri = false;
}
else if (!string.IsNullOrEmpty(validatingUri.Fragment))
{
// The endpoint URI MUST NOT include a fragment component.
// http://tools.ietf.org/html/rfc6749#section-3.1.2
acceptableUri = false;
}
else if (!Options.AllowInsecureHttp && string.Equals(validatingUri.Scheme, "http", StringComparison.OrdinalIgnoreCase))
{
// The redirection endpoint SHOULD require the use of TLS
// http://tools.ietf.org/html/rfc6749#section-3.1.2.1
acceptableUri = false;
}
if (!acceptableUri)
{
clientContext.SetError(Constants.Errors.InvalidRequest);
return await SendErrorRedirectAsync(clientContext, clientContext);
}
}
await Options.Provider.ValidateClientRedirectUri(clientContext);
if (!clientContext.IsValidated)
{
Logger.LogVerbose("Unable to validate client information");
return await SendErrorRedirectAsync(clientContext, clientContext);
}
var validatingContext = new OAuthValidateAuthorizeRequestContext(Context, Options, authorizeRequest, clientContext);
if (string.IsNullOrEmpty(authorizeRequest.ResponseType))
{
Logger.LogVerbose("Authorize endpoint request missing required response_type parameter");
validatingContext.SetError(Constants.Errors.InvalidRequest);
}
else if (!authorizeRequest.IsAuthorizationCodeGrantType && !authorizeRequest.IsImplicitGrantType)
{
Logger.LogVerbose("Authorize endpoint request contains unsupported response_type parameter");
validatingContext.SetError(Constants.Errors.UnsupportedResponseType);
}
else
{
await Options.Provider.ValidateAuthorizeRequest(validatingContext);
}
if (!validatingContext.IsValidated)
{
// an invalid request is not processed further
return await SendErrorRedirectAsync(clientContext, validatingContext);
}
_clientContext = clientContext;
_authorizeEndpointRequest = authorizeRequest;
var authorizeEndpointContext = new OAuthAuthorizeEndpointContext(Context, Options, authorizeRequest);
await Options.Provider.AuthorizeEndpoint(authorizeEndpointContext);
return authorizeEndpointContext.IsRequestCompleted;
}
private async Task InvokeTokenEndpointAsync()
{
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
// remove milliseconds in case they don't round-trip
currentUtc = currentUtc.Subtract(TimeSpan.FromMilliseconds(currentUtc.Millisecond));
IFormCollection form = await Request.ReadFormAsync();
var clientContext = new OAuthValidateClientAuthenticationContext(Context, Options, form);
await Options.Provider.ValidateClientAuthentication(clientContext);
if (!clientContext.IsValidated)
{
Logger.LogError("clientID is not valid.");
if (!clientContext.HasError)
clientContext.SetError(Constants.Errors.InvalidClient);
await SendErrorAsJsonAsync(clientContext);
return;
}
var tokenEndpointRequest = new TokenEndpointRequest(form);
var validatingContext = new OAuthValidateTokenRequestContext(Context, Options, tokenEndpointRequest, clientContext);
AuthenticationTicket ticket = null;
if (tokenEndpointRequest.IsAuthorizationCodeGrantType)
{
// Authorization Code Grant http://tools.ietf.org/html/rfc6749#section-4.1
// Access Token Request http://tools.ietf.org/html/rfc6749#section-4.1.3
ticket = await InvokeTokenEndpointAuthorizationCodeGrantAsync(validatingContext, currentUtc);
}
else if (tokenEndpointRequest.IsResourceOwnerPasswordCredentialsGrantType)
{
// Resource Owner Password Credentials Grant http://tools.ietf.org/html/rfc6749#section-4.3
// Access Token Request http://tools.ietf.org/html/rfc6749#section-4.3.2
ticket = await InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(validatingContext, currentUtc);
}
else if (tokenEndpointRequest.IsClientCredentialsGrantType)
{
// Client Credentials Grant http://tools.ietf.org/html/rfc6749#section-4.4
// Access Token Request http://tools.ietf.org/html/rfc6749#section-4.4.2
ticket = await InvokeTokenEndpointClientCredentialsGrantAsync(validatingContext, currentUtc);
}
else if (tokenEndpointRequest.IsRefreshTokenGrantType)
{
// Refreshing an Access Token
// http://tools.ietf.org/html/rfc6749#section-6
ticket = await InvokeTokenEndpointRefreshTokenGrantAsync(validatingContext, currentUtc);
}
else if (tokenEndpointRequest.IsCustomExtensionGrantType)
{
// Defining New Authorization Grant Types
// http://tools.ietf.org/html/rfc6749#section-8.3
ticket = await InvokeTokenEndpointCustomGrantAsync(validatingContext, currentUtc);
}
else
{
// Error Response http://tools.ietf.org/html/rfc6749#section-5.2
// The authorization grant type is not supported by the
// authorization server.
Logger.LogError("grant type is not recognized");
validatingContext.SetError(Constants.Errors.UnsupportedGrantType);
}
if (ticket == null)
{
await SendErrorAsJsonAsync(validatingContext);
return;
}
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(Options.AccessTokenExpireTimeSpan);
var tokenEndpointContext = new OAuthTokenEndpointContext(Context, Options, ticket, tokenEndpointRequest);
await Options.Provider.TokenEndpoint(tokenEndpointContext);
if (tokenEndpointContext.TokenIssued)
{
ticket = new AuthenticationTicket(tokenEndpointContext.Principal, tokenEndpointContext.Properties, tokenEndpointContext.Options.AuthenticationScheme);
}
else
{
Logger.LogError("Token was not issued to tokenEndpointContext");
validatingContext.SetError(Constants.Errors.InvalidGrant);
await SendErrorAsJsonAsync(validatingContext);
return;
}
var accessTokenContext = new AuthenticationTokenCreateContext(
Context,
Options.AccessTokenFormat,
ticket);
await Options.AccessTokenProvider.CreateAsync(accessTokenContext);
string accessToken = accessTokenContext.Token;
if (string.IsNullOrEmpty(accessToken))
{
accessToken = accessTokenContext.SerializeTicket();
}
DateTimeOffset? accessTokenExpiresUtc = ticket.Properties.ExpiresUtc;
var refreshTokenCreateContext = new AuthenticationTokenCreateContext(Context, Options.RefreshTokenFormat, accessTokenContext.Ticket);
await Options.RefreshTokenProvider.CreateAsync(refreshTokenCreateContext);
string refreshToken = refreshTokenCreateContext.Token;
var tokenEndpointResponseContext = new OAuthTokenEndpointResponseContext(Context, Options, ticket, tokenEndpointRequest, accessToken, tokenEndpointContext.AdditionalResponseParameters);
await Options.Provider.TokenEndpointResponse(tokenEndpointResponseContext);
MemoryStream stream, memoryStream = null;
string body;
try
{
stream = memoryStream = new MemoryStream();
using (var writer = new JsonTextWriter(new StreamWriter(memoryStream)))
{
memoryStream = null;
writer.WriteStartObject();
writer.WritePropertyName(Constants.Parameters.AccessToken);
writer.WriteValue(accessToken);
writer.WritePropertyName(Constants.Parameters.TokenType);
writer.WriteValue(Constants.TokenTypes.Bearer);
if (accessTokenExpiresUtc.HasValue)
{
TimeSpan? expiresTimeSpan = accessTokenExpiresUtc - currentUtc;
var expiresIn = (long)expiresTimeSpan.Value.TotalSeconds;
if (expiresIn > 0)
{
writer.WritePropertyName(Constants.Parameters.ExpiresIn);
writer.WriteValue(expiresIn);
}
}
if (!string.IsNullOrEmpty(refreshToken))
{
writer.WritePropertyName(Constants.Parameters.RefreshToken);
writer.WriteValue(refreshToken);
}
foreach (var additionalResponseParameter in tokenEndpointResponseContext.AdditionalResponseParameters)
{
writer.WritePropertyName(additionalResponseParameter.Key);
writer.WriteValue(additionalResponseParameter.Value);
}
writer.WriteEndObject();
writer.Flush();
body = Encoding.UTF8.GetString(stream.ToArray());
Response.ContentType = "application/json;charset=UTF-8";
Response.Headers["Cache-Control"] = "no-cache";
Response.Headers["Pragma"] = "no-cache";
Response.Headers["Expires"] = "-1";
Response.ContentLength = Encoding.UTF8.GetByteCount(body);
}
}
finally
{
if (memoryStream != null)
memoryStream.Dispose();
}
await Response.WriteAsync(body, Encoding.UTF8, Context.RequestAborted);
}
private class Appender
{
private readonly char _delimiter;
private readonly StringBuilder _sb;
private bool _hasDelimiter;
public Appender(string value, char delimiter)
{
_sb = new StringBuilder(value);
_delimiter = delimiter;
_hasDelimiter = value.IndexOf(delimiter) != -1;
}
public Appender Append(string name, string value)
{
_sb.Append(_hasDelimiter ? '&' : _delimiter)
.Append(Uri.EscapeDataString(name))
.Append('=')
.Append(Uri.EscapeDataString(value));
_hasDelimiter = true;
return this;
}
public override string ToString()
{
return _sb.ToString();
}
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
return Task.FromResult<AuthenticateResult>(null);
}
#endregion
#region Public Members
public override async Task<bool> HandleRequestAsync()
{
var matchRequestContext = new OAuthMatchContext(Context, Options);
if (Options.AuthorizeEndpointPath.HasValue && Options.AuthorizeEndpointPath == Request.Path)
{
matchRequestContext.MatchesAuthorizeEndpoint();
}
else if (Options.TokenEndpointPath.HasValue && Options.TokenEndpointPath == Request.Path)
{
matchRequestContext.MatchesTokenEndpoint();
}
await Options.Provider.MatchEndpoint(matchRequestContext);
if (matchRequestContext.HandledResponse)
return true;
if (matchRequestContext.Skipped)
return false;
if (matchRequestContext.IsAuthorizeEndpoint || matchRequestContext.IsTokenEndpoint)
{
if (!Options.AllowInsecureHttp && !Context.Request.IsHttps)
{
Logger.LogWarning("Authorization server ignoring http request because AllowInsecureHttp is false.");
return false;
}
if (matchRequestContext.IsAuthorizeEndpoint)
return await InvokeAuthorizeEndpointAsync();
if (matchRequestContext.IsTokenEndpoint)
{
await InvokeTokenEndpointAsync();
return true;
}
}
return false;
}
protected override async Task HandleSignInAsync(SignInContext context)
{
// only successful results of an authorize request are altered
if (_clientContext == null || _authorizeEndpointRequest == null || Response.StatusCode != 200)
return;
if (context?.Principal == null)
return;
AuthenticationResponseGrant signin = new AuthenticationResponseGrant(context.Principal, new AuthenticationProperties(context.Properties));
var returnParameter = new Dictionary<string, string>();
if (_authorizeEndpointRequest.IsAuthorizationCodeGrantType)
{
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
signin.Properties.IssuedUtc = currentUtc;
signin.Properties.ExpiresUtc = currentUtc.Add(Options.AuthorizationCodeExpireTimeSpan);
// associate client_id with all subsequent tickets
signin.Properties.Items[Constants.Extra.ClientId] = _authorizeEndpointRequest.ClientId;
if (!string.IsNullOrEmpty(_authorizeEndpointRequest.RedirectUri))
{
// keep original request parameter for later comparison
signin.Properties.Items[Constants.Extra.RedirectUri] = _authorizeEndpointRequest.RedirectUri;
}
var tokenCreationContext = new AuthenticationTokenCreateContext(Context, Options.AuthorizationCodeFormat, new AuthenticationTicket(signin.Principal, signin.Properties, signin.Identity.AuthenticationType));
await Options.AuthorizationCodeProvider.CreateAsync(tokenCreationContext);
string code = tokenCreationContext.Token;
if (string.IsNullOrEmpty(code))
{
Logger.LogError("response_type code requires an Options.AuthorizationCodeProvider implementing a single-use token.");
var errorContext = new OAuthValidateAuthorizeRequestContext(Context, Options, _authorizeEndpointRequest, _clientContext);
errorContext.SetError(Constants.Errors.UnsupportedResponseType);
await SendErrorRedirectAsync(_clientContext, errorContext);
return;
}
var authResponseContext = new OAuthAuthorizationEndpointResponseContext(Context, Options, new AuthenticationTicket(signin.Principal, signin.Properties, signin.Identity.AuthenticationType), _authorizeEndpointRequest, null, code);
await Options.Provider.AuthorizationEndpointResponse(authResponseContext);
foreach (var parameter in authResponseContext.AdditionalResponseParameters)
{
returnParameter[parameter.Key] = parameter.Value.ToString();
}
returnParameter[Constants.Parameters.Code] = code;
if (!string.IsNullOrEmpty(_authorizeEndpointRequest.State))
{
returnParameter[Constants.Parameters.State] = _authorizeEndpointRequest.State;
}
string location = string.Empty;
if (_authorizeEndpointRequest.IsFormPostResponseMode)
{
location = Options.FormPostEndpoint.ToString();
returnParameter[Constants.Parameters.RedirectUri] = _clientContext.RedirectUri;
}
else
{
location = _clientContext.RedirectUri;
}
foreach (var key in returnParameter.Keys)
{
location = QueryHelpers.AddQueryString(location, key, returnParameter[key]);
}
Response.Redirect(location);
}
else if (_authorizeEndpointRequest.IsImplicitGrantType)
{
string location = _clientContext.RedirectUri;
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
signin.Properties.IssuedUtc = currentUtc;
signin.Properties.ExpiresUtc = currentUtc.Add(Options.AccessTokenExpireTimeSpan);
// associate client_id with access token
signin.Properties.Items[Constants.Extra.ClientId] = _authorizeEndpointRequest.ClientId;
var accessTokenContext = new AuthenticationTokenCreateContext(Context, Options.AccessTokenFormat, new AuthenticationTicket(signin.Principal, signin.Properties, signin.Identity.AuthenticationType));
await Options.AccessTokenProvider.CreateAsync(accessTokenContext);
string accessToken = accessTokenContext.Token;
if (string.IsNullOrEmpty(accessToken))
{
accessToken = accessTokenContext.SerializeTicket();
}
DateTimeOffset? accessTokenExpiresUtc = accessTokenContext.Ticket.Properties.ExpiresUtc;
var appender = new Appender(location, '#');
appender.Append(Constants.Parameters.AccessToken, accessToken)
.Append(Constants.Parameters.TokenType, Constants.TokenTypes.Bearer);
if (accessTokenExpiresUtc.HasValue)
{
TimeSpan? expiresTimeSpan = accessTokenExpiresUtc - currentUtc;
var expiresIn = (long)(expiresTimeSpan.Value.TotalSeconds + .5);
appender.Append(Constants.Parameters.ExpiresIn, expiresIn.ToString(CultureInfo.InvariantCulture));
}
if (!string.IsNullOrEmpty(_authorizeEndpointRequest.State))
{
appender.Append(Constants.Parameters.State, _authorizeEndpointRequest.State);
}
var authResponseContext = new OAuthAuthorizationEndpointResponseContext(Context, Options, new AuthenticationTicket(signin.Principal, signin.Properties, signin.Identity.AuthenticationType), _authorizeEndpointRequest, accessToken, null);
await Options.Provider.AuthorizationEndpointResponse(authResponseContext);
foreach (var parameter in authResponseContext.AdditionalResponseParameters)
{
appender.Append(parameter.Key, parameter.Value.ToString());
}
Response.Redirect(appender.ToString());
}
}
#endregion
}
}

View File

@ -0,0 +1,85 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.DataProtection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.WebEncoders;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Authorization Server middleware component which is added to an OWIN pipeline. This class is not
/// created by application code directly, instead it is added by calling the the IAppBuilder UseOAuthAuthorizationServer
/// extension method.
/// </summary>
public class OAuthAuthorizationServerMiddleware : AuthenticationMiddleware<OAuthAuthorizationServerOptions>
{
/// <summary>
/// Authorization Server middleware component which is added to an OWIN pipeline. This constructor is not
/// called by application code directly, instead it is added by calling the the IAppBuilder UseOAuthAuthorizationServer
/// extension method.
/// </summary>
public OAuthAuthorizationServerMiddleware(RequestDelegate next, OAuthAuthorizationServerOptions options, ILoggerFactory loggerFactory, IDataProtectionProvider dataProtectionProvider, IUrlEncoder encoder) : base(next, options, loggerFactory, encoder)
{
if (Options.Provider == null)
{
Options.Provider = new OAuthAuthorizationServerProvider();
}
if (Options.AuthorizationCodeFormat == null)
{
IDataProtector dataProtecter = dataProtectionProvider.CreateProtector(typeof(OAuthAuthorizationServerMiddleware).FullName, "Authentication_Code", "v1");
Options.AuthorizationCodeFormat = new TicketDataFormat(dataProtecter);
}
if (Options.RefreshTokenFormat == null)
{
IDataProtector dataProtecter = dataProtectionProvider.CreateProtector(typeof(OAuthAuthorizationServerMiddleware).FullName, "Refresh_Token", "v1");
Options.RefreshTokenFormat = new TicketDataFormat(dataProtecter);
}
if (Options.TokenDataProtector == null)
{
#if DNXCORE50
Options.TokenDataProtector = new DataProtectionProvider(new DirectoryInfo(Environment.GetEnvironmentVariable("Temp"))).CreateProtector("OAuth.AspNet.AuthServer");
#else
Options.TokenDataProtector = new MonoDataProtectionProvider("OAuth.AspNet.AuthServer").CreateProtector("OAuth.Data.Protector");
#endif
}
if (Options.AccessTokenFormat == null)
{
IDataProtector dataProtecter = Options.TokenDataProtector.CreateProtector("Access_Token", "v1");
Options.AccessTokenFormat = new TicketDataFormat(dataProtecter);
}
if (Options.AuthorizationCodeProvider == null)
{
Options.AuthorizationCodeProvider = new AuthenticationTokenProvider();
}
if (Options.AccessTokenProvider == null)
{
Options.AccessTokenProvider = new AuthenticationTokenProvider();
}
if (Options.RefreshTokenProvider == null)
{
Options.RefreshTokenProvider = new AuthenticationTokenProvider();
}
}
/// <summary>
/// Called by the AuthenticationMiddleware base class to create a per-request handler.
/// </summary>
/// <returns>A new instance of the request handler</returns>
protected override AuthenticationHandler<OAuthAuthorizationServerOptions> CreateHandler()
{
return new OAuthAuthorizationServerHandler();
}
}
}

View File

@ -0,0 +1,136 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.DataProtection;
using Microsoft.AspNet.Http;
using System;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Options class provides information needed to control Authorization Server middleware behavior
/// </summary>
public class OAuthAuthorizationServerOptions : AuthenticationOptions
{
/// <summary>
/// Creates an instance of authorization server options with default values.
/// </summary>
public OAuthAuthorizationServerOptions()
{
AuthenticationScheme = OAuthDefaults.AuthenticationType;
AuthorizationCodeExpireTimeSpan = TimeSpan.FromMinutes(5);
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(20);
SystemClock = new SystemClock();
}
/// <summary>
/// The request path where client applications will redirect the user-agent in order to
/// obtain user consent to issue a token. Must begin with a leading slash, like "/Authorize".
/// </summary>
public PathString AuthorizeEndpointPath { get; set; }
/// <summary>
/// The request path client applications communicate with directly as part of the OAuth protocol.
/// Must begin with a leading slash, like "/Token". If the client is issued a client_secret, it must
/// be provided to this endpoint.
/// </summary>
public PathString TokenEndpointPath { get; set; }
/// <summary>
/// The object provided by the application to process events raised by the Authorization Server middleware.
/// The application may implement the interface fully, or it may create an instance of OAuthAuthorizationServerProvider
/// and assign delegates only to the events it wants to process.
/// </summary>
public IOAuthAuthorizationServerProvider Provider { get; set; }
/// <summary>
/// The data format used to protect and unprotect the information contained in the authorization code.
/// If not provided by the application the default data protection provider depends on the host server.
/// The SystemWeb host on IIS will use ASP.NET machine key data protection, and HttpListener and other self-hosted
/// servers will use DPAPI data protection.
/// </summary>
public ISecureDataFormat<AuthenticationTicket> AuthorizationCodeFormat { get; set; }
/// <summary>
/// The data protection provider used to protect token information.
/// </summary>
public IDataProtector TokenDataProtector { get; set; }
/// <summary>
/// The data format used to protect the information contained in the access token.
/// If not provided by the application the default data protection provider depends on the host server.
/// The SystemWeb host on IIS will use ASP.NET machine key data protection, and HttpListener and other self-hosted
/// servers will use DPAPI data protection. If a different access token
/// provider or format is assigned, a compatible instance must be assigned to the OAuthBearerAuthenticationOptions.AccessTokenProvider
/// or OAuthBearerAuthenticationOptions.AccessTokenFormat property of the resource server.
/// </summary>
public ISecureDataFormat<AuthenticationTicket> AccessTokenFormat { get; set; }
/// <summary>
/// The data format used to protect and unprotect the information contained in the refresh token.
/// If not provided by the application the default data protection provider depends on the host server.
/// The SystemWeb host on IIS will use ASP.NET machine key data protection, and HttpListener and other self-hosted
/// servers will use DPAPI data protection.
/// </summary>
public ISecureDataFormat<AuthenticationTicket> RefreshTokenFormat { get; set; }
/// <summary>
/// The period of time the authorization code remains valid after being issued. The default is five minutes.
/// This time span must also take into account clock synchronization between servers in a web farm, so a very
/// brief value could result in unexpectedly expired tokens.
/// </summary>
public TimeSpan AuthorizationCodeExpireTimeSpan { get; set; }
/// <summary>
/// The period of time the access token remains valid after being issued. The default is twenty minutes.
/// The client application is expected to refresh or acquire a new access token after the token has expired.
/// </summary>
public TimeSpan AccessTokenExpireTimeSpan { get; set; }
/// <summary>
/// Produces a single-use authorization code to return to the client application. For the OAuth server to be secure the
/// application MUST provide an instance for AuthorizationCodeProvider where the token produced by the OnCreate or OnCreateAsync event
/// is considered valid for only one call to OnReceive or OnReceiveAsync.
/// </summary>
public IAuthenticationTokenProvider AuthorizationCodeProvider { get; set; }
/// <summary>
/// Produces a bearer token the client application will typically be providing to resource server as the authorization bearer
/// http request header. If not provided the token produced on the server's default data protection. If a different access token
/// provider or format is assigned, a compatible instance must be assigned to the OAuthBearerAuthenticationOptions.AccessTokenProvider
/// or OAuthBearerAuthenticationOptions.AccessTokenFormat property of the resource server.
/// </summary>
public IAuthenticationTokenProvider AccessTokenProvider { get; set; }
/// <summary>
/// Produces a refresh token which may be used to produce a new access token when needed. If not provided the authorization server will
/// not return refresh tokens from the /Token endpoint.
/// </summary>
public IAuthenticationTokenProvider RefreshTokenProvider { get; set; }
/// <summary>
/// Set to true if the web application is able to render error messages on the /Authorize endpoint. This is only needed for cases where
/// the browser is not redirected back to the client application, for example, when the client_id or redirect_uri are incorrect. The
/// /Authorize endpoint should expect to see "oauth.Error", "oauth.ErrorDescription", "oauth.ErrorUri" properties added to the owin environment.
/// </summary>
public bool ApplicationCanDisplayErrors { get; set; }
/// <summary>
/// Used to know what the current clock time is when calculating or validating token expiration. When not assigned default is based on
/// DateTimeOffset.UtcNow. This is typically needed only for unit testing.
/// </summary>
public ISystemClock SystemClock { get; set; }
/// <summary>
/// True to allow authorize and token requests to arrive on http URI addresses, and to allow incoming
/// redirect_uri authorize request parameter to have http URI addresses.
/// </summary>
public bool AllowInsecureHttp { get; set; }
/// <summary>
/// Endpoint responsible for Form Post Response Mode
/// See also, http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
/// </summary>
public PathString FormPostEndpoint { get; set; }
}
}

View File

@ -0,0 +1,378 @@
using System;
using System.Threading.Tasks;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Default implementation of IOAuthAuthorizationServerProvider used by Authorization
/// Server to communicate with the web application while processing requests. OAuthAuthorizationServerProvider provides some default behavior,
/// may be used as a virtual base class, and offers delegate properties which may be used to
/// handle individual calls without declaring a new class type.
/// </summary>
public class OAuthAuthorizationServerProvider : IOAuthAuthorizationServerProvider
{
/// <summary>
/// Creates new instance of default provider behavior
/// </summary>
public OAuthAuthorizationServerProvider()
{
OnMatchEndpoint = context => Task.FromResult<object>(null);
OnValidateClientRedirectUri = context => Task.FromResult<object>(null);
OnValidateClientAuthentication = context => Task.FromResult<object>(null);
OnValidateAuthorizeRequest = DefaultBehavior.ValidateAuthorizeRequest;
OnValidateTokenRequest = DefaultBehavior.ValidateTokenRequest;
OnGrantAuthorizationCode = DefaultBehavior.GrantAuthorizationCode;
OnGrantResourceOwnerCredentials = context => Task.FromResult<object>(null);
OnGrantRefreshToken = DefaultBehavior.GrantRefreshToken;
OnGrantClientCredentials = context => Task.FromResult<object>(null);
OnGrantCustomExtension = context => Task.FromResult<object>(null);
OnAuthorizeEndpoint = context => Task.FromResult<object>(null);
OnTokenEndpoint = context => Task.FromResult<object>(null);
OnAuthorizationEndpointResponse = context => Task.FromResult<object>(null);
OnTokenEndpointResponse = context => Task.FromResult<object>(null);
}
/// <summary>
/// Called to determine if an incoming request is treated as an Authorize or Token
/// endpoint. If Options.AuthorizeEndpointPath or Options.TokenEndpointPath
/// are assigned values, then handling this event is optional and context.IsAuthorizeEndpoint and context.IsTokenEndpoint
/// will already be true if the request path matches.
/// </summary>
public Func<OAuthMatchContext, Task> OnMatchEndpoint { get; set; }
/// <summary>
/// Called to validate that the context.ClientId is a registered "client_id", and that the context.RedirectUri a "redirect_uri"
/// registered for that client. This only occurs when processing the Authorize endpoint. The application MUST implement this
/// call, and it MUST validate both of those factors before calling context.Validated. If the context.Validated method is called
/// with a given redirectUri parameter, then IsValidated will only become true if the incoming redirect URI matches the given redirect URI.
/// If context.Validated is not called the request will not proceed further.
/// </summary>
public Func<OAuthValidateClientRedirectUriContext, Task> OnValidateClientRedirectUri { get; set; }
/// <summary>
/// Called to validate that the origin of the request is a registered "client_id", and that the correct credentials for that client are
/// present on the request. If the web application accepts Basic authentication credentials,
/// context.TryGetBasicCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request header. If the web
/// application accepts "client_id" and "client_secret" as form encoded POST parameters,
/// context.TryGetFormCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request body.
/// If context.Validated is not called the request will not proceed further.
/// </summary>
public Func<OAuthValidateClientAuthenticationContext, Task> OnValidateClientAuthentication { get; set; }
/// <summary>
/// Called for each request to the Authorize endpoint to determine if the request is valid and should continue.
/// The default behavior when using the OAuthAuthorizationServerProvider is to assume well-formed requests, with
/// validated client redirect URI, should continue processing. An application may add any additional constraints.
/// </summary>
public Func<OAuthValidateAuthorizeRequestContext, Task> OnValidateAuthorizeRequest { get; set; }
/// <summary>
/// Called for each request to the Token endpoint to determine if the request is valid and should continue.
/// The default behavior when using the OAuthAuthorizationServerProvider is to assume well-formed requests, with
/// validated client credentials, should continue processing. An application may add any additional constraints.
/// </summary>
public Func<OAuthValidateTokenRequestContext, Task> OnValidateTokenRequest { get; set; }
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "authorization_code". This occurs after the Authorize
/// endpoint as redirected the user-agent back to the client with a "code" parameter, and the client is exchanging that for an "access_token".
/// The claims and properties
/// associated with the authorization code are present in the context.Ticket. The application must call context.Validated to instruct the Authorization
/// Server middleware to issue an access token based on those claims and properties. The call to context.Validated may be given a different
/// AuthenticationTicket or ClaimsIdentity in order to control which information flows from authorization code to access token.
/// The default behavior when using the OAuthAuthorizationServerProvider is to flow information from the authorization code to
/// the access token unmodified.
/// See also http://tools.ietf.org/html/rfc6749#section-4.1.3
/// </summary>
public Func<OAuthGrantAuthorizationCodeContext, Task> OnGrantAuthorizationCode { get; set; }
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "password". This occurs when the user has provided name and password
/// credentials directly into the client application's user interface, and the client application is using those to acquire an "access_token" and
/// optional "refresh_token". If the web application supports the
/// resource owner credentials grant type it must validate the context.Username and context.Password as appropriate. To issue an
/// access token the context.Validated must be called with a new ticket containing the claims about the resource owner which should be associated
/// with the access token. The application should take appropriate measures to ensure that the endpoint isn<73>t abused by malicious callers.
/// The default behavior is to reject this grant type.
/// See also http://tools.ietf.org/html/rfc6749#section-4.3.2
/// </summary>
public Func<OAuthGrantResourceOwnerCredentialsContext, Task> OnGrantResourceOwnerCredentials { get; set; }
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "client_credentials". This occurs when a registered client
/// application wishes to acquire an "access_token" to interact with protected resources on it's own behalf, rather than on behalf of an authenticated user.
/// If the web application supports the client credentials it may assume the context.ClientId has been validated by the ValidateClientAuthentication call.
/// To issue an access token the context.Validated must be called with a new ticket containing the claims about the client application which should be associated
/// with the access token. The application should take appropriate measures to ensure that the endpoint isn<73>t abused by malicious callers.
/// The default behavior is to reject this grant type.
/// See also http://tools.ietf.org/html/rfc6749#section-4.4.2
/// </summary>
public Func<OAuthGrantClientCredentialsContext, Task> OnGrantClientCredentials { get; set; }
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "refresh_token". This occurs if your application has issued a "refresh_token"
/// along with the "access_token", and the client is attempting to use the "refresh_token" to acquire a new "access_token", and possibly a new "refresh_token".
/// To issue a refresh token the an Options.RefreshTokenProvider must be assigned to create the value which is returned. The claims and properties
/// associated with the refresh token are present in the context.Ticket. The application must call context.Validated to instruct the
/// Authorization Server middleware to issue an access token based on those claims and properties. The call to context.Validated may
/// be given a different AuthenticationTicket or ClaimsIdentity in order to control which information flows from the refresh token to
/// the access token. The default behavior when using the OAuthAuthorizationServerProvider is to flow information from the refresh token to
/// the access token unmodified.
/// See also http://tools.ietf.org/html/rfc6749#section-6
/// </summary>
public Func<OAuthGrantRefreshTokenContext, Task> OnGrantRefreshToken { get; set; }
/// <summary>
/// Called when a request to the Token andpoint arrives with a "grant_type" of any other value. If the application supports custom grant types
/// it is entirely responsible for determining if the request should result in an access_token. If context.Validated is called with ticket
/// information the response body is produced in the same way as the other standard grant types. If additional response parameters must be
/// included they may be added in the final TokenEndpoint call.
/// See also http://tools.ietf.org/html/rfc6749#section-4.5
/// </summary>
public Func<OAuthGrantCustomExtensionContext, Task> OnGrantCustomExtension { get; set; }
/// <summary>
/// Called at the final stage of an incoming Authorize endpoint request before the execution continues on to the web application component
/// responsible for producing the html response. Anything present in the OWIN pipeline following the Authorization Server may produce the
/// response for the Authorize page. If running on IIS any ASP.NET technology running on the server may produce the response for the
/// Authorize page. If the web application wishes to produce the response directly in the AuthorizeEndpoint call it may write to the
/// context.Response directly and should call context.RequestCompleted to stop other handlers from executing. If the web application wishes
/// to grant the authorization directly in the AuthorizeEndpoint call it cay call context.OwinContext.Authentication.SignIn with the
/// appropriate ClaimsIdentity and should call context.RequestCompleted to stop other handlers from executing.
/// </summary>
public Func<OAuthAuthorizeEndpointContext, Task> OnAuthorizeEndpoint { get; set; }
/// <summary>
/// Called at the final stage of a successful Token endpoint request. An application may implement this call in order to do any final
/// modification of the claims being used to issue access or refresh tokens. This call may also be used in order to add additional
/// response parameters to the Token endpoint's json response body.
/// </summary>
public Func<OAuthTokenEndpointContext, Task> OnTokenEndpoint { get; set; }
/// <summary>
/// Called before the AuthorizationEndpoint redirects its response to the caller. The response could be the
/// token, when using implicit flow or the AuthorizationEndpoint when using authorization code flow.
/// An application may implement this call in order to do any final modification of the claims being used
/// to issue access or refresh tokens. This call may also be used in order to add additional
/// response parameters to the authorization endpoint's response.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public Func<OAuthAuthorizationEndpointResponseContext, Task> OnAuthorizationEndpointResponse { get; set; }
/// <summary>
/// Called before the TokenEndpoint redirects its response to the caller.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public Func<OAuthTokenEndpointResponseContext, Task> OnTokenEndpointResponse { get; set; }
/// <summary>
/// Called to determine if an incoming request is treated as an Authorize or Token
/// endpoint. If Options.AuthorizeEndpointPath or Options.TokenEndpointPath
/// are assigned values, then handling this event is optional and context.IsAuthorizeEndpoint and context.IsTokenEndpoint
/// will already be true if the request path matches.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task MatchEndpoint(OAuthMatchContext context)
{
return OnMatchEndpoint.Invoke(context);
}
/// <summary>
/// Called to validate that the context.ClientId is a registered "client_id", and that the context.RedirectUri a "redirect_uri"
/// registered for that client. This only occurs when processing the Authorize endpoint. The application MUST implement this
/// call, and it MUST validate both of those factors before calling context.Validated. If the context.Validated method is called
/// with a given redirectUri parameter, then IsValidated will only become true if the incoming redirect URI matches the given redirect URI.
/// If context.Validated is not called the request will not proceed further.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
return OnValidateClientRedirectUri.Invoke(context);
}
/// <summary>
/// Called to validate that the origin of the request is a registered "client_id", and that the correct credentials for that client are
/// present on the request. If the web application accepts Basic authentication credentials,
/// context.TryGetBasicCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request header. If the web
/// application accepts "client_id" and "client_secret" as form encoded POST parameters,
/// context.TryGetFormCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request body.
/// If context.Validated is not called the request will not proceed further.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
return OnValidateClientAuthentication.Invoke(context);
}
/// <summary>
/// Called for each request to the Authorize endpoint to determine if the request is valid and should continue.
/// The default behavior when using the OAuthAuthorizationServerProvider is to assume well-formed requests, with
/// validated client redirect URI, should continue processing. An application may add any additional constraints.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context)
{
return OnValidateAuthorizeRequest.Invoke(context);
}
/// <summary>
/// Called for each request to the Token endpoint to determine if the request is valid and should continue.
/// The default behavior when using the OAuthAuthorizationServerProvider is to assume well-formed requests, with
/// validated client credentials, should continue processing. An application may add any additional constraints.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task ValidateTokenRequest(OAuthValidateTokenRequestContext context)
{
return OnValidateTokenRequest.Invoke(context);
}
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "authorization_code". This occurs after the Authorize
/// endpoint as redirected the user-agent back to the client with a "code" parameter, and the client is exchanging that for an "access_token".
/// The claims and properties
/// associated with the authorization code are present in the context.Ticket. The application must call context.Validated to instruct the Authorization
/// Server middleware to issue an access token based on those claims and properties. The call to context.Validated may be given a different
/// AuthenticationTicket or ClaimsIdentity in order to control which information flows from authorization code to access token.
/// The default behavior when using the OAuthAuthorizationServerProvider is to flow information from the authorization code to
/// the access token unmodified.
/// See also http://tools.ietf.org/html/rfc6749#section-4.1.3
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
{
return OnGrantAuthorizationCode.Invoke(context);
}
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "refresh_token". This occurs if your application has issued a "refresh_token"
/// along with the "access_token", and the client is attempting to use the "refresh_token" to acquire a new "access_token", and possibly a new "refresh_token".
/// To issue a refresh token the an Options.RefreshTokenProvider must be assigned to create the value which is returned. The claims and properties
/// associated with the refresh token are present in the context.Ticket. The application must call context.Validated to instruct the
/// Authorization Server middleware to issue an access token based on those claims and properties. The call to context.Validated may
/// be given a different AuthenticationTicket or ClaimsIdentity in order to control which information flows from the refresh token to
/// the access token. The default behavior when using the OAuthAuthorizationServerProvider is to flow information from the refresh token to
/// the access token unmodified.
/// See also http://tools.ietf.org/html/rfc6749#section-6
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
return OnGrantRefreshToken.Invoke(context);
}
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "password". This occurs when the user has provided name and password
/// credentials directly into the client application's user interface, and the client application is using those to acquire an "access_token" and
/// optional "refresh_token". If the web application supports the
/// resource owner credentials grant type it must validate the context.Username and context.Password as appropriate. To issue an
/// access token the context.Validated must be called with a new ticket containing the claims about the resource owner which should be associated
/// with the access token. The application should take appropriate measures to ensure that the endpoint isn<73>t abused by malicious callers.
/// The default behavior is to reject this grant type.
/// See also http://tools.ietf.org/html/rfc6749#section-4.3.2
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
return OnGrantResourceOwnerCredentials.Invoke(context);
}
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of "client_credentials". This occurs when a registered client
/// application wishes to acquire an "access_token" to interact with protected resources on it's own behalf, rather than on behalf of an authenticated user.
/// If the web application supports the client credentials it may assume the context.ClientId has been validated by the ValidateClientAuthentication call.
/// To issue an access token the context.Validated must be called with a new ticket containing the claims about the client application which should be associated
/// with the access token. The application should take appropriate measures to ensure that the endpoint isn<73>t abused by malicious callers.
/// The default behavior is to reject this grant type.
/// See also http://tools.ietf.org/html/rfc6749#section-4.4.2
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
return OnGrantClientCredentials.Invoke(context);
}
/// <summary>
/// Called when a request to the Token endpoint arrives with a "grant_type" of any other value. If the application supports custom grant types
/// it is entirely responsible for determining if the request should result in an access_token. If context.Validated is called with ticket
/// information the response body is produced in the same way as the other standard grant types. If additional response parameters must be
/// included they may be added in the final TokenEndpoint call.
/// See also http://tools.ietf.org/html/rfc6749#section-4.5
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task GrantCustomExtension(OAuthGrantCustomExtensionContext context)
{
return OnGrantCustomExtension.Invoke(context);
}
/// <summary>
/// Called at the final stage of an incoming Authorize endpoint request before the execution continues on to the web application component
/// responsible for producing the html response. Anything present in the OWIN pipeline following the Authorization Server may produce the
/// response for the Authorize page. If running on IIS any ASP.NET technology running on the server may produce the response for the
/// Authorize page. If the web application wishes to produce the response directly in the AuthorizeEndpoint call it may write to the
/// context.Response directly and should call context.RequestCompleted to stop other handlers from executing. If the web application wishes
/// to grant the authorization directly in the AuthorizeEndpoint call it cay call context.OwinContext.Authentication.SignIn with the
/// appropriate ClaimsIdentity and should call context.RequestCompleted to stop other handlers from executing.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
return OnAuthorizeEndpoint.Invoke(context);
}
/// <summary>
/// Called at the final stage of a successful Token endpoint request. An application may implement this call in order to do any final
/// modification of the claims being used to issue access or refresh tokens. This call may also be used in order to add additional
/// response parameters to the Token endpoint's json response body.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task TokenEndpoint(OAuthTokenEndpointContext context)
{
return OnTokenEndpoint.Invoke(context);
}
/// <summary>
/// Called before the AuthorizationEndpoint redirects its response to the caller. The response could be the
/// token, when using implicit flow or the AuthorizationEndpoint when using authorization code flow.
/// An application may implement this call in order to do any final modification of the claims being used
/// to issue access or refresh tokens. This call may also be used in order to add additional
/// response parameters to the authorization endpoint's response.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context)
{
return OnAuthorizationEndpointResponse.Invoke(context);
}
/// <summary>
/// Called before the TokenEndpoint redirects its response to the caller.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public virtual Task TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
{
return OnTokenEndpointResponse.Invoke(context);
}
}
}

View File

@ -0,0 +1,33 @@
using Microsoft.AspNet.Http;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// An event raised after the Authorization Server has processed the request, but before it is passed on to the web application.
/// Calling RequestCompleted will prevent the request from passing on to the web application.
/// </summary>
public class OAuthAuthorizeEndpointContext : BaseOAuthEndpointContext
{
/// <summary>
/// Creates an instance of this context
/// </summary>
public OAuthAuthorizeEndpointContext(HttpContext context, OAuthAuthorizationServerOptions options, AuthorizeEndpointRequest authorizeRequest) : base(context, options)
{
AuthorizeRequest = authorizeRequest;
}
/// <summary>
/// Gets OAuth authorization request data.
/// </summary>
public AuthorizeEndpointRequest AuthorizeRequest { get; private set; }
public bool IsRequestCompleted { get; private set; }
public void RequestCompleted()
{
IsRequestCompleted = true;
}
}
}

View File

@ -0,0 +1,69 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Authentication;
using System;
using System.Collections.Generic;
using System.Security.Claims;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Provides context information when processing an Authorization Response
/// </summary>
public class OAuthAuthorizationEndpointResponseContext : BaseOAuthEndpointContext
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthAuthorizationEndpointResponseContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="ticket"></param>
/// <param name="tokenEndpointRequest"></param>
public OAuthAuthorizationEndpointResponseContext(HttpContext context, OAuthAuthorizationServerOptions options, AuthenticationTicket ticket, AuthorizeEndpointRequest authorizeEndpointRequest, string accessToken, string authorizationCode) : base(context, options)
{
if (ticket == null)
{
throw new ArgumentNullException("ticket");
}
Principal = ticket.Principal;
Properties = ticket.Properties;
AuthorizeEndpointRequest = authorizeEndpointRequest;
AdditionalResponseParameters = new Dictionary<string, object>(StringComparer.Ordinal);
AccessToken = accessToken;
AuthorizationCode = authorizationCode;
}
/// <summary>
/// Gets the identity of the resource owner.
/// </summary>
public ClaimsPrincipal Principal { get; private set; }
/// <summary>
/// Dictionary containing the state of the authentication session.
/// </summary>
public AuthenticationProperties Properties { get; private set; }
/// <summary>
/// Gets information about the authorize endpoint request.
/// </summary>
public AuthorizeEndpointRequest AuthorizeEndpointRequest { get; private set; }
/// <summary>
/// Enables additional values to be appended to the token response.
/// </summary>
public IDictionary<string, object> AdditionalResponseParameters { get; private set; }
/// <summary>
/// The serialized Access-Token. Depending on the flow, it can be null.
/// </summary>
public string AccessToken { get; private set; }
/// <summary>
/// The created Authorization-Code. Depending on the flow, it can be null.
/// </summary>
public string AuthorizationCode { get; private set; }
}
}

View File

@ -0,0 +1,70 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace OAuth.AspNet.AuthServer
{
public static class Constants
{
public static class Parameters
{
public const string ResponseType = "response_type";
public const string GrantType = "grant_type";
public const string ClientId = "client_id";
public const string ClientSecret = "client_secret";
public const string RedirectUri = "redirect_uri";
public const string Scope = "scope";
public const string State = "state";
public const string Code = "code";
public const string RefreshToken = "refresh_token";
public const string Username = "username";
public const string Password = "password";
public const string Error = "error";
public const string ErrorDescription = "error_description";
public const string ErrorUri = "error_uri";
public const string ExpiresIn = "expires_in";
public const string AccessToken = "access_token";
public const string TokenType = "token_type";
public const string ResponseMode = "response_mode";
}
public static class ResponseTypes
{
public const string Code = "code";
public const string Token = "token";
}
public static class GrantTypes
{
public const string AuthorizationCode = "authorization_code";
public const string ClientCredentials = "client_credentials";
public const string RefreshToken = "refresh_token";
public const string Password = "password";
}
public static class TokenTypes
{
public const string Bearer = "bearer";
}
public static class Errors
{
public const string InvalidRequest = "invalid_request";
public const string InvalidClient = "invalid_client";
public const string InvalidGrant = "invalid_grant";
public const string UnsupportedResponseType = "unsupported_response_type";
public const string UnsupportedGrantType = "unsupported_grant_type";
public const string UnauthorizedClient = "unauthorized_client";
}
public static class Extra
{
public const string ClientId = "client_id";
public const string RedirectUri = "redirect_uri";
}
public static class ResponseModes
{
public const string FormPost = "form_post";
}
}
}

View File

@ -0,0 +1,15 @@

namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Default values used by authorization server and bearer authentication.
/// </summary>
public static class OAuthDefaults
{
/// <summary>
/// Default value for AuthenticationType property in the OAuthBearerAuthenticationOptions and
/// OAuthAuthorizationServerOptions.
/// </summary>
public const string AuthenticationType = "Bearer";
}
}

View File

@ -0,0 +1,21 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Http;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Provides context information when handling an OAuth authorization code grant.
/// </summary>
public class OAuthGrantAuthorizationCodeContext : BaseValidatingTicketContext<OAuthAuthorizationServerOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthGrantAuthorizationCodeContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="ticket"></param>
public OAuthGrantAuthorizationCodeContext(HttpContext context, OAuthAuthorizationServerOptions options, AuthenticationTicket ticket) : base(context, options, ticket) { }
}
}

View File

@ -0,0 +1,36 @@
using Microsoft.AspNet.Http;
using System.Collections.Generic;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Provides context information used in handling an OAuth client credentials grant.
/// </summary>
public class OAuthGrantClientCredentialsContext : BaseValidatingTicketContext<OAuthAuthorizationServerOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthGrantClientCredentialsContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="clientId"></param>
/// <param name="scope"></param>
public OAuthGrantClientCredentialsContext(HttpContext context, OAuthAuthorizationServerOptions options, string clientId, IList<string> scope) : base(context, options, null)
{
ClientId = clientId;
Scope = scope;
}
/// <summary>
/// OAuth client id.
/// </summary>
public string ClientId { get; private set; }
/// <summary>
/// List of scopes allowed by the resource owner.
/// </summary>
public IList<string> Scope { get; private set; }
}
}

View File

@ -0,0 +1,42 @@
using Microsoft.AspNet.Http;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Provides context information used when handling OAuth extension grant types.
/// </summary>
public class OAuthGrantCustomExtensionContext : BaseValidatingTicketContext<OAuthAuthorizationServerOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthGrantCustomExtensionContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="clientId"></param>
/// <param name="grantType"></param>
/// <param name="parameters"></param>
public OAuthGrantCustomExtensionContext(HttpContext context, OAuthAuthorizationServerOptions options, string clientId, string grantType, IReadableStringCollection parameters) : base(context, options, null)
{
ClientId = clientId;
GrantType = grantType;
Parameters = parameters;
}
/// <summary>
/// Gets the OAuth client id.
/// </summary>
public string ClientId { get; private set; }
/// <summary>
/// Gets the name of the OAuth extension grant type.
/// </summary>
public string GrantType { get; private set; }
/// <summary>
/// Gets a list of additional parameters from the token request.
/// </summary>
public IReadableStringCollection Parameters { get; private set; }
}
}

View File

@ -0,0 +1,30 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Http;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Provides context information used when granting an OAuth refresh token.
/// </summary>
public class OAuthGrantRefreshTokenContext : BaseValidatingTicketContext<OAuthAuthorizationServerOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthGrantRefreshTokenContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="ticket"></param>
/// <param name="clientId"></param>
public OAuthGrantRefreshTokenContext(HttpContext context, OAuthAuthorizationServerOptions options, AuthenticationTicket ticket, string clientId) : base(context, options, ticket)
{
ClientId = clientId;
}
/// <summary>
/// The OAuth client id.
/// </summary>
public string ClientId { get; private set; }
}
}

View File

@ -0,0 +1,50 @@
using Microsoft.AspNet.Http;
using System.Collections.Generic;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Provides context information used in handling an OAuth resource owner grant.
/// </summary>
public class OAuthGrantResourceOwnerCredentialsContext : BaseValidatingTicketContext<OAuthAuthorizationServerOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthGrantResourceOwnerCredentialsContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="clientId"></param>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <param name="scope"></param>
public OAuthGrantResourceOwnerCredentialsContext(HttpContext context, OAuthAuthorizationServerOptions options, string clientId, string userName, string password, IList<string> scope) : base(context, options, null)
{
ClientId = clientId;
UserName = userName;
Password = password;
Scope = scope;
}
/// <summary>
/// OAuth client id.
/// </summary>
public string ClientId { get; private set; }
/// <summary>
/// Resource owner username.
/// </summary>
public string UserName { get; private set; }
/// <summary>
/// Resource owner password.
/// </summary>
public string Password { get; private set; }
/// <summary>
/// List of scopes allowed by the resource owner.
/// </summary>
public IList<string> Scope { get; private set; }
}
}

View File

@ -0,0 +1,74 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Http;
using System;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Provides notification used for determining the OAuth flow type based on the request.
/// </summary>
public class OAuthMatchContext : BaseControlContext
{
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="OAuthMatchContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
public OAuthMatchContext(HttpContext context, OAuthAuthorizationServerOptions options) : base(context)
{
if (options == null)
throw new ArgumentNullException(nameof(options));
Options = options;
}
#endregion
#region Public Members
public OAuthAuthorizationServerOptions Options { get; }
/// <summary>
/// Gets whether or not the endpoint is an OAuth authorize endpoint.
/// </summary>
public bool IsAuthorizeEndpoint { get; private set; }
/// <summary>
/// Gets whether or not the endpoint is an OAuth token endpoint.
/// </summary>
public bool IsTokenEndpoint { get; private set; }
/// <summary>
/// Sets the endpoint type to authorize endpoint.
/// </summary>
public void MatchesAuthorizeEndpoint()
{
IsAuthorizeEndpoint = true;
IsTokenEndpoint = false;
}
/// <summary>
/// Sets the endpoint type to token endpoint.
/// </summary>
public void MatchesTokenEndpoint()
{
IsAuthorizeEndpoint = false;
IsTokenEndpoint = true;
}
/// <summary>
/// Sets the endpoint type to neither authorize nor token.
/// </summary>
public void MatchesNothing()
{
IsAuthorizeEndpoint = false;
IsTokenEndpoint = false;
}
#endregion
}
}

View File

@ -0,0 +1,75 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Authentication;
using System;
using System.Collections.Generic;
using System.Security.Claims;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Provides context information used when processing an OAuth token request.
/// </summary>
public class OAuthTokenEndpointContext : BaseOAuthEndpointContext
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthTokenEndpointContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="ticket"></param>
/// <param name="tokenEndpointRequest"></param>
public OAuthTokenEndpointContext(HttpContext context, OAuthAuthorizationServerOptions options, AuthenticationTicket ticket, TokenEndpointRequest tokenEndpointRequest) : base(context, options)
{
if (ticket == null)
{
throw new ArgumentNullException("ticket");
}
Principal = ticket.Principal;
Properties = ticket.Properties;
TokenEndpointRequest = tokenEndpointRequest;
AdditionalResponseParameters = new Dictionary<string, object>(StringComparer.Ordinal);
TokenIssued = Principal != null;
}
/// <summary>
/// Gets the identity of the resource owner.
/// </summary>
public ClaimsPrincipal Principal { get; private set; }
/// <summary>
/// Dictionary containing the state of the authentication session.
/// </summary>
public AuthenticationProperties Properties { get; private set; }
/// <summary>
/// Gets information about the token endpoint request.
/// </summary>
public TokenEndpointRequest TokenEndpointRequest { get; set; }
/// <summary>
/// Gets whether or not the token should be issued.
/// </summary>
public bool TokenIssued { get; private set; }
/// <summary>
/// Enables additional values to be appended to the token response.
/// </summary>
public IDictionary<string, object> AdditionalResponseParameters { get; private set; }
/// <summary>
/// Issues the token.
/// </summary>
/// <param name="principal"></param>
/// <param name="properties"></param>
public void Issue(ClaimsPrincipal principal, AuthenticationProperties properties)
{
Principal = principal;
Properties = properties;
TokenIssued = true;
}
}
}

View File

@ -0,0 +1,82 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Authentication;
using System;
using System.Collections.Generic;
using System.Security.Claims;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Provides context information used at the end of a token-endpoint-request.
/// </summary>
public class OAuthTokenEndpointResponseContext : BaseOAuthEndpointContext
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthTokenEndpointResponseContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="ticket"></param>
/// <param name="tokenEndpointRequest"></param>
public OAuthTokenEndpointResponseContext(HttpContext context, OAuthAuthorizationServerOptions options, AuthenticationTicket ticket, TokenEndpointRequest tokenEndpointRequest, string accessToken, IDictionary<string, object> additionalResponseParameters) : base(context, options)
{
if (ticket == null)
{
throw new ArgumentNullException("ticket");
}
Principal = ticket.Principal;
Properties = ticket.Properties;
TokenEndpointRequest = tokenEndpointRequest;
AdditionalResponseParameters = new Dictionary<string, object>(StringComparer.Ordinal);
TokenIssued = Principal != null;
AccessToken = accessToken;
AdditionalResponseParameters = additionalResponseParameters;
}
/// <summary>
/// Gets the identity of the resource owner.
/// </summary>
public ClaimsPrincipal Principal { get; private set; }
/// <summary>
/// Dictionary containing the state of the authentication session.
/// </summary>
public AuthenticationProperties Properties { get; private set; }
/// <summary>
/// The issued Access-Token
/// </summary>
public string AccessToken { get; private set; }
/// <summary>
/// Gets information about the token endpoint request.
/// </summary>
public TokenEndpointRequest TokenEndpointRequest { get; set; }
/// <summary>
/// Gets whether or not the token should be issued.
/// </summary>
public bool TokenIssued { get; private set; }
/// <summary>
/// Enables additional values to be appended to the token response.
/// </summary>
public IDictionary<string, object> AdditionalResponseParameters { get; private set; }
/// <summary>
/// Issues the token.
/// </summary>
/// <param name="principal"></param>
/// <param name="properties"></param>
public void Issue(ClaimsPrincipal principal, AuthenticationProperties properties)
{
Principal = principal;
Properties = properties;
TokenIssued = true;
}
}
}

View File

@ -0,0 +1,35 @@
using Microsoft.AspNet.Http;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Provides context information used in validating an OAuth authorization request.
/// </summary>
public class OAuthValidateAuthorizeRequestContext : BaseValidatingContext<OAuthAuthorizationServerOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthValidateAuthorizeRequestContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="authorizeRequest"></param>
/// <param name="clientContext"></param>
public OAuthValidateAuthorizeRequestContext(HttpContext context, OAuthAuthorizationServerOptions options, AuthorizeEndpointRequest authorizeRequest, OAuthValidateClientRedirectUriContext clientContext) : base(context, options)
{
AuthorizeRequest = authorizeRequest;
ClientContext = clientContext;
}
/// <summary>
/// Gets OAuth authorization request data.
/// </summary>
public AuthorizeEndpointRequest AuthorizeRequest { get; private set; }
/// <summary>
/// Gets data about the OAuth client.
/// </summary>
public OAuthValidateClientRedirectUriContext ClientContext { get; private set; }
}
}

View File

@ -0,0 +1,106 @@
using Microsoft.AspNet.Http;
using System;
using System.Text;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Contains information about the client credentials.
/// </summary>
public class OAuthValidateClientAuthenticationContext : BaseValidatingClientContext
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthValidateClientAuthenticationContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="parameters"></param>
public OAuthValidateClientAuthenticationContext(HttpContext context, OAuthAuthorizationServerOptions options, IReadableStringCollection parameters) : base(context, options, null)
{
Parameters = parameters;
}
/// <summary>
/// Gets the set of form parameters from the request.
/// </summary>
public IReadableStringCollection Parameters { get; private set; }
/// <summary>
/// Sets the client id and marks the context as validated by the application.
/// </summary>
/// <param name="clientId"></param>
/// <returns></returns>
public bool Validated(string clientId)
{
ClientId = clientId;
return Validated();
}
/// <summary>
/// Extracts HTTP basic authentication credentials from the HTTP authenticate header.
/// </summary>
/// <param name="clientId"></param>
/// <param name="clientSecret"></param>
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", Justification = "Optimized for usage")]
public bool TryGetBasicCredentials(out string clientId, out string clientSecret)
{
// Client Authentication http://tools.ietf.org/html/rfc6749#section-2.3
// Client Authentication Password http://tools.ietf.org/html/rfc6749#section-2.3.1
string authorization = Request.Headers["Authorization"];
if (!string.IsNullOrWhiteSpace(authorization) && authorization.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
{
try
{
byte[] data = Convert.FromBase64String(authorization.Substring("Basic ".Length).Trim());
string text = Encoding.UTF8.GetString(data);
int delimiterIndex = text.IndexOf(':');
if (delimiterIndex >= 0)
{
clientId = text.Substring(0, delimiterIndex);
clientSecret = text.Substring(delimiterIndex + 1);
ClientId = clientId;
return true;
}
}
catch (FormatException)
{
// Bad Base64 string
}
catch (ArgumentException)
{
// Bad utf-8 string
}
}
clientId = null;
clientSecret = null;
return false;
}
/// <summary>
/// Extracts forms authentication credentials from the HTTP request body.
/// </summary>
/// <param name="clientId"></param>
/// <param name="clientSecret"></param>
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", Justification = "Optimized for usage")]
public bool TryGetFormCredentials(out string clientId, out string clientSecret)
{
clientId = Parameters[Constants.Parameters.ClientId];
if (!string.IsNullOrEmpty(clientId))
{
clientSecret = Parameters[Constants.Parameters.ClientSecret];
ClientId = clientId;
return true;
}
clientId = null;
clientSecret = null;
return false;
}
}
}

View File

@ -0,0 +1,71 @@
using Microsoft.AspNet.Http;
using System;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Contains data about the OAuth client redirect URI
/// </summary>
public class OAuthValidateClientRedirectUriContext : BaseValidatingClientContext
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthValidateClientRedirectUriContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="clientId"></param>
/// <param name="redirectUri"></param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "3#", Justification = "redirect_uri is a string parameter")]
public OAuthValidateClientRedirectUriContext(HttpContext context, OAuthAuthorizationServerOptions options, string clientId, string redirectUri) : base(context, options, clientId)
{
RedirectUri = redirectUri;
}
/// <summary>
/// Gets the client redirect URI
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "redirect_uri is a string parameter")]
public string RedirectUri { get; private set; }
/// <summary>
/// Marks this context as validated by the application. IsValidated becomes true and HasError becomes false as a result of calling.
/// </summary>
/// <returns></returns>
public override bool Validated()
{
if (string.IsNullOrEmpty(RedirectUri))
{
// Don't allow default validation when redirect_uri not provided with request
return false;
}
return base.Validated();
}
/// <summary>
/// Checks the redirect URI to determine whether it equals <see cref="RedirectUri"/>.
/// </summary>
/// <param name="redirectUri"></param>
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "redirect_uri is a string parameter")]
public bool Validated(string redirectUri)
{
if (redirectUri == null)
{
throw new ArgumentNullException("redirectUri");
}
if (!string.IsNullOrEmpty(RedirectUri) &&
!string.Equals(RedirectUri, redirectUri, StringComparison.Ordinal))
{
// Don't allow validation to alter redirect_uri provided with request
return false;
}
RedirectUri = redirectUri;
return Validated();
}
}
}

View File

@ -0,0 +1,35 @@
using Microsoft.AspNet.Http;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Provides context information used in validating an OAuth token request.
/// </summary>
public class OAuthValidateTokenRequestContext : BaseValidatingContext<OAuthAuthorizationServerOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthValidateTokenRequestContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="tokenRequest"></param>
/// <param name="clientContext"></param>
public OAuthValidateTokenRequestContext(HttpContext context, OAuthAuthorizationServerOptions options, TokenEndpointRequest tokenRequest, BaseValidatingClientContext clientContext) : base(context, options)
{
TokenRequest = tokenRequest;
ClientContext = clientContext;
}
/// <summary>
/// Gets the token request data.
/// </summary>
public TokenEndpointRequest TokenRequest { get; private set; }
/// <summary>
/// Gets information about the client.
/// </summary>
public BaseValidatingClientContext ClientContext { get; private set; }
}
}

View File

@ -0,0 +1,164 @@
using Microsoft.AspNet.Http;
using System;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Data object representing the information contained in form encoded body of a Token endpoint request.
/// </summary>
public class TokenEndpointRequest
{
/// <summary>
/// Creates a new instance populated with values from the form encoded body parameters.
/// </summary>
/// <param name="parameters">Form encoded body parameters from a request.</param>
public TokenEndpointRequest(IReadableStringCollection parameters)
{
if (parameters == null)
{
throw new ArgumentNullException("parameters");
}
Parameters = parameters;
GrantType = parameters[Constants.Parameters.GrantType];
ClientId = parameters[Constants.Parameters.ClientId];
if (string.Equals(GrantType, Constants.GrantTypes.AuthorizationCode, StringComparison.Ordinal))
{
AuthorizationCodeGrant = new TokenEndpointRequestAuthorizationCode
{
Code = parameters[Constants.Parameters.Code],
RedirectUri = parameters[Constants.Parameters.RedirectUri],
};
}
else if (string.Equals(GrantType, Constants.GrantTypes.ClientCredentials, StringComparison.Ordinal))
{
ClientCredentialsGrant = new TokenEndpointRequestClientCredentials
{
Scope = ((string)parameters[Constants.Parameters.Scope] ?? string.Empty).Split(' ')
};
}
else if (string.Equals(GrantType, Constants.GrantTypes.RefreshToken, StringComparison.Ordinal))
{
RefreshTokenGrant = new TokenEndpointRequestRefreshToken
{
RefreshToken = parameters[Constants.Parameters.RefreshToken],
Scope = ((string)parameters[Constants.Parameters.Scope] ?? string.Empty).Split(' ')
};
}
else if (string.Equals(GrantType, Constants.GrantTypes.Password, StringComparison.Ordinal))
{
ResourceOwnerPasswordCredentialsGrant = new TokenEndpointRequestResourceOwnerPasswordCredentials
{
UserName = parameters[Constants.Parameters.Username],
Password = parameters[Constants.Parameters.Password],
Scope = ((string)parameters[Constants.Parameters.Scope] ?? string.Empty).Split(' ')
};
}
else if (!string.IsNullOrEmpty(GrantType))
{
CustomExtensionGrant = new TokenEndpointRequestCustomExtension
{
Parameters = parameters,
};
}
else
{
throw new ArgumentException("No grant type found in the request");
}
}
/// <summary>
/// The form encoded body parameters of the Token endpoint request
/// </summary>
public IReadableStringCollection Parameters { get; private set; }
/// <summary>
/// The "grant_type" parameter of the Token endpoint request. This parameter is required.
/// </summary>
public string GrantType { get; private set; }
/// <summary>
/// The "client_id" parameter of the Token endpoint request. This parameter is optional. It might not
/// be present if the request is authenticated in a different way, for example, by using basic authentication
/// credentials.
/// </summary>
public string ClientId { get; private set; }
/// <summary>
/// Data object available when the "grant_type" is "authorization_code".
/// See also http://tools.ietf.org/html/rfc6749#section-4.1.3
/// </summary>
public TokenEndpointRequestAuthorizationCode AuthorizationCodeGrant { get; private set; }
/// <summary>
/// Data object available when the "grant_type" is "client_credentials".
/// See also http://tools.ietf.org/html/rfc6749#section-4.4.2
/// </summary>
public TokenEndpointRequestClientCredentials ClientCredentialsGrant { get; private set; }
/// <summary>
/// Data object available when the "grant_type" is "refresh_token".
/// See also http://tools.ietf.org/html/rfc6749#section-6
/// </summary>
public TokenEndpointRequestRefreshToken RefreshTokenGrant { get; private set; }
/// <summary>
/// Data object available when the "grant_type" is "password".
/// See also http://tools.ietf.org/html/rfc6749#section-4.3.2
/// </summary>
public TokenEndpointRequestResourceOwnerPasswordCredentials ResourceOwnerPasswordCredentialsGrant { get; private set; }
/// <summary>
/// Data object available when the "grant_type" is unrecognized.
/// See also http://tools.ietf.org/html/rfc6749#section-4.5
/// </summary>
public TokenEndpointRequestCustomExtension CustomExtensionGrant { get; private set; }
/// <summary>
/// True when the "grant_type" is "authorization_code".
/// See also http://tools.ietf.org/html/rfc6749#section-4.1.3
/// </summary>
public bool IsAuthorizationCodeGrantType
{
get { return AuthorizationCodeGrant != null; }
}
/// <summary>
/// True when the "grant_type" is "client_credentials".
/// See also http://tools.ietf.org/html/rfc6749#section-4.4.2
/// </summary>
public bool IsClientCredentialsGrantType
{
get { return ClientCredentialsGrant != null; }
}
/// <summary>
/// True when the "grant_type" is "refresh_token".
/// See also http://tools.ietf.org/html/rfc6749#section-6
/// </summary>
public bool IsRefreshTokenGrantType
{
get { return RefreshTokenGrant != null; }
}
/// <summary>
/// True when the "grant_type" is "password".
/// See also http://tools.ietf.org/html/rfc6749#section-4.3.2
/// </summary>
public bool IsResourceOwnerPasswordCredentialsGrantType
{
get { return ResourceOwnerPasswordCredentialsGrant != null; }
}
/// <summary>
/// True when the "grant_type" is unrecognized.
/// See also http://tools.ietf.org/html/rfc6749#section-4.5
/// </summary>
public bool IsCustomExtensionGrantType
{
get { return CustomExtensionGrant != null; }
}
}
}

View File

@ -0,0 +1,23 @@
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Data object used by TokenEndpointRequest when the "grant_type" is "authorization_code".
/// </summary>
public class TokenEndpointRequestAuthorizationCode
{
/// <summary>
/// The value passed to the Token endpoint in the "code" parameter
/// </summary>
public string Code { get; set; }
/// <summary>
/// The value passed to the Token endpoint in the "redirect_uri" parameter. This MUST be provided by the caller
/// if the original visit to the Authorize endpoint contained a "redirect_uri" parameter.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "By design")]
public string RedirectUri { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Data object used by TokenEndpointRequest when the "grant_type" is "client_credentials".
/// </summary>
public class TokenEndpointRequestClientCredentials
{
/// <summary>
/// The value passed to the Token endpoint in the "scope" parameter
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This class is just for passing data through.")]
public IList<string> Scope { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using Microsoft.AspNet.Http;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Data object used by TokenEndpointRequest which contains parameter information when the "grant_type" is unrecognized.
/// </summary>
public class TokenEndpointRequestCustomExtension
{
/// <summary>
/// The parameter information when the "grant_type" is unrecognized.
/// </summary>
public IReadableStringCollection Parameters { get; set; }
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Data object used by TokenEndpointRequest when the "grant_type" parameter is "refresh_token".
/// </summary>
public class TokenEndpointRequestRefreshToken
{
/// <summary>
/// The value passed to the Token endpoint in the "refresh_token" parameter
/// </summary>
public string RefreshToken { get; set; }
/// <summary>
/// The value passed to the Token endpoint in the "scope" parameter
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is just a data container object.")]
public IList<string> Scope { get; set; }
}
}

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace OAuth.AspNet.AuthServer
{
/// <summary>
/// Data object used by TokenEndpointRequest when the "grant_type" is "password".
/// </summary>
public class TokenEndpointRequestResourceOwnerPasswordCredentials
{
/// <summary>
/// The value passed to the Token endpoint in the "username" parameter
/// </summary>
public string UserName { get; set; }
/// <summary>
/// The value passed to the Token endpoint in the "password" parameter
/// </summary>
public string Password { get; set; }
/// <summary>
/// The value passed to the Token endpoint in the "scope" parameter
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is just a data class.")]
public IList<string> Scope { get; set; }
}
}

View File

@ -0,0 +1,41 @@
{
"version": "1.0.5-*",
"description": "OAuth AspNet Server",
"authors": [
"Paul Schneider <paul@pschneider.fr>"
],
"packOptions": {
"repository": {
"type": "git",
"url": "https://github.com/pazof/yavsc"
},
"licenseUrl": "https://github.com/pazof/yavsc/blob/vnext/LICENSE",
"requireLicenseAcceptance": true,
"owners": [
"Paul Schneider <paul@pschneider.fr>"
],
"summary": "Yet another very small company",
"projectUrl": "http://yavsc.pschneider.fr",
"tags": [
"Authorization server",
"OAuth",
"Web API"
]
},
"tooling": {
"defaultNamespace": "Yavsc"
},
"dependencies": {
"Newtonsoft.Json": "9.0.1",
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-rc1-*",
"OAuth.AspNet.Token": {
"type": "build",
"target": "project"
}
},
"frameworks": {
"dnx451": {
"frameworkAssemblies": {}
}
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.IO;
using Microsoft.AspNet.DataProtection;
public class MonoDataProtectionProvider : IDataProtectionProvider
{
private readonly string appName;
public MonoDataProtectionProvider()
: this(Guid.NewGuid().ToString())
{ }
public MonoDataProtectionProvider(DirectoryInfo dataProtectionDirInfo)
: this(Guid.NewGuid().ToString())
{
}
public MonoDataProtectionProvider(string appName)
{
if (appName == null) { throw new ArgumentNullException("appName"); }
this.appName = appName;
}
public IDataProtector Create(params string[] purposes)
{
if (purposes == null) { throw new ArgumentNullException("profile"); }
return new MonoDataProtector(appName, purposes);
}
public IDataProtector CreateProtector(string purpose)
{
return Create(new string[] { purpose });
}
}

View File

@ -0,0 +1,83 @@
//
// MonoDataProtector.cs
//
// Author:
// Paul Schneider <paul@pschneider.fr>
//
// Copyright (c) 2016 GNU GPL
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Security.Cryptography;
using System.IO;
using Microsoft.AspNet.DataProtection;
using System.Linq;
public class MonoDataProtector : IDataProtector
{
private const string PRIMARY_PURPOSE = "IDataProtector";
private readonly string appName;
private readonly DataProtectionScope dataProtectionScope;
private readonly string[] purposes;
public MonoDataProtector(string appName, string[] purposes)
{
if (appName == null) { throw new ArgumentNullException("appName"); }
if (purposes == null) { throw new ArgumentNullException("purposes"); }
this.appName = appName;
this.purposes = purposes;
this.dataProtectionScope = DataProtectionScope.CurrentUser;
}
public IDataProtector CreateProtector(string purpose)
{
if (purposes.Contains(purpose))
return new MonoDataProtector(appName, new string[] { purpose });
return new MonoDataProtector(appName, new string[] { });
}
public byte[] Protect(byte[] userData)
{
return ProtectedData.Protect(userData, this.GetEntropy(), dataProtectionScope);
}
public byte[] Unprotect(byte[] protectedData)
{
return ProtectedData.Unprotect(protectedData, this.GetEntropy(), dataProtectionScope);
}
private byte[] GetEntropy()
{
using (SHA256 sha256 = SHA256.Create())
{
using (MemoryStream memoryStream = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, sha256, CryptoStreamMode.Write))
using (StreamWriter writer = new StreamWriter(cryptoStream))
{
writer.Write(this.appName);
writer.Write(PRIMARY_PURPOSE);
foreach (string purpose in this.purposes)
{
writer.Write(purpose);
}
}
return sha256.Hash;
}
}
}

View File

@ -0,0 +1,90 @@
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.DataProtection;
using System;
using System.IdentityModel.Tokens;
using System.Security.Claims;
using System.Text.RegularExpressions;
namespace OAuth.AspNet.Tokens
{
public class TicketDataFormatTokenValidator : ISecurityTokenValidator
{
#region Constructors
public TicketDataFormatTokenValidator(IDataProtectionProvider dataProtectionProvider, string purpose = "AccessToken") : this(dataProtectionProvider, purpose , new string [] { "v1" }) { }
public TicketDataFormatTokenValidator(IDataProtectionProvider dataProtectionProvider, string purpose, string [] subPurposes)
{
if (dataProtectionProvider == null)
{
dataProtectionProvider = new MonoDataProtectionProvider(System.AppDomain.CurrentDomain.FriendlyName)
.CreateProtector("profile");
}
_ticketDataFormat = new TicketDataFormat(dataProtectionProvider.CreateProtector(purpose, subPurposes));
}
#endregion
#region non-Public Members
private TicketDataFormat _ticketDataFormat;
private const string _serializationRegex = @"^[A-Za-z0-9-_]*$";
private int _maximumTokenSizeInBytes = TokenValidationParameters.DefaultMaximumTokenSizeInBytes;
#endregion
#region Public Members
public bool CanValidateToken
{
get
{
return true;
}
}
public int MaximumTokenSizeInBytes
{
get
{
return _maximumTokenSizeInBytes;
}
set
{
if (value < 1)
throw new ArgumentOutOfRangeException(nameof(MaximumTokenSizeInBytes), "Negative or zero-sized tokens are invalid.");
_maximumTokenSizeInBytes = value;
}
}
public bool CanReadToken(string securityToken)
{
if (string.IsNullOrWhiteSpace(securityToken))
throw new ArgumentException("Security token has no value.", nameof(securityToken));
if (securityToken.Length * 2 > this.MaximumTokenSizeInBytes)
return false;
if (Regex.IsMatch(securityToken, _serializationRegex))
return true;
return false;
}
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
AuthenticationTicket ticket = _ticketDataFormat.Unprotect(securityToken);
validatedToken = null;
return ticket?.Principal;
}
#endregion
}
}

View File

@ -0,0 +1,38 @@
{
"version": "1.0.5-*",
"description": "OAuth AspNet Token",
"authors": [
"Paul Schneider <paul@pschneider.fr>"
],
"packOptions": {
"repository": {
"type": "git",
"url": "https://github.com/pazof/yavsc"
},
"licenseUrl": "https://github.com/pazof/yavsc/blob/vnext/LICENSE",
"requireLicenseAcceptance": true,
"owners": [
"Paul Schneider <paul@pschneider.fr>"
],
"summary": "Yet another very small company",
"projectUrl": "http://yavsc.pschneider.fr",
"tags": [
"Authorization server",
"OAuth",
"Web API"
]
},
"tooling": {
"defaultNamespace": "Yavsc"
},
"dependencies": {
"Newtonsoft.Json": "9.0.1",
"Microsoft.AspNet.Authentication.JwtBearer": "1.0.0-rc1-final",
"Microsoft.AspNet.DataProtection": "1.0.0-rc1-final"
},
"frameworks": {
"dnx451": {
"frameworkAssemblies": {}
}
}
}

View File

@ -0,0 +1,480 @@
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Net;
using System.Text;
using GetUsernameAsyncFunc=System.Func<System.Collections.Generic.IDictionary<string,string>, System.Threading.Tasks.Task<string>>;
using System.IO;
using Newtonsoft.Json;
namespace Yavsc.Authentication
{
public class OAuthenticator
{
public OAuthenticator()
{
}
string clientId;
string clientSecret;
string scope;
Uri authorizeUrl;
Uri accessTokenUrl;
Uri redirectUrl;
GetUsernameAsyncFunc getUsernameAsync;
string requestState;
bool reportedForgery = false;
/// <summary>
/// Gets the client identifier.
/// </summary>
/// <value>The client identifier.</value>
public string ClientId
{
get { return this.clientId; }
}
/// <summary>
/// Gets the client secret.
/// </summary>
/// <value>The client secret.</value>
public string ClientSecret
{
get { return this.clientSecret; }
}
/// <summary>
/// Gets the authorization scope.
/// </summary>
/// <value>The authorization scope.</value>
public string Scope
{
get { return this.scope; }
}
/// <summary>
/// Gets the authorize URL.
/// </summary>
/// <value>The authorize URL.</value>
public Uri AuthorizeUrl
{
get { return this.authorizeUrl; }
}
/// <summary>
/// Gets the access token URL.
/// </summary>
/// <value>The URL used to request access tokens after an authorization code was received.</value>
public Uri AccessTokenUrl
{
get { return this.accessTokenUrl; }
}
/// <summary>
/// Redirect Url
/// </summary>
public Uri RedirectUrl
{
get { return this.redirectUrl; }
}
/// <summary>
/// Initializes a new <see cref="ZicMoove.Droid.OAuth.YaOAuth2WebAuthenticator"/>
/// that authenticates using implicit granting (token).
/// </summary>
/// <param name='clientId'>
/// Client identifier.
/// </param>
/// <param name='scope'>
/// Authorization scope.
/// </param>
/// <param name='authorizeUrl'>
/// Authorize URL.
/// </param>
/// <param name='redirectUrl'>
/// Redirect URL.
/// </param>
/// <param name='getUsernameAsync'>
/// Method used to fetch the username of an account
/// after it has been successfully authenticated.
/// </param>
public OAuthenticator(string clientId, string scope, Uri authorizeUrl, Uri redirectUrl, GetUsernameAsyncFunc getUsernameAsync = null)
: this(redirectUrl)
{
if (string.IsNullOrEmpty(clientId))
{
throw new ArgumentException("clientId must be provided", "clientId");
}
if (authorizeUrl==null)
throw new ArgumentNullException("authorizeUrl");
this.clientId = clientId;
this.scope = scope ?? "";
this.authorizeUrl = authorizeUrl ;
this.getUsernameAsync = getUsernameAsync;
this.accessTokenUrl = null;
}
/// <summary>
/// Initializes a new instance <see cref="ZicMoove.Droid.OAuth.YaOAuth2WebAuthenticator"/>
/// that authenticates using authorization codes (code).
/// </summary>
/// <param name='clientId'>
/// Client identifier.
/// </param>
/// <param name='clientSecret'>
/// Client secret.
/// </param>
/// <param name='scope'>
/// Authorization scope.
/// </param>
/// <param name='authorizeUrl'>
/// Authorize URL.
/// </param>
/// <param name='redirectUrl'>
/// Redirect URL.
/// </param>
/// <param name='accessTokenUrl'>
/// URL used to request access tokens after an authorization code was received.
/// </param>
/// <param name='getUsernameAsync'>
/// Method used to fetch the username of an account
/// after it has been successfully authenticated.
/// </param>
public OAuthenticator(string clientId, string clientSecret, string scope, Uri authorizeUrl, Uri redirectUrl, Uri accessTokenUrl, GetUsernameAsyncFunc getUsernameAsync = null)
: this(redirectUrl, clientSecret, accessTokenUrl)
{
if (string.IsNullOrEmpty(clientId))
{
throw new ArgumentException("clientId must be provided", "clientId");
}
this.clientId = clientId;
if (string.IsNullOrEmpty(clientSecret))
{
throw new ArgumentException("clientSecret must be provided", "clientSecret");
}
this.clientSecret = clientSecret;
this.scope = scope ?? "";
if (authorizeUrl == null)
{
throw new ArgumentNullException("authorizeUrl");
}
this.authorizeUrl = authorizeUrl;
if (accessTokenUrl == null)
{
throw new ArgumentNullException("accessTokenUrl");
}
this.accessTokenUrl = accessTokenUrl;
if (redirectUrl == null)
throw new Exception("redirectUrl is null");
this.redirectUrl = redirectUrl;
this.getUsernameAsync = getUsernameAsync;
}
OAuthenticator(Uri redirectUrl, string clientSecret = null, Uri accessTokenUrl = null)
{
this.redirectUrl = redirectUrl;
this.clientSecret = clientSecret;
this.accessTokenUrl = accessTokenUrl;
//
// Generate a unique state string to check for forgeries
//
var chars = new char[16];
var rand = new Random();
for (var i = 0; i < chars.Length; i++)
{
chars[i] = (char)rand.Next((int)'a', (int)'z' + 1);
}
this.requestState = new string(chars);
}
bool IsImplicit { get { return accessTokenUrl == null; } }
/// <summary>
/// Method that returns the initial URL to be displayed in the web browser.
/// </summary>
/// <returns>
/// A task that will return the initial URL.
/// </returns>
public Task<Uri> GetInitialUrlAsync()
{
var url = new Uri(string.Format(
"{0}?client_id={1}&redirect_uri={2}&response_type={3}&scope={4}&state={5}",
authorizeUrl.AbsoluteUri,
Uri.EscapeDataString(clientId),
Uri.EscapeDataString(RedirectUrl.AbsoluteUri),
IsImplicit ? "token" : "code",
Uri.EscapeDataString(scope),
Uri.EscapeDataString(requestState)));
var tcs = new TaskCompletionSource<Uri>();
tcs.SetResult(url);
return tcs.Task;
}
/// <summary>
/// Raised when a new page has been loaded.
/// </summary>
/// <param name='url'>
/// URL of the page.
/// </param>
/// <param name='query'>
/// The parsed query of the URL.
/// </param>
/// <param name='fragment'>
/// The parsed fragment of the URL.
/// </param>
protected void OnPageEncountered(Uri url, IDictionary<string, string> query, IDictionary<string, string> fragment)
{
if (url.AbsoluteUri.StartsWith(this.redirectUrl.AbsoluteUri))
{
// if (!this.redirectUrl.Equals(url)) {
// this is not our redirect page,
// but perhaps one one the third party identity providers
// One don't check for a state here.
//
/* if (fragment.ContainsKey("continue")) {
var cont = fragment["continue"];
// TODO continue browsing this address
var tcs = new TaskCompletionSource<Uri>();
tcs.SetResult(new Uri(cont));
tcs.Task.RunSynchronously();
}
return;*/
// }
var all = new Dictionary<string, string>(query);
foreach (var kv in fragment)
all[kv.Key] = kv.Value;
//
// Check for forgeries
//
if (all.ContainsKey("state"))
{
if (all["state"] != requestState && !reportedForgery)
{
reportedForgery = true;
OnError("Invalid state from server. Possible forgery!");
return;
}
}
}
}
private void OnError(string v)
{
throw new NotImplementedException();
}
private void OnError(AggregateException ex)
{
throw new NotImplementedException();
}
/// <summary>
/// Raised when a new page has been loaded.
/// </summary>
/// <param name='url'>
/// URL of the page.
/// </param>
/// <param name='query'>
/// The parsed query string of the URL.
/// </param>
/// <param name='fragment'>
/// The parsed fragment of the URL.
/// </param>
protected void OnRedirectPageLoaded(Uri url, IDictionary<string, string> query, IDictionary<string, string> fragment)
{
//
// Look for the access_token
//
if (fragment.ContainsKey("access_token"))
{
//
// We found an access_token
//
OnRetrievedAccountProperties(fragment);
}
else if (!IsImplicit)
{
//
// Look for the code
//
if (query.ContainsKey("code"))
{
var code = query["code"];
RequestAccessTokenAsync(code).ContinueWith(task =>
{
if (task.IsFaulted)
{
OnError(task.Exception);
}
else
{
OnRetrievedAccountProperties(task.Result);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
else
{
OnError("Expected code in response, but did not receive one.");
return;
}
}
else
{
OnError("Expected access_token in response, but did not receive one.");
return;
}
}
/// <summary>
/// Asynchronously requests an access token with an authorization <paramref name="code"/>.
/// </summary>
/// <returns>
/// A dictionary of data returned from the authorization request.
/// </returns>
/// <param name='code'>The authorization code.</param>
/// <remarks>Implements: http://tools.ietf.org/html/rfc6749#section-4.1</remarks>
Task<IDictionary<string, string>> RequestAccessTokenAsync(string code)
{
var queryValues = new Dictionary<string, string> {
{ "grant_type", "authorization_code" },
{ "code", code },
{ "redirect_uri", RedirectUrl.AbsoluteUri },
{ "client_id", clientId }
};
if (!string.IsNullOrEmpty(clientSecret))
{
queryValues["client_secret"] = clientSecret;
}
return RequestAccessTokenAsync(queryValues);
}
/// <summary>
/// Asynchronously makes a request to the access token URL with the given parameters.
/// </summary>
/// <param name="queryValues">The parameters to make the request with.</param>
/// <returns>The data provided in the response to the access token request.</returns>
public async Task<IDictionary<string, string>> RequestAccessTokenAsync(IDictionary<string, string> queryValues)
{
StringBuilder postData = new StringBuilder();
if (!queryValues.ContainsKey("client_id"))
{
postData.Append("client_id="+Uri.EscapeDataString($"{this.clientId}")+"&");
}
if (!queryValues.ContainsKey("client_secret"))
{
postData.Append("client_secret="+Uri.EscapeDataString($"{this.clientSecret}")+"&");
}
if (!queryValues.ContainsKey("scope"))
{
postData.Append("scope="+Uri.EscapeDataString($"{this.scope}")+"&");
}
foreach (string key in queryValues.Keys)
{
postData.Append($"{key}="+Uri.EscapeDataString($"{queryValues[key]}")+"&");
}
var req = WebRequest.Create(accessTokenUrl);
(req as HttpWebRequest).Accept = "application/json";
req.Method = "POST";
var body = Encoding.UTF8.GetBytes(postData.ToString());
req.ContentLength = body.Length;
req.ContentType = "application/x-www-form-urlencoded";
var s = req.GetRequestStream();
s.Write(body, 0, body.Length);
var auth = await req.GetResponseAsync();
var repstream = auth.GetResponseStream();
var respReader = new StreamReader(repstream);
var text = await respReader.ReadToEndAsync();
req.Abort();
// Parse the response
var data = text.Contains("{") ? JsonDecode(text) : FormDecode(text);
if (data.ContainsKey("error"))
{
OnError("Error authenticating: " + data["error"]);
}
else if (data.ContainsKey("access_token"))
{
return data;
}
else
{
OnError("Expected access_token in access token response, but did not receive one.");
}
return data;
}
private IDictionary<string,string> FormDecode(string text)
{
throw new NotImplementedException();
}
private IDictionary<string,string> JsonDecode(string text)
{
return JsonConvert.DeserializeObject<Dictionary<string,string>>(text);
}
/// <summary>
/// Event handler that is fired when an access token has been retreived.
/// </summary>
/// <param name='accountProperties'>
/// The retrieved account properties
/// </param>
protected virtual void OnRetrievedAccountProperties(IDictionary<string, string> accountProperties)
{
//
// Now we just need a username for the account
//
if (getUsernameAsync != null)
{
getUsernameAsync(accountProperties).ContinueWith(task =>
{
if (task.IsFaulted)
{
OnError(task.Exception);
}
else
{
OnSucceeded(task.Result, accountProperties);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
else
{
OnSucceeded("", accountProperties);
}
}
private void OnSucceeded(string v, IDictionary<string, string> accountProperties)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,10 @@
namespace Yavsc.Models.Billing
{
public static class BillingCodes
{
public const string Rdv = "Rdv";
public const string MBrush = "MBrush";
public const string Brush = "Brush";
}
}

View File

@ -0,0 +1,9 @@
namespace Yavsc
{
public interface IAccountBalance
{
long ContactCredits { get; set; }
decimal Credits { get; set; }
string UserId { get; set; }
}
}

View File

@ -0,0 +1,14 @@
namespace Yavsc.Billing
{
public interface IBillItem {
string Name { get; set; }
string Description { get; set; }
int Count { get; set; }
decimal UnitaryCost { get; set; }
string Currency { get; set; }
string Reference { get; }
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using Yavsc.Services;
namespace Yavsc.Billing
{
public interface IBillable {
string Description { get; }
List<IBillItem> GetBillItems();
long Id { get; set; }
string ActivityCode { get; set; }
string PerformerId { get; set; }
string ClientId { get; set; }
/// <summary>
/// Date de validation de la demande par le client
/// </summary>
/// <returns></returns>
DateTime? ValidationDate { get; }
bool GetIsAcquitted ();
string GetFileBaseName (IBillingService service);
}
}

View File

@ -0,0 +1,8 @@
namespace Yavsc.Billing
{
public interface IBillingImpacter { 
decimal Impact(decimal orgValue);
}
}

View File

@ -0,0 +1,13 @@
namespace Yavsc.Billing
{
public interface ICommandLine : IBillItem
{
// FIXME too hard: no such generic name in any interface
long Id { get; set; }
// FIXME too far: perhaps no existing estimate
long EstimateId { get; set; }
}
}

View File

@ -0,0 +1,18 @@
namespace Yavsc
{
using System.Collections.Generic;
public interface IEstimate
{
List<string> AttachedFiles { get; set; }
List<string> AttachedGraphics { get; }
string ClientId { get; set; }
long? CommandId { get; set; }
string CommandType { get; set; }
string Description { get; set; }
long Id { get; set; }
string OwnerId { get; set; }
string Title { get; set; }
}
}

View File

@ -0,0 +1,13 @@

namespace Yavsc
{
public interface IBlogPost : IBaseTrackedEntity, IIdentified<long>, IRating<long>, ITitle
{
string AuthorId { get; set; }
string Content { get; set; }
string Photo { get; set; }
bool Visible { get; set; }
}
}

View File

@ -0,0 +1,63 @@
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Text;
using Yavsc.ViewModels.UserFiles;
namespace Yavsc.Abstract.FileSystem
{
public static class AbstractFileSystemHelpers
{
public static string UserBillsDirName { set; get; }
public static string UserFilesDirName { set; get; }
public static bool IsValidYavscPath(this string path)
{
if (string.IsNullOrEmpty(path)) return true;
foreach (var name in path.Split(Path.DirectorySeparatorChar))
{
if (!IsValidDirectoryName(name) || name.Equals("..") || name.Equals("."))
return false;
}
if (path[path.Length-1]==FileSystemConstants.RemoteDirectorySeparator) return false;
return true;
}
public static bool IsValidDirectoryName(this string name)
{
return !name.Any(c => !FileSystemConstants.ValidFileNameChars.Contains(c));
}
// Ensure this path is canonical,
// No "dirto/./this", neither "dirt/to/that/"
// no .. and each char must be listed as valid in constants
public static string FilterFileName(string fileName)
{
if (fileName==null) return null;
StringBuilder sb = new StringBuilder();
foreach (var c in fileName)
{
if (FileSystemConstants.ValidFileNameChars.Contains(c))
sb.Append(c);
else sb.Append('_');
}
return sb.ToString();
}
public static UserDirectoryInfo GetUserFiles(this ClaimsPrincipal user, string subdir)
{
UserDirectoryInfo di = new UserDirectoryInfo(UserFilesDirName, user.Identity.Name, subdir);
return di;
}
}
public static class FileSystemConstants
{
public const char RemoteDirectorySeparator = '/';
public static char[] ValidFileNameChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-=_~. ".ToCharArray();
}
}

View File

@ -0,0 +1,8 @@
namespace Yavsc.Abstract.FileSystem {
public interface IDirectoryShortInfo
{
string Name { get; set; }
bool IsEmpty { get; set; }
}
}

View File

@ -0,0 +1,15 @@
namespace Yavsc.Abstract.FileSystem
{
public interface IFileRecievedInfo
{
string MimeType { get; set; }
string DestDir { get; set; }
string FileName { get; set; }
bool Overriden { get; set; }
bool QuotaOffensed { get; set; }
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.IO;
using System.Linq;
using Yavsc.Abstract.FileSystem;
namespace Yavsc.ViewModels.UserFiles
{
public class UserDirectoryInfo
{
public string UserName { get; set; }
public string SubPath { get; set; }
public RemoteFileInfo [] Files {
get; set;
}
public DirectoryShortInfo [] SubDirectories { 
get; set;
}
private DirectoryInfo dInfo;
// for deserialization
public UserDirectoryInfo()
{
}
public UserDirectoryInfo(string userReposPath, string username, string path)
{
if (string.IsNullOrWhiteSpace(username))
throw new NotSupportedException("No user name, no user dir.");
UserName = username;
var finalPath = username;
if (!finalPath.IsValidYavscPath())
throw new InvalidOperationException(
$"File name contains invalid chars ({finalPath})");
dInfo = new DirectoryInfo(
userReposPath+Path.DirectorySeparatorChar+finalPath);
if (dInfo.Exists) {
Files = dInfo.GetFiles().Select
( entry => new RemoteFileInfo { Name = entry.Name, Size = entry.Length,
CreationTime = entry.CreationTime, LastModified = entry.LastWriteTime }).ToArray();
SubDirectories = dInfo.GetDirectories().Select
( d=> new DirectoryShortInfo { Name= d.Name, IsEmpty=false } ).ToArray();
}
else {
// don't return null, but empty arrays
Files = new RemoteFileInfo[0];
SubDirectories = new DirectoryShortInfo[0];
}
}
}
public class DirectoryShortInfo: IDirectoryShortInfo {
public string Name { get; set; }
public bool IsEmpty { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
namespace Yavsc.ViewModels
{
public class RemoteFileInfo
{
public string Name { get; set; }
public long Size { get; set; }
public DateTime CreationTime { get; set; }
public DateTime LastModified { get; set; }
}
}

View File

@ -0,0 +1,46 @@
//
// CalendarEventList.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace Yavsc.Models.Google
{
/// <summary>
/// Calendar event list.
/// </summary>
[Obsolete("use GoogleUse.Apis")]
public class CalendarEventList
{
/// <summary>
/// The next page token.
/// </summary>
public string nextPageToken;
/// <summary>
/// The next sync token.
/// </summary>
public string nextSyncToken;
/// <summary>
/// The items.
/// </summary>
public Resource [] items ;
}
}

View File

@ -0,0 +1,56 @@
//
// CalendarList.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2014 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace Yavsc.Models.Google.Calendar
{
/// <summary>
/// Calendar list.
/// </summary>
[Obsolete("use Google.Apis")]
public class CalendarList {
/// <summary>
/// Gets or sets the kind.
/// </summary>
/// <value>The kind.</value>
public string kind { get; set;}
/// <summary>
/// Gets or sets the etag.
/// </summary>
/// <value>The etag.</value>
public string etag { get; set; }
/// <summary>
/// Gets or sets the next sync token.
/// </summary>
/// <value>The next sync token.</value>
public string description { get; set; }
public string summpary { get; set; }
public string nextSyncToken { get; set; }
/// <summary>
/// Gets or sets the items.
/// </summary>
/// <value>The items.</value>
public CalendarListEntry[] items { get; set; }
}
}

View File

@ -0,0 +1,114 @@
//
// CalendarListEntry.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2014 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace Yavsc.Models.Google.Calendar
{
/// <summary>
/// Calendar list entry.
/// </summary>
///
[Obsolete("use GoogleUse.Apis")]
public class CalendarListEntry {
/// <summary>
/// Gets or sets the kind.
/// </summary>
/// <value>The kind.</value>
public string kind { get; set;}
/// <summary>
/// Gets or sets the etag.
/// </summary>
/// <value>The etag.</value>
public string etag { get; set; }
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>The identifier.</value>
public string id { get; set; }
/// <summary>
/// Gets or sets the summary.
/// </summary>
/// <value>The summary.</value>
public string summary { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>The description.</value>
public string description { get; set; }
/// <summary>
/// Gets or sets the time zone.
/// </summary>
/// <value>The time zone.</value>
public string timeZone { get; set; }
/// <summary>
/// Gets or sets the color identifier.
/// </summary>
/// <value>The color identifier.</value>
public string colorId { get; set; }
/// <summary>
/// Gets or sets the color of the background.
/// </summary>
/// <value>The color of the background.</value>
public string backgroundColor { get; set; }
/// <summary>
/// Gets or sets the color of the foreground.
/// </summary>
/// <value>The color of the foreground.</value>
public string foregroundColor { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="Yavsc.Model.Google.CalendarListEntry"/> is selected.
/// </summary>
/// <value><c>true</c> if selected; otherwise, <c>false</c>.</value>
public bool selected { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="Yavsc.Model.Google.CalendarListEntry"/> is primary.
/// </summary>
/// <value><c>true</c> if primary; otherwise, <c>false</c>.</value>
public bool primary { get; set; }
/// <summary>
/// Gets or sets the access role.
/// </summary>
/// <value>The access role.</value>
public string accessRole { get; set; }
/// <summary>
/// Reminder.
/// </summary>
/// <summary>
/// Gets or sets the default reminders.
/// </summary>
/// <value>The default reminders.</value>
public Reminder[] defaultReminders { get; set; }
/* "notificationSettings": { "notifications":
[ { "type": "eventCreation", "method": "email" },
{ "type": "eventChange", "method": "email" },
{ "type": "eventCancellation", "method": "email" },
{ "type": "eventResponse", "method": "email" } ] }, "primary": true },
*/
}
/// <summary>
/// Reminder.
/// </summary>
}

View File

@ -0,0 +1,18 @@
using System;
namespace Yavsc.Models.Google.Calendar
{
[Obsolete("use GoogleUse.Apis")]
public class Reminder {
/// <summary>
/// Gets or sets the method.
/// </summary>
/// <value>The method.</value>
public string method { get; set; }
/// <summary>
/// Gets or sets the minutes.
/// </summary>
/// <value>The minutes.</value>
public int minutes { get; set; }
}
}

View File

@ -0,0 +1,43 @@
//
// GDate.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace Yavsc.Models.Google
{
/// <summary>
/// G date.
/// </summary>
public class GDate {
/// <summary>
/// The date.
/// </summary>
public DateTime? date;
/// <summary>
/// The datetime.
/// </summary>
public DateTime? datetime;
/// <summary>
/// The time zone.
/// </summary>
public string timeZone;
}
}

View File

@ -0,0 +1,78 @@
//
// MessageWithPayLoad.cs
//
// Author:
// paul <>
//
// Copyright (c) 2015 paul
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using Yavsc.Models.Messaging;
namespace Yavsc.Models.Google.Messaging
{
// https://gcm-http.googleapis.com/gcm/send
/// <summary>
/// Message with payload.
/// </summary>
public class MessageWithPayload<T> {
/// <summary>
/// To.
/// </summary>
public string to;
/// <summary>
/// The registration identifiers.
/// </summary>
public string [] registration_ids;
/// <summary>
/// The data.
/// </summary>
public T data ;
/// <summary>
/// The notification.
/// </summary>
public Notification notification;
/// <summary>
/// The collapse key.
/// </summary>
public string collapse_key; // in order to collapse ...
/// <summary>
/// The priority.
/// </summary>
public int priority; // between 0 and 10, 10 is the lowest!
/// <summary>
/// The content available.
/// </summary>
public bool content_available;
/// <summary>
/// The delay while idle.
/// </summary>
public bool delay_while_idle;
/// <summary>
/// The time to live.
/// </summary>
public int time_to_live; // seconds
/// <summary>
/// The name of the restricted package.
/// </summary>
public string restricted_package_name;
/// <summary>
/// The dry run.
/// </summary>
public bool dry_run;
}
}

View File

@ -0,0 +1,69 @@
//
// MessageWithPayloadResponse.cs
//
// Author:
// paul <paul@pschneider.fr>
//
// Copyright (c) 2015 paul
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Yavsc.Models.Google.Messaging
{
// https://gcm-http.googleapis.com/gcm/send
/// <summary>
/// Message with payload response.
/// </summary>
public class MessageWithPayloadResponse {
/// <summary>
/// The multicast identifier.
/// </summary>
public string multicast_id;
/// <summary>
/// The success count.
/// </summary>
public int success;
/// <summary>
/// The failure count.
/// </summary>
public int failure;
/// <summary>
/// The canonical identifiers... ?!?
/// </summary>
public string canonical_ids;
/// <summary>
/// Detailled result.
/// </summary>
public class Result {
/// <summary>
/// The message identifier.
/// </summary>
public string message_id;
/// <summary>
/// The registration identifier.
/// </summary>
public string registration_id;
/// <summary>
/// The error.
/// </summary>
public string error;
}
/// <summary>
/// The results.
/// </summary>
public Result [] results;
}
}

View File

@ -0,0 +1,165 @@
//
// People.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2014 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Yavsc.Models.Google
{
/// <summary>
/// People.
/// </summary>
public class People {
/// <summary>
/// Gets or sets the kind.
/// </summary>
/// <value>The kind.</value>
public string kind { get; set; }
/// <summary>
/// Gets or sets the etag.
/// </summary>
/// <value>The etag.</value>
public string etag { get; set; }
/// <summary>
/// Gets or sets the gender.
/// </summary>
/// <value>The gender.</value>
public string gender { get; set; }
/// <summary>
/// E mail.
/// </summary>
public class EMail{
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
public string value { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public string type { get; set; }
}
/// <summary>
/// Gets or sets the emails.
/// </summary>
/// <value>The emails.</value>
public EMail[] emails { get; set; }
/// <summary>
/// Gets or sets the type of the object.
/// </summary>
/// <value>The type of the object.</value>
public string objectType { get; set; }
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>The identifier.</value>
public string id { get; set; }
/// <summary>
/// Gets or sets the display name.
/// </summary>
/// <value>The display name.</value>
public string displayName { get; set; }
/// <summary>
/// Name.
/// </summary>
public class Name {
/// <summary>
/// Gets or sets the name of the family.
/// </summary>
/// <value>The name of the family.</value>
public string familyName { get; set; }
/// <summary>
/// Gets or sets the name of the given.
/// </summary>
/// <value>The name of the given.</value>
public string givenName { get; set; }
}
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public Name name { get; set;}
/// <summary>
/// Gets or sets the URL.
/// </summary>
/// <value>The URL.</value>
public string url { get; set; }
/// <summary>
/// Image.
/// </summary>
public class Image {
/// <summary>
/// Gets or sets the URL.
/// </summary>
/// <value>The URL.</value>
public string url { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="Yavsc.Model.Google.People.Image"/> is default.
/// </summary>
/// <value><c>true</c> if is default; otherwise, <c>false</c>.</value>
public bool isDefault { get; set; }
}
/// <summary>
/// Gets or sets the image.
/// </summary>
/// <value>The image.</value>
public Image image { get; set; }
/// <summary>
/// Place.
/// </summary>
public class Place {
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
public string value { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="Yavsc.Model.Google.People.Place"/> is primary.
/// </summary>
/// <value><c>true</c> if primary; otherwise, <c>false</c>.</value>
public bool primary { get; set; }
}
/// <summary>
/// Gets or sets the places lived.
/// </summary>
/// <value>The places lived.</value>
public Place[] placesLived { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="Yavsc.Model.Google.People"/> is plus user.
/// </summary>
/// <value><c>true</c> if is plus user; otherwise, <c>false</c>.</value>
public bool isPlusUser { get; set; }
/// <summary>
/// Gets or sets the language.
/// </summary>
/// <value>The language.</value>
public string language { get; set; }
/// <summary>
/// Gets or sets the circled by count.
/// </summary>
/// <value>The circled by count.</value>
public int circledByCount { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="Yavsc.Model.Google.People"/> is verified.
/// </summary>
/// <value><c>true</c> if verified; otherwise, <c>false</c>.</value>
public bool verified { get; set; }
}
}

View File

@ -0,0 +1,45 @@
//
// Resource.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Yavsc.Models.Google
{
/// <summary>
/// Resource.
/// </summary>
public class Resource {
public string id;
public string location;
public string status;
public GDate start;
public GDate end;
public string recurence;
public string description;
public string summary;
/// <summary>
/// Available <=> transparency == "transparent"
/// </summary>
public string transparency;
}
}

View File

@ -0,0 +1,47 @@
//
// Entity.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Yavsc.Models.Google
{
/// <summary>
/// Entity.
/// </summary>
public class Entity
{
/// <summary>
/// The I.
/// </summary>
public string ID;
/// <summary>
/// The name.
/// </summary>
public string Name;
/// <summary>
/// The type: AUTOMOBILE: A car or passenger vehicle.
/// * TRUCK: A truck or cargo vehicle.
/// * WATERCRAFT: A boat or other waterborne vehicle.
/// * PERSON: A person.
/// </summary>
public string Type;
}
}

View File

@ -0,0 +1,37 @@
//
// EntityQuery.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Yavsc.Models.Google
{
/// <summary>
/// Entity query.
/// </summary>
public class EntityQuery {
/// <summary>
/// The entity identifiers.
/// </summary>
public string [] EntityIds;
/// <summary>
/// The minimum identifier.
/// </summary>
public string MinId;
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace Yavsc
{
public interface IBaseTrackedEntity
{
DateTime DateCreated { get; set; }
string UserCreated { get; set; }
DateTime DateModified { get; set; }
string UserModified { get; set; }
}
}

View File

@ -0,0 +1,36 @@
//
// IIdentified.cs
//
// Author:
// Paul Schneider <paul@pschneider.fr>
//
// Copyright (c) 2015 GNU GPL
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Yavsc
{
/// <summary>
/// I identified.
/// </summary>
public interface IIdentified<T>
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>The identifier.</value>
T Id { get; set; }
}
}

View File

@ -0,0 +1,98 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Yavsc.Abstract.IT
{
public class CharArray : List<char>, IEnumerable<char>, IList<char>
{
public CharArray (char [] charArray) : base (charArray)
{
}
public CharArray (IList<char> word): base(word) {
}
public CharArray (IEnumerable<char> word): base(word) {
}
public IList<char> Aggregate(char other)
{
this.Add(other);
return this;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class CodeFromChars : List<CharArray>, ICode<char>
{
public void AddLetter(IEnumerable<char> letter)
{
var candide = new CharArray(letter);
// TODO build new denied letters: compute the automate
// and check it is determinised
// Automate a = new Automate();
Add(candide);
}
public bool Validate()
{
// this is a n*n task
throw new NotImplementedException();
}
IEnumerator<IEnumerable<char>> IEnumerable<IEnumerable<char>>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private class Automate
{
int State { get; set; }
Dictionary<int, Dictionary<int,int>> Transitions { get; set; }
CodeFromChars Code { get; set; }
void Compute(CharArray chars)
{
if (!Code.Contains(chars)) {
State = -1;
return;
}
if (!Transitions.ContainsKey(State)) {
State = -2;
return;
}
var states = Transitions[State];
int letter = Code.IndexOf(chars);
if (!states.ContainsKey(letter))
{
State = -3;
return;
}
State = states[letter];
}
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace Yavsc.Abstract.IT
{
// un code est, parmis les ensembles de suites de signes,
// ceux qui n'ont qu'une seule suite de suites pouvant représenter toute suite de suite de signes
public interface ICode<TSign> : IEnumerable<IEnumerable<TSign>>
{
/// <summary>
/// Checks false that a letter list combinaison correspond to another one
/// </summary>
/// <returns></returns>
bool Validate();
/// <summary>
/// Defines a new letter in this code,
/// as an enumerable of <c>TLetter</c>
/// </summary>
/// <param name="letter"></param>
/// <returns></returns>
void AddLetter(IEnumerable<TSign> letter);
}
}

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace Yavsc.Abstract.IT
{
public interface IProject
{
long Id { get; set ; }
string OwnerId { get; set; }
string Name { get; set; }
string Version { get; set; }
IEnumerable<string> GetConfigurations();
}
}

View File

@ -0,0 +1,28 @@
//
// ITitle.cs
//
// Author:
// Paul Schneider <paul@pschneider.fr>
//
// Copyright (c) 2015 GNU GPL
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Yavsc
{
public interface ITitle
{
string Title { get; set; }
}
}

View File

@ -0,0 +1,56 @@
//
// GoogleAuthToken.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2014 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Yavsc.Abstract.Identity
{
/// <summary>
/// Auth token, as they are received.
/// </summary>
public class AuthToken {
/// <summary>
/// Gets or sets the access token.
/// </summary>
/// <value>The access token.</value>
public string access_token { get; set; }
/// <summary>
/// Gets or sets the identifier token.
/// </summary>
/// <value>The identifier token.</value>
public string id_token { get; set; }
/// <summary>
/// Gets or sets the type of the token.
/// </summary>
/// <value>The type of the token.</value>
public string token_type { get; set ; }
/// <summary>
/// Gets or sets the refresh token.
/// </summary>
/// <value>The refresh token.</value>
public string refresh_token { get; set; }
/// <summary>
/// Gets or sets the expires in.
/// </summary>
/// <value>The expires in.</value>
public int expires_in { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace Yavsc.Abstract.Identity
{
public class ClientProviderInfo
{
public string UserName { get; set; }
public string Avatar { get; set; }
[Key]
public string UserId { get; set; }
public string EMail { get; set; }
public string Phone { get; set; }
public long BillingAddressId { get; set; }
}
}

View File

@ -0,0 +1,12 @@
namespace Yavsc.Abstract.Identity
{
public interface IApplicationUser
{
string Id { get; set; }
string UserName { get; set; }
string Avatar { get ; set; }
IAccountBalance AccountBalance { get; set; }
string DedicatedGoogleCalendar { get; set; }
ILocation PostalAddress { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace Yavsc.Abstract.Identity {
public class Me : UserInfo
{
public AuthToken Token {
get;
set;
}
}
}

View File

@ -0,0 +1,8 @@
namespace Yavsc.Abstract.Identity.Security
{
public interface ICircleAuthorization
{
long CircleId { get; set; }
}
}

View File

@ -0,0 +1,11 @@
namespace Yavsc.Abstract.Identity.Security
{
public interface ICircleAuthorized
{
long Id { get; set; }
string GetOwnerId ();
bool AuthorizeCircle(long circleId);
ICircleAuthorization [] GetACL();
}
}

View File

@ -0,0 +1,13 @@
using System;
namespace Yavsc.Abstract.Identity
{
public class TokenInfo
{
public string AccessToken { get; set; }
public string RefreshToken { get; set; }
public int ExpiresIn { set; get; }
public DateTime Received { get; set; }
public string TokenType { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More