Skip to content

mrstebo/Nancy.OAuth2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

53 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build status MyGet Prerelease NuGet Version

Nancy.OAuth2

A Nancy module for adding OAuth2 support.

Based on work by thecodejunkie

Nancy.OAuth2 is available via NuGet:

Install-Package Nancy.OAuth2

Getting Started

Note: In the following examples, IOAuthService and IUserService would be your own implementations

Install the module

public class Bootstrapper : DefaultNancyBootstrapper
{
    protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
    {
        // Default configuration for OAuth.Enable()
        OAuth.Enable(config =>
        {
            config.Base = "/oauth";
            config.AuthorizationRequestRoute = "/authorize";
            config.AuthorizationAllowRoute = "/allow";
            config.AuthorizationDenyRoute = "/deny";
            config.TokenRoute = "/token";
        });

        // If you are using the authorization_code grant, then register your implementation
        container.Register<IAuthorizationEndpointService, AuthorizationEndpointService>();

        // Register your implementation of the ITokenEndpointService
        container.Register<ITokenEndpointService, TokenEndpointService>();

        base.RequestStartup(container, pipelines, context);
    }
}

Create a class that implements the ITokenEndpointService interface.

public class TokenEndpointService : ITokenEndpointService
{
    private readonly IOAuthService _oauthService;
    private readonly IUserService _userService;

    public TokenEndpointService(
        IOAuthService oauthService,
        IUserService userService)
    {
        _oauthService = oauthService;
        _userService = userService;
    }

    public OAuthValidationResult ValidateRequest(TokenRequest request, NancyContext context)
    {
        if (!IsValidClient(request.ClientId, request.ClientSecret))
            return ErrorType.InvalidClient;

        // Only allow certain grant types
        switch (request.GrantType)
        {
            case GrantTypes.Password:
                return ValidatePasswordGrant(request);

            case GrantTypes.Authorization:
                return ValidateAuthorizationCodeGrant(request);

            default:
                return ErrorType.InvalidGrant;
        }
    }

    public TokenResponse CreateTokenResponse(TokenRequest request, NancyContext context)
    {
        // Build a token and store it somewhere so you can validate it later
        return BuildTokenResponse(request, context);
    }

    private bool IsValidClient(string clientId, string clientSecret)
    {
        var client = _oauthService.FindClientById(request.ClientId);

        return client != null && client.ClientSecret == clientSecret;
    }

    private OAuthValidationResult ValidatePasswordGrant(TokenRequest request)
    {
        var user = _userService.FindUserByUsername(request.Username);

        return user == null || user.HasValidPassword(request.Password)
            ? ErrorType.InvalidGrant
            : ErrorType.None;
    }

    private OAuthValidationResult ValidateAuthorizationCodeGrant(TokenRequest request)
    {
        var authCode = _oauthService.FindAuthorizationCode(request.Code);

        return authCode == null
            ? ErrorType.InvalidGrant
            : ErrorType.None;
    }
}

Authorization

If you want to enable the /authorize endpoints then you will need to create a class that implements the IAuthorizationEndpointService interface.

public class AuthorizationEndpointService : IAuthorizationEndpointService
{
    private readonly IOAuthService _oauthService;
    private readonly IUserService _userService;

    public AuthorizationEndpointService(
        IOAuthService oauthService,
        IUserService userService)
    {
        _oauthService = oauthService;
        _userService = userService;
    }

    public string GenerateAuthorizationToken(AuthorizationRequest request, NancyContext context)
    {
        var client = _oauthService.FindClientById(request.ClientId);
        var user = _userService.FindUserByUsername(context.CurrentUser.UserName);
        var authCode = _oauthService.CreateAuthCode(client, user);

        return authCode.Token;
    }

    public OAuthValidationResult ValidateRequest(AuthorizationRequest request, NancyContext context)
    {
        var client = _oauthService.FindClientById(request.ClientId);

        if (client == null)
            return ErrorType.InvalidClient;

        // Perform validation of the request for the client e.g.
        // - Is the RedirectUri allowed?
        // - Does it support the authorization_code grant?

        return ErrorType.None;
    }

    public Tuple<string, object> GetAuthorizationView(AuthorizationRequest request, NancyContext context)
    {
        var client = _oauthService.FindClientById(request.ClientId);
        var permissions = _oauthService.GetClientPermissions(request.ClientId);

        return new Tuple<string, object>("Authorize", new AuthorizeViewModel
        {
            Name = client.Name,
            Description = client.Description,
            Permissions = permissions
        });
    }
}

Any contributions will be greatly appreciated!