0

So once again I have tome left over to dive into Security. And I so badly want to understand how to make my AngularJS SPA to be able to call my API through a secure environment.

What I have a hard time grasping is the path of the security token I think.

So I have managed to secure my AngularJS SPA with OAuth2 through the help of one of Microsofts guides in the Azure Active Directory Developer guides.

GUIDE HERE

I can now Login and become authorized to enter the SPA, but when I make my calls to the API where the different API endpoints have an Authorize tag over them, I get Unauthorized, wish is understandable of course.

Which leads me to my next problem. First of all I probably don't have anything to handle the Authorization in my Web API, and also how do I make my API to authorize calls from my AngularJS SPA when its endpoints are being called on or made requests to.

What is the next step and what do I need to know about to both understand and implement the correct flow between/through the both applications.

______________________UPDATE____________________________

So I've come a bit further. but now I get a 401 unauthorized, so my Angular SPA cant get through the authorize tag. Am I missing something?

Javacript app.js:

var app = angular.module('app', ['AdalAngular']);
app.config(['$httpProvider', 'adalAuthenticationServiceProvider', function ($httpProvider, adalProvider) { //$routeProvider,

    $httpProvider.defaults.withCredentials = true;

    var endpoints = {
        "https://localhost:44376/": "http://oauthsolutionadtest.onmicrosoft.com/theapi"
    };

    adalProvider.init(
    {
        instance: 'https://login.microsoftonline.com/',
        tenant: 'oauthsolutionadtest.onmicrosoft.com',
        clientId: 'CLIENT-ID',
        endpoints: endpoints,
    },
        $httpProvider
    );

}]);

var sampleController = app.controller("sampleController", ["$scope", "$http", "adalAuthenticationService", function ($scope, $http, adalService) {

    $scope.login = login;
    $scope.logout = logout;
    $scope.onlyAdmin = onlyAdmin;

    function login(){
        adalService.login();
    };

    function logout(){
        adalService.logOut();
    };

    function onlyAdmin() {
        alert("INNE_1");

     $http.get("https://localhost:44376/api/testmessage")
        .success(
        function (data, status, headers, config, response) {
            alert("INNE_2");
            $scope.admin = true;
            console.log(data, status, headers, config, response);
        }).error(
            function (response) {
                alert("INNE_3 " + response);
                console.log(response);
            }
        )

    }


}])

HTML:

<!DOCTYPE html>
<html ng-app="app">
<head>
    <title>Sample SPA (AngularJs) And Azure AD</title>
    <meta charset="utf-8" />

    <link href="Content/bootstrap.min.css" rel="stylesheet" />
    <link href="Content/toaster.css" rel="stylesheet" />
    <link href="Content/loading-bar.min.css" rel="stylesheet" />

</head>
<body ng-controller="sampleController">
    <!--<div data-ng-view=""></div>-->

    <div>
        <button ng-click="login()" ng-hide="userInfo.isAuthenticated">Login</button>

        <button ng-click="logout()" ng-show="userInfo.isAuthenticated">Logout</button>

        <button ng-click="onlyAdmin()">
            Admin
        </button>

        {{admin}}

        <pre>
        {{userInfo}}
        </pre>
    </div>


    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>

    <script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.13/js/adal.min.js"></script>
    <script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.13/js/adal-angular.min.js"></script>
    <script type="text/javascript" src="App/app.js"></script>

</body>
</html>

WEBAPI Controller:

using System;
using System.Configuration;
using System.Linq;
using System.Security.Claims;
using System.Web.Http;
using System.Web.Http.Cors;


namespace OAuthSolution.API.Controllers
{
    public class ContactController : ApiController
    {


        [Route("api/testmessage")]
        [HttpGet]
        [EnableCors(origins: "https://localhost:44311", headers: "*", methods: "GET, POST, OPTIONS", SupportsCredentials =true)]
        [Authorize]
        public string testMessage()
        {

            return "You got the Test Message";
        }


        [Route("api/theGet")]
        [HttpGet]
        [EnableCors(origins: "https://localhost:44311", headers: "*", methods: "*", SupportsCredentials = true)]
        [Authorize]
        public IHttpActionResult Get()
        {
            var adminGroupId = ConfigurationManager.AppSettings["adminGroupId"];

            Claim groupAdmin = ClaimsPrincipal.Current.Claims.FirstOrDefault(x => x.Type == "groups" && x.Value.Equals(adminGroupId, StringComparison.CurrentCultureIgnoreCase));

            if(groupAdmin != null)
            {
                return Ok("Admin");
            }

            return Unauthorized();
        }
    }
}

WebAPI Startup.cs:

/////STARTUP.CS

using Microsoft.Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Owin;
using Microsoft.Owin.Security.ActiveDirectory;
using System.Configuration;
using System.Web.Http;
using Microsoft.Owin.Security.OAuth;

[assembly: OwinStartup(typeof(OAuthSolution.API.Startup))]

namespace OAuthSolution.API
{
    public partial class Startup
    {
        public void Configuration (IAppBuilder app)
        {
            ConfigureAuth(app);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

        }

        private void ConfigureAuth(IAppBuilder app)
        {
            var azureADBearerAuthOptions = new WindowsAzureActiveDirectoryBearerAuthenticationOptions
            {
                Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
            };

            azureADBearerAuthOptions.TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters()
            {
                ValidAudience = ConfigurationManager.AppSettings["ida:Audience"],
            };

            app.UseWindowsAzureActiveDirectoryBearerAuthentication(azureADBearerAuthOptions);

        }
    }
}

WebApiConfig.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Http.Cors;

namespace OAuthSolution.API
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {

            // Web API configuration and services
            config.EnableCors();

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

Web.Config: enter image description here

Header: enter image description here

The token decrypted from jwt.io:

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "Y4ueK2oaINQiQb5YEBSYVyDcpAU",
  "kid": "Y4ueK2oaINQiQb5YEBSYVyDcpAU"
}

{
  "aud": "http://oauthsolutionadtest.onmicrosoft.com/theapi",
  "iss": "https://sts.windows.net/f2f535e0-294f-4704-befc-8ce754f10bd7/",
  "iat": 1486134484,
  "nbf": 1486134484,
  "exp": 1486138384,
  "acr": "1",
  "amr": [
    "pwd"
  ],
  "appid": "a9d7f295-1c8e-43bc-9600-bdc0bff1d567",
  "appidacr": "0",
  "e_exp": 10800,
  "family_name": "admin",
  "given_name": "admin",
  "groups": [
    "f929d8fd-e361-473d-8325-6e8d1ccba5a0"
  ],
  "ipaddr": "90.224.252.71",
  "name": "admin",
  "oid": "251df8ba-112b-4c06-af7e-4c0899f0118b",
  "platf": "3",
  "scp": "user_impersonation",
  "sub": "Ih0hL_bmMPuMeYk3R_gEWZZmUteJfL0F1afFhiPYUFU",
  "tid": "f2f535e0-294f-4704-befc-8ce754f10bd7",
  "unique_name": "[email protected]",
  "upn": "[email protected]",
  "ver": "1.0"
}

14
  • Is your Angular app on the same domain as the API? What is your Audience config setting set to on the API? Commented Feb 3, 2017 at 13:20
  • I'll Post another update, i guess you want the web.config information? Thank you for your fast reply Juunas. :) Commented Feb 3, 2017 at 13:34
  • @juunas They are on the same computer (localHost) but they are registered to the same azure ad. if thats what you are asking. Also in the same solution if thats of any help. Commented Feb 3, 2017 at 15:21
  • Adal.js is probably sending the id token to the API because it is on same domain. I had this problem once before but seem to have lost the test project. Check the token over at jwt.io, you can confirm if the audience is correct or not. You can also try set ADAL logging higher: window.Logging.level = 3; and window.Logging.log = console.log;. Commented Feb 3, 2017 at 15:35
  • @juunas The Audience was correct reading from the decrypted token in jwt.to. But where do I set ADAL logging higher? Sorry for the dumb question but i really have no idea :P Commented Feb 3, 2017 at 15:48

1 Answer 1

0

I think i found the solution to my problem...

I had to add this line:

config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

And this namespace:

using Microsoft.Owin.Security.OAuth;

(also nuget for this to work: Microsoft.Owin.Host.SystemWeb)

to my WebApiConfig.cs file, inside the Register method.

Now i can call my API through CORS.

Hope this helps anyone else.

Also, Thanks for all the help!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.