Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Matt Brailsford 3960 posts 21147 karma points MVP 9x c-trib
    Feb 03, 2016 @ 19:29
    Matt Brailsford
    0

    Securing a custom API controller with OAuth + Members?

    Anyone got any experience of securing a custom API using OAuth and Umbraco members? I'm building a site that has members sign up, but I also then need their data to be accessible via a mobile app, so I want to create an API controller to communicate with and protect it using OAuth backed by the Umbraco Members system for the actual login credentials.

    I know there is the work Warren did with JWT http://creativewebspecialist.co.uk/2015/01/06/securing-umbraco-web-apis-using-json-web-tokens/ but I was wondering if anyone has done anything using OAuth and have any code they don't mind sharing?

    Cheers

    Matt

  • Ian 178 posts 752 karma points
    Feb 03, 2016 @ 19:36
    Ian
    0

    Hi matt have you seen the thread I had with John. We both had the idea of using identity server to do what you are describing. I posted some code which used the umbraco user manager as backing for the authentication.

    See here if its any help to you https://our.umbraco.org/forum/developers/extending-umbraco/74636-use-identityserver-with-user-membership

    I was using an authentication code flow with refresh tokens to provide long lived access but it also works with implicit flow if thats more appropriate.

  • Anders Bjerner 476 posts 2885 karma points MVP 6x admin c-trib
    Feb 03, 2016 @ 20:18
    Anders Bjerner
    0

    Not sure which OAuth version you're looking for. When following the OAuth 2.0 specification, a user must specify an access token.

    In the very small example below, my OAuthAttribute class will validate the access token, and find the member behind the access token. Given that this is just an example, the access token is simply validated against a small switch statement.

    In a real scenario, you should obviously have some way of generating and storing the access tokens for each member, and possibly also log the member in for that particular request.

    using System.Web;
    using System.Web.Http;
    using System.Web.Http.Controllers;
    using Umbraco.Web.WebApi;
    
    [OAuthAuthorizeAttribute]
    public class OAuthApiController : UmbracoApiController {
    
        // Just a "dummy" class that you should inherit from
    
    }
    
    public class OAuthAuthorizeAttribute : AuthorizeAttribute {
    
        protected override bool IsAuthorized(HttpActionContext actionContext) {
    
            string accessToken = HttpContext.Current.Request.QueryString["access_token"];
    
            int memberId = 0;
            switch (accessToken) {
                case "let me in": memberId = 1096; break;
                case "come on": memberId = 1097; break;
                case "i have cookies": memberId = 1098; break;
            }
    
            return memberId > 0;
    
        }
    
    }
    

    OAuth 1.0a involves a lot more security, but that should be possible as well.

  • Shannon Deminick 1523 posts 5256 karma points MVP
    Feb 04, 2016 @ 11:28
    Shannon Deminick
    101

    Hi Matt,

    For this to work you need a Token Server and a Token Authentication Handler.

    The building blocks are there for you to copy in various projects but unfortunately it's not built (yet), this type of thing is planned for the RestApi project but of course we've all got way too busy with everything else happening ;)

    To make this easier, you should:

  • Matt Brailsford 3960 posts 21147 karma points MVP 9x c-trib
    Feb 05, 2016 @ 08:18
    Matt Brailsford
    1

    So I have managed to get this working based on the suggestions from Shan. Right now it's just a POC so not very friendly to share, but if I can tidy things up, I'll post on here.

    Needless to say, if you do follow Shans guidelines, you should be able to get it working. It's just a steep learning curve if you don't know what you are doing (like me).

  • Pramod Modavathodi 1 post 20 karma points
    Jul 07, 2016 @ 06:55
    Pramod Modavathodi
    0

    Hi Matt,

    Can you please post your solution here. We are creating a mobile app which logs into CRM (contact) using custom login(username, password) or social media login. So planning to have something similar you are using.

    Any tip and code will be really helpfu.

    Cheers, Pramod

  • mark firth 31 posts 74 karma points
    Nov 17, 2016 @ 18:09
    mark firth
    0

    I'd also like to to see any code. I've got all the token server and token auth working but only for the back office on the end point /umbraco/oauth/token.

    I can not get the backoffice to create a token if i change the token end point in UseUmbracoBackOfficeTokenAuth method so i am not sure how this gets invoked.

    I've also copied the UseUmbracoBackOfficeTokenAuth and created a new method UseUmbracoMemberTokenAuth with a MemberAuthServerProvider which uses: UmbracoMembersUserManager

    So any pointers would be much appreciated.

    I should add this is version 7.5.2

    cheers,

    mark

  • Nadia 45 posts 122 karma points
    Dec 21, 2016 @ 18:39
    Nadia
    0

    I would be keen on some sample code as well. I have followed Shan's instructions. But I am getting a error in the AuthServerprovider.

    I changed BackOfficeAuthServerProvider in the GrantResourceOwnerCredentials to

    var userManager = context.OwinContext.GetUserManager<UmbracoMembersUserManager<UmbracoApplicationMember>>()`;
    

    But it is returning a null. as userManager.

  • Nadia 45 posts 122 karma points
    Dec 22, 2016 @ 01:46
    Nadia
    0

    I worked out the issue. Here is what I my MembersAuthServerprovider has ended up as

      public class MembersAuthServerProvider : OAuthAuthorizationServerProvider
    {
        private readonly MembersAuthServerProviderOptions _options;
    
        public MembersAuthServerProvider(MembersAuthServerProviderOptions options = null)
        {
            if (options == null)
                options = new MembersAuthServerProviderOptions();
            _options = options;
        }
    
        /// <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 override Task ValidateTokenRequest(OAuthValidateTokenRequestContext context)
        {
            ProcessCors(context);
    
            return base.ValidateTokenRequest(context);
        }
    
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var userManager = context.OwinContext
                    .GetUserManager<UmbracoMembersUserManager<UmbracoApplicationMember>>();
    
            var user = await userManager.FindAsync(context.UserName, context.Password);
    
            if (user == null)
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                return;
            }
    
            ClaimsIdentity oAuthIdentity = await userManager.ClaimsIdentityFactory.CreateAsync(userManager, user, OAuthDefaults.AuthenticationType);
            ClaimsIdentity cookiesIdentity = await userManager.ClaimsIdentityFactory.CreateAsync(userManager, user, CookieAuthenticationDefaults.AuthenticationType); 
    
    
    
            AuthenticationProperties properties = CreateProperties(user.UserName, 1).ToString());
            AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(cookiesIdentity);
    
        }
    
        public override Task TokenEndpoint(OAuthTokenEndpointContext context)
        {
            foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
            {
                context.AdditionalResponseParameters.Add(property.Key, property.Value);
            }
    
            return Task.FromResult<object>(null);
        }
    
        public static AuthenticationProperties CreateProperties(string userName, string id)
        {
            IDictionary<string, string> data = new Dictionary<string, string>
            {
                { "userName", userName },
                 { "id", id}
            };
            return new AuthenticationProperties(data);
        }
    
        private void ProcessCors(OAuthValidateTokenRequestContext context)
        {
            var accessControlRequestMethodHeaders = context.Request.Headers.GetCommaSeparatedValues(CorsConstants.AccessControlRequestMethod);
            var originHeaders = context.Request.Headers.GetCommaSeparatedValues(CorsConstants.Origin);
            var accessControlRequestHeaders = context.Request.Headers.GetCommaSeparatedValues(CorsConstants.AccessControlRequestMethod);
            var corsRequest = new CorsRequestContext
            {
                Host = context.Request.Host.Value,
                HttpMethod = context.Request.Method,
                Origin = originHeaders == null ? null : originHeaders.FirstOrDefault(),
                RequestUri = context.Request.Uri,
                AccessControlRequestMethod = accessControlRequestMethodHeaders == null ? null : accessControlRequestMethodHeaders.FirstOrDefault()
            };
            if (accessControlRequestHeaders != null)
            {
                foreach (var header in context.Request.Headers.GetCommaSeparatedValues(CorsConstants.AccessControlRequestMethod))
                {
                    corsRequest.AccessControlRequestHeaders.Add(header);
                }
            }
    
            var engine = new CorsEngine();
    
            if (corsRequest.IsPreflight)
            {
                try
                {
                    // Make sure Access-Control-Request-Method is valid.
                    // ReSharper disable once UnusedVariable
                    var test = new HttpMethod(corsRequest.AccessControlRequestMethod);
                }
                catch (ArgumentException)
                {
                    context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                    context.SetError("Access Control Request Method Cannot Be Null Or Empty");
                    //context.RequestCompleted();
                    return;
                }
                catch (FormatException)
                {
                    context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                    context.SetError("Invalid Access Control Request Method");
                    //context.RequestCompleted();
                    return;
                }
    
                var result = engine.EvaluatePolicy(corsRequest, _options.CorsPolicy);
    
                if (!result.IsValid)
                {
                    context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                    context.SetError(string.Join(" | ", result.ErrorMessages));
                    //context.RequestCompleted();
                    return;                    
                }
    
                WriteCorsHeaders(result, context);
            }
            else
            {
                var result = engine.EvaluatePolicy(corsRequest, _options.CorsPolicy);
    
                if (result.IsValid)
                {
                    WriteCorsHeaders(result, context);                    
                }
            }
        }
    
        private void WriteCorsHeaders(CorsResult result, OAuthValidateTokenRequestContext context)
        {
            var headers = result.ToResponseHeaders();
    
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    context.Response.Headers.Append(header.Key, header.Value);
                }
            }
        }
    }
    
  • Biagio Paruolo 1550 posts 1755 karma points c-trib
    Jan 20, 2017 @ 12:49
    Biagio Paruolo
    0

    Hi, I've to do the same thing. Do it work?

    So, you installed:

    • Umbracoidentity
    • Umbracoidentityextension
    • UmbracoRestApi

    Here is a JWT implementation : https://creativewebspecialist.co.uk/2015/01/06/securing-umbraco-web-apis-using-json-web-tokens/

  • Nadia 45 posts 122 karma points
    Jan 24, 2017 @ 03:04
    Nadia
    0

    Yes, I installed UmbracoIdenity and UmbracoIndentityExtensions.

    For the JWT token, I just created a formatter , based on this tutorial http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/

  • Biagio Paruolo 1550 posts 1755 karma points c-trib
    Jan 24, 2017 @ 06:46
    Biagio Paruolo
    0

    I see your code MembersAuthServerprovider. Have you putted into OWIN startup? Have you tried to use with social login?

  • Nadia 45 posts 122 karma points
    Jan 24, 2017 @ 18:36
    Nadia
    0

    I use the UmbracoIdentityStartup that is in the extensions package. If you follow that package, you have everything you need, Just need to modify the MemberAuthServerProvider as my code demonstrates

  • Biagio Paruolo 1550 posts 1755 karma points c-trib
    Jan 31, 2017 @ 06:47
    Biagio Paruolo
    0

    I have installed Umbraco Indentity 5.0 and I'm customizing the OAuth Umbraco package and it works server side and thank Identity it's possible to link the social login to Umbraco member. But where is stored the social login info? I think into a local cookie.

    Now, I'm study how to do mobile app side to use social login and then pass the token to Umbraco OAuth to authorize and so.

  • Shannon Deminick 1523 posts 5256 karma points MVP
    Jan 31, 2017 @ 07:13
    Shannon Deminick
    0

    It creates a table in the main Umbraco db.

  • Biagio Paruolo 1550 posts 1755 karma points c-trib
    Jan 31, 2017 @ 08:24
    Biagio Paruolo
    0

    Do you mean UmbracoExternalLogin? Here is the record saved. Is't "providerKey" the autheticate Google token?

    enter image description here

Please Sign in or register to post replies

Write your reply to:

Draft