Copied to clipboard

Flag this post as spam?

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


  • Sören Deger 733 posts 2844 karma points c-trib
    Nov 05, 2014 @ 12:20
    Sören Deger
    0

    Authentication to call the WebAPI by external application

    Hello,

    I understand how I can protect my webApi controller, so that only certain members or users of umbraco can these calls. But I have the requirement to implement an authentication for external application, which calls this webApi.

    For example: 

    An external application invokes a certain URL of my Umbraco WebAPI, must authenticate against a member or otherwise, and if success, it gets the result of the webApi.

    How can I do this? Have anyone a code snippet to solve this? How must call the url of WebAPI for authentication by the external application? Any ideas?

     

    Sören

  • Sebastiaan Janssen 5044 posts 15475 karma points MVP admin hq
    Nov 05, 2014 @ 13:43
    Sebastiaan Janssen
    1

    I don't know any articles off the top of my head but Google for any articles doing this in normal WebApi. UmbracoApiController is a super thin layer on top of ApiController (normal .Net Web API) to just add the Umbraco Member authentication.
    Anything that normal Web API can do is possible with UmbracoApiController so if you figure out how to do it in WebApi, you're done.

  • Sören Deger 733 posts 2844 karma points c-trib
    Nov 05, 2014 @ 13:54
    Sören Deger
    0

    Hi Sebastiaan,

    thank you! I'll be look at this.


    Sören 

  • Sören Deger 733 posts 2844 karma points c-trib
    Nov 06, 2014 @ 11:44
    Sören Deger
    103

    I have found a way to solve this :-) There are also other ways to do this, but this is one solution.

    I have created an ApiController in umbraco with this code:

    using System;
    using System.Security.Principal;
    using System.Text;
    using System.Threading;
    using System.Web.Http;
    using System.Web.Http.Controllers;
    using System.Web.Http.Filters;
    using Umbraco.Web.Mvc;
    using Umbraco.Web.WebApi;
    using Umbraco.Web;
    using Umbraco.Web.Security;
    
    namespace Demo.Controllers
    {
        [MyBasicAuthenticationFilter]   
        [PluginController("demo")]
        public class DemoApiController : UmbracoApiController
        {
            [HttpGet]
            public string test()
            {
                return "Hello World!";
            }
    
        }
    
        ///
        /// Generic Basic Authentication filter that checks for basic authentication
        /// headers and challenges for authentication if no authentication is provided
        /// Sets the Thread Principle with a GenericAuthenticationPrincipal.
        /// 
        /// You can override the OnAuthorize method for custom auth logic that
        /// might be application specific.    
        ///
        ///Always remember that Basic Authentication passes username and passwords
        /// from client to server in plain text, so make sure SSL is used with basic auth
        /// to encode the Authorization header on all requests (not just the login).
        ///
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
        public class BasicAuthenticationFilter : AuthorizationFilterAttribute
        {
            bool Active = true;
    
            public BasicAuthenticationFilter()
            { }
    
            ///
            /// Overriden constructor to allow explicit disabling of this
            /// filter's behavior. Pass false to disable (same as no filter
            /// but declarative)
            ///
            ///
            public BasicAuthenticationFilter(bool active)
            {
                Active = active;
            }
    
    
            ///
            /// Override to Web API filter method to handle Basic Auth check
            ///
            ///
            public override void OnAuthorization(HttpActionContext actionContext)
            {
                if (Active)
                {
                    var identity = ParseAuthorizationHeader(actionContext);
                    if (identity == null)
                    {
                        Challenge(actionContext);
                        return;
                    }
    
    
                    if (!OnAuthorizeUser(identity.Name, identity.Password, actionContext))
                    {
                        Challenge(actionContext);
                        return;
                    }
    
                    var principal = new GenericPrincipal(identity, null);
    
                    Thread.CurrentPrincipal = principal;
    
                    // inside of ASP.NET this is required
                    //if (HttpContext.Current != null)
                    //    HttpContext.Current.User = principal;
    
                    base.OnAuthorization(actionContext);
                }
            }
    
            ///
            /// Base implementation for user authentication - you probably will
            /// want to override this method for application specific logic.
            /// 
            /// The base implementation merely checks for username and password
            /// present and set the Thread principal.
            /// 
            /// Override this method if you want to customize Authentication
            /// and store user data as needed in a Thread Principle or other
            /// Request specific storage.
            ///
            ///
            ///
            ///
            ///
            protected virtual bool OnAuthorizeUser(string username, string password, HttpActionContext actionContext)
            {
                if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
                    return false;
    
                return true;
            }
    
            ///
            /// Parses the Authorization header and creates user credentials
            ///
            ///
            protected virtual BasicAuthenticationIdentity ParseAuthorizationHeader(HttpActionContext actionContext)
            {
                string authHeader = null;
                var auth = actionContext.Request.Headers.Authorization;
                if (auth != null && auth.Scheme == "Basic")
                    authHeader = auth.Parameter;
    
                if (string.IsNullOrEmpty(authHeader))
                    return null;
    
                authHeader = Encoding.Default.GetString(Convert.FromBase64String(authHeader));
    
                var tokens = authHeader.Split(':');
                if (tokens.Length < 2)
                    return null;
    
                return new BasicAuthenticationIdentity(tokens[0], tokens[1]);
            }
    
    
            ///
            /// Send the Authentication Challenge request
            ///
            ///
            ///
            void Challenge(HttpActionContext actionContext)
            {
                var host = actionContext.Request.RequestUri.DnsSafeHost;
                //actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
                actionContext.Response = actionContext.Request.CreateUserNoAccessResponse();
                actionContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", host));
            }
    
        }
    
        public class BasicAuthenticationIdentity : GenericIdentity
        {
            public BasicAuthenticationIdentity(string name, string password)
                : base(name, "Basic")
            {
                this.Password = password;
            }
    
            /// 
            /// Basic Auth Password for custom authentication
            /// 
            public string Password { get; set; }
        }
    
        public class MyBasicAuthenticationFilter : BasicAuthenticationFilter
        {
    
            public MyBasicAuthenticationFilter()
            { }
    
            public MyBasicAuthenticationFilter(bool active)
                : base(active)
            { }
    
    
            protected override bool OnAuthorizeUser(string username, string password, HttpActionContext actionContext)
            {
                MembershipHelper Members = new MembershipHelper(UmbracoContext.Current);
    
                if (Members.Login(username, password))
                {
    // Here you can also check against a specific member type or member group
    Members.Logout(); return true; } return false; } } }

    If you will call the umbraco api from an external application (e.g. other umbraco application, console application, php application or others) you must response an authentication header to the api url.

    This shows how I call the api with authentication header from another umbraco application:

    using System;
    using System.Net;
    using System.Text;
    using System.Web.Http;
    using Umbraco.Web.Mvc;
    using Umbraco.Web.WebApi;
    
    namespace Demo_External.Controllers
    {
        [PluginController("demoExternal")]
        public class ExternalApiController : UmbracoApiController
        {
            [Umbraco.Web.WebApi.UmbracoAuthorize]
            [HttpGet]
            public string CallUmbraco()
            {
                WebClient wc = new WebClient();
                wc.Headers.Add("Authorization", "Basic " +
                    Convert.ToBase64String(
                    Encoding.ASCII.GetBytes("test:12345!")));
                string a = wc.DownloadString("http://localhost:52125/Umbraco/demo/DemoApi/test");
    
                return "Hello from external application! Here is the result: " + a;           
            }
    
        }
    }

    To make it work you must create a member with username "test" and password "12345!" in the umbraco application, which provides the web api.

    I hope this is helpful, if someone is looking for such a solution.
    Sören
  • Matt Taylor 873 posts 2086 karma points
    Jun 15, 2022 @ 16:11
    Matt Taylor
    0

    Thanks this helped me a lot.

    I uncommented

    //if (HttpContext.Current != null)
                    //    HttpContext.Current.User = principal;
    

    because I wanted access to the member later on.

  • Norbert Haberl 32 posts 115 karma points
    Jul 14, 2015 @ 10:19
    Norbert Haberl
    0

    Awesome ... do have any source code available maybe? I would like to change this sample to get an access token by user and afterwards send this token, instead of username and pw.

    Do you maybe know any examples of token based authentication this way?

    Thanks Norbert

  • Sören Deger 733 posts 2844 karma points c-trib
    Jul 14, 2015 @ 11:27
    Sören Deger
    0

    Hi Norbert,

    I have no source code for this. But there are a good Tutorial for this case from Warren Buckley:

    http://creativewebspecialist.co.uk/2015/01/06/securing-umbraco-web-apis-using-json-web-tokens/

    https://github.com/warrenbuckley/Umbraco-JWT-AuthTokens

    If you have questions related to this tutorial you can also ask him on twitter: @warrenbuckley

    Here is another general tutorial from microsoft about securing web apis with tokens: http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api

    A UmbracoApiController is inherited from the default MVC ApiController. You can do all the things with a UmbracoApiController like you do with a MVC ApiController.

    Hope this helps?

    Best regards,

    Sören

  • Norbert Haberl 32 posts 115 karma points
    Jul 14, 2015 @ 11:47
    Norbert Haberl
    0

    Yeah Sören, that helps! This was exactly the same article I have found too :-)

    He is focusing on backend users, so I thought that's not the right approach.

    But I will have a look on this, thanks Norbert

  • Sören Deger 733 posts 2844 karma points c-trib
    Jul 14, 2015 @ 12:07
    Sören Deger
    0

    Hi Norbert,

    please notice that the example from my code above use umbraco members. Members are the frontend users in a protected umbraco website. The other way is to use umbrao users. Users are the backend users in umbraco. Both is possible. You can also create a user or a member in umbraco only for your API. With the permissions you can denied access to backend/frontend. So you can easily manage your API users/members in umbraco. Of course, you can also go the independently third way with auth tokens.

    Best, Sören

  • Ryios 122 posts 263 karma points
    Jul 15, 2015 @ 22:07
    Ryios
    0

    Umbraco already has controllers routed to do authentication, you can just call those from your external app.

    http://somesite/umbraco/Surface/UmbLogin/{action}/{id} http://somesite/umbraco/Surface/UmbLoginStatus/{action}/{id} http://somesite/umbraco/Surface/UmbProfile/{action}/{id} http://somesite/umbraco/Surface/UmbRegister/{action}/{id}

    Those are for members,

    And for users (back office users)

    http://somesite/umbraco/backoffice/UmbracoApi/Authentication/{action}/{id}

    Each controller accepts models on their actions which can be serialized from JSON data.

    You shouldn't need to build anything to do this, you should just be able to call them with angular $http or jQuery.Ajax.

    Or if you want to do it server side on the external app, make yourself a Rest Client and call them server side.

    I figured this out because I have some code that will list out all the routes registered,

    @{
    Layout = "~/App_Plugins/TLCKB/Views/MainLayout.cshtml";
    var routes = System.Web.Routing.RouteTable.Routes;
    }
    <ul>
        @foreach (var route in routes)
        {
            var r = (Route)route;
            <li>@r.Url</li>
        }
    </ul>
    

    Once you know all the routes, also realize that a route like

    umbraco/backoffice/UmbracoApi/Authentication/{action}/{id}
    

    Maps to a controller with a class name of AuthenticationController (MVC assumes that AuthenticationController should route to Authentication).

    So if you grab .Net Reflector or IL Spy and add all the umbraco dll's you can search for "AuthenticationController" to see what Actions you can call and you can do the same for every umbraco controller in the routes.

    In our site, we did the opposite. We create a central login site in straight MVC and we authenticate Umbraco against that as well as other websites in our system using Windows Identity Foundation and PassiveRedirect.

  • Son Pham 31 posts 72 karma points
    Sep 30, 2015 @ 15:19
    Son Pham
    0

    Hi Ryios. Thanks for sharing. but can you provide a small code snippet how to call this api http://somesite/umbraco/backoffice/UmbracoApi/Authentication/{action}/{id}

    How does "header" params looks like ? Do we need to set cookie or token for next request ?

  • James Jackson-South 489 posts 1747 karma points c-trib
    Nov 04, 2016 @ 01:09
    James Jackson-South
    0

    Hey Ryios,

    Got any code samples for that?

Please Sign in or register to post replies

Write your reply to:

Draft