2015-09-02 26 views
12

我試用adal.js與從外部Web API站點(不同域)獲取數據的Angular SPA(單頁應用程序)網站。使用adal.js可以方便地對SPA進行身份驗證,但如果需要使用持證人令牌,則無法通過adal.js與API進行通信。除了無數的博客外,我還使用https://github.com/AzureAD/azure-activedirectory-library-for-js作爲模板。Adal.js沒有爲外部api端點資源獲取令牌

問題是,當我在啓動adal.js時設置端點時,adal.js似乎將所有輸出端點流量重定向到microsofts登錄服務。

觀察:

  • Adal.js會話存儲包含兩個adal.access.token.key條目。一個用於SPA Azure AD應用程序的客戶端ID,另一個用於外部API。只有SPA令牌有一個值。
  • 如果我不注入$ httpProvider到adal.js中,那麼調用將發送到外部API,並且返回401。
  • 如果我手動將SPA令牌添加到http頭(授權:承載'令牌值'),我得到一個401作爲回報。

我的理論是adal.js無法檢索端點的令牌(可能是因爲我配置了一些錯誤的SPA)和停止交通的終點,因爲它是無法得到所需的令牌。 SPA令牌不能用於API,因爲它不包含所需的權限。 爲什麼adal.js沒有獲取端點的令牌,我該如何解決?

其他信息:

  • 客戶端天青AD應用被配置爲使用委派針對API權限和oauth2AllowImplicitFlow =在應用清單真。
  • API Azure AD應用程序配置爲模擬,oauth2AllowImplicitFlow = true(不要認爲這是必需的,但嘗試過)。這是多租戶。
  • 該API配置爲允許所有CORS來源,並且在被其他Web應用程序使用模擬(混合MVC(Adal.net)+ Angular)使用時,它可以正常工作。

會話存儲:

key (for the SPA application): adal.access.token.keyxxxxx-b7ab-4d1c-8cc8-xxx value: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1u... 

key (for API application): adal.access.token.keyxxxxx-bae6-4760-b434-xxx 
value: 

app.js(角和阿達爾配置文件)

(function() { 
    'use strict'; 

    var app = angular.module('app', [ 
     // Angular modules 
     'ngRoute', 

     // Custom modules 

     // 3rd Party Modules 
     'AdalAngular' 

    ]); 

    app.config(['$routeProvider', '$locationProvider', 
     function ($routeProvider, $locationProvider) { 
     $routeProvider   

      // route for the home page 
      .when('/home', { 
       templateUrl: 'App/Features/Test1/home.html', 
       controller: 'home' 
      }) 

      // route for the about page 
      .when('/about', { 
       templateUrl: 'App/Features/Test2/about.html', 
       controller: 'about', 
       requireADLogin: true 
      }) 

      .otherwise({ 
       redirectTo: '/home' 
      }) 

     //$locationProvider.html5Mode(true).hashPrefix('!'); 

     }]); 

    app.config(['$httpProvider', 'adalAuthenticationServiceProvider', 
     function ($httpProvider, adalAuthenticationServiceProvider) { 
      // endpoint to resource mapping(optional) 
      var endpoints = { 
       "https://localhost/Api/": "xxx-bae6-4760-b434-xxx", 
      }; 

      adalAuthenticationServiceProvider.init(
        {       
         // Config to specify endpoints and similar for your app 
         clientId: "xxx-b7ab-4d1c-8cc8-xxx", // Required 
         //localLoginUrl: "/login", // optional 
         //redirectUri : "your site", optional 
         extraQueryParameter: 'domain_hint=mydomain.com', 
         endpoints: endpoints // If you need to send CORS api requests. 
        }, 
        $httpProvider // pass http provider to inject request interceptor to attach tokens 
        ); 
     }]); 
})(); 

調用端點角碼:

$scope.getItems = function() { 
      $http.get("https://localhost/Api/Items") 
       .then(function (response) {       
        $scope.items = response.Items; 
       }); 
+0

我想我可能有同樣的問題。你有沒有想到這一個? – Juha

+0

沒有。我會讓你知道如果我知道它 –

+0

開始在這一點上的賞金。我與你的情況完全相同,並嘗試過所有我能想到的事情,閱讀ADAL快速入門Gtithub網站上的所有信息,甚至購買了Vittorio的血腥書籍! 我只是不能相信,這樣一個簡單的情況根本不起作用! –

回答

0

我不是當然,如果我們的設置完全一樣,但我認爲它是可比的。

我有一個Angular SPA,通過Azure API管理(APIM)使用外部Web API。我的代碼可能不是最佳實踐,但它對我來說目前爲止:)

SPA Azure AD應用程序具有委派訪問外部API Azure AD應用程序的權限。

的SPA(基於所述Adal TodoList SPA sample

從todoListSvc.js app.js

adalProvider.init(
    { 
     instance: 'https://login.microsoftonline.com/', 
     tenant: 'mysecrettenant.onmicrosoft.com', 
     clientId: '********-****-****-****-**********',//ClientId of the Azure AD app for my SPA app    
     extraQueryParameter: 'nux=1', 
     cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost. 
    }, 
    $httpProvider 
    ); 

片段從

getWhoAmIBackend: function() { 
     return $http.get('/api/Employee/GetWhoAmIBackend'); 
    }, 

片段EmployeeController現在

public string GetWhoAmIBackend() 
    { 
     try 
     { 
      AuthenticationResult result = GetAuthenticated(); 

      HttpClient client = new HttpClient(); 
      client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken); 

      var request = new HttpRequestMessage() 
      { 
       RequestUri = new Uri(string.Format("{0}", "https://api.mydomain.com/secretapi/api/Employees/GetWhoAmI")), 
       Method = HttpMethod.Get, //This is the URL to my APIM endpoint, but you should be able to use a direct link to your external API 

      }; 
      request.Headers.Add("Ocp-Apim-Trace", "true"); //Not needed if you don't use APIM 
      request.Headers.Add("Ocp-Apim-Subscription-Key", "******mysecret subscriptionkey****"); //Not needed if you don't use APIM 

      var response = client.SendAsync(request).Result; 
      if (response.IsSuccessStatusCode) 
      { 
       var res = response.Content.ReadAsStringAsync().Result; 
       return res; 
      } 
      return "No dice :("; 
     } 
     catch (Exception e) 
     { 
      if (e.InnerException != null) 
       throw e.InnerException; 
      throw e; 
     } 
    } 

     private static AuthenticationResult GetAuthenticated() 
    { 
     BootstrapContext bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as BootstrapContext; 
     var token = bootstrapContext.Token; 

     Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext = 
      new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.microsoftonline.com/mysecrettenant.onmicrosoft.com"); 

     //The Client here is the SPA in Azure AD. The first param is the ClientId and the second is a key created in the Azure Portal for the AD App 
     ClientCredential credential = new ClientCredential("clientid****-****", "secretkey ********-****"); 

     //Get username from Claims 
     string userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null ? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value : ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value; 

     //Creating UserAssertion used for the "On-Behalf-Of" flow 
     UserAssertion userAssertion = new UserAssertion(bootstrapContext.Token, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName); 

     //Getting the token to talk to the external API 
     var result = authContext.AcquireToken("https://mysecrettenant.onmicrosoft.com/backendAPI", credential, userAssertion); 
     return result; 
    } 

,在我的後端外部API,我Startup.Auth.cs看起來是這樣的:

外部API Startup.Auth.cs

 public void ConfigureAuth(IAppBuilder app) 
    { 
     app.UseWindowsAzureActiveDirectoryBearerAuthentication(
      new WindowsAzureActiveDirectoryBearerAuthenticationOptions 
      { 
       Tenant = ConfigurationManager.AppSettings["ida:Tenant"], 
       TokenValidationParameters = new TokenValidationParameters 
       { 
        ValidAudience = ConfigurationManager.AppSettings["ida:Audience"], 
        SaveSigninToken = true 
       }, 
       AuthenticationType = "OAuth2Bearer" 
      }); 
    } 

請讓我知道這是否有幫助,或者如果我可以得到進一步的幫助。

+0

EmployeeController是否與Angular SPA位於同一臺計算機上?即都在同一個MVC項目中? –

+0

是的,對不起,沒有澄清。 我有一個SPA的解決方案,它包含前端JavaScript以及EmployeeController。 現在,EmployeeController用於調用實際邏輯所在的實際後端Web API。 – Jonas

+0

我明白了。所以你一直無法讓adal.js直接對外部API使用模擬。 EmployeeController是作爲解決此限制的解決方法而引入的?當我們啓動這個項目時(它被推遲了),我們可能會得到類似的解決方案:) :) –

0

您需要讓Web API知道您的客戶端應用程序。僅從您的客戶端向API添加委託權限是不夠的。

要使API客戶端感知,請轉到Azure管理門戶,下載API的清單並將Client應用程序的ClientID添加到「knownClientApplications」列表中。將清單上傳回API應用程序。

+0

這在使用adal.net時不是必需的,因爲我有可以使用adal.net和模擬API的工作解決方案。這是adal.js的特殊要求嗎?我也試過這個,但它沒有工作:( –

+0

@BjørnF.Rasmussen我試着在這個GitHub線程中提出類似的問題,並且我得到了一些反饋,我試過了,但它對我沒有幫助。爲你的作品:https://github.com/Azure-Samples/active-directory-dotnet-webapi-multitenant-windows-store/issues/7#issuecomment-188923994 –

+0

我已經是配置感謝您試用:) –

1

ADAL.js確實從id_token獲取了不同的access_token,用於調用在不同域上運行的Azure AD受保護的API。 最初,在登錄時,它只需要id_token。該令牌具有訪問相同域的資源的訪問權限。 但是,在調用在不同域中運行的API時,adal攔截器會檢查adal.init()中是否將API URL配置爲端點。

只有這樣纔會爲請求的資源調用訪問令牌。它還需要在AAD中配置SPA才能訪問API應用程序。

實現這一目標的關鍵是以下: 1. adal.init添加端點()

var endpoints = { 

    // Map the location of a request to an API to a the identifier of the associated resource 
    //"Enter the root location of your API app here, e.g. https://contosotogo.azurewebsites.net/": 
    // "Enter the App ID URI of your API app here, e.g. https://contoso.onmicrosoft.com/TestAPI", 
    "https://api.powerbi.com": "https://analysis.windows.net/powerbi/api", 
    "https://localhost:44300/": "https://testpowerbirm.onmicrosoft.com/PowerBICustomServiceAPIApp" 
}; 

adalProvider.init(
    { 
     instance: 'https://login.microsoftonline.com/', 
     tenant: 'common', 
     clientId: '2313d50b-7ce9-4c0e-a142-ce751a295175', 
     extraQueryParameter: 'nux=1', 
     endpoints: endpoints, 
     requireADLogin: true, 

     //cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost. 
     // Also, token acquisition for the To Go API will fail in IE when running on localhost, due to IE security restrictions. 
    }, 
    $httpProvider 
    ); 
  • 給權限在天青AD到SPA應用訪問API的應用程序: enter image description here
  • 你可以參考以下鏈接瞭解詳細信息:ADAL.js deep dive

    +0

    了。 Thx爲您的反饋。這似乎與我嘗試的一樣,但也許它在更新版本的adal.js中工作。我打算在明年開始時再次嘗試 –

    +0

    有人可以更詳細地解釋App ID URI部分嗎? 我已經得到了SPA應用程序的工作和驗證,但是當我嘗試驗證到web api時,它不起作用。當我查看來自我的angular 2應用程序的http標題時,我看不到任何具有驗證的標題信息。這使我相信我沒有給它正確的端點信息。如果我的根位置是https://blabla.azurewebsites.net/ 可我的應用程序ID URI也https://blabla.azurewebsites.net/ ? –

    1

    好吧,我一直在靠牆撞我的頭,以解決這個問題。試圖讓我的ADAL.js SPA應用程序(無角)成功地將跨域XHR請求發送到我珍貴的支持CORS的Web API。

    This sample app,像我這樣的所有新手都在使用這個問題:它具有API和SPA都來自同一個域 - 只需要一個AD Tenant應用註冊。這隻會讓事情變得混亂,因爲它需要將事情分解成單獨的部分。

    所以,開箱即用,樣品有這個Startup.Auth.cs該工程確定,至於樣品去... ...

    public void ConfigureAuth(IAppBuilder app) { 
    
         app.UseWindowsAzureActiveDirectoryBearerAuthentication(
          new WindowsAzureActiveDirectoryBearerAuthenticationOptions 
          { 
           Audience = ConfigurationManager.AppSettings["ida:Audience"], 
           Tenant = ConfigurationManager.AppSettings["ida:Tenant"], 
          }); 
        } 
    

    但是,你需要修改上面的代碼,刪除Audience分配,然後參加一系列的觀衆..是的:ValidAudiences ..因此,對於每個與您的WebAPI交談的SPA客戶,您都希望將您的SPA註冊的ClientID置於此陣列中......

    它應該看起來像這樣...

    public void ConfigureAuth(IAppBuilder app) 
    { 
        app.UseWindowsAzureActiveDirectoryBearerAuthentication(
         new WindowsAzureActiveDirectoryBearerAuthenticationOptions 
         { 
          Tenant = ConfigurationManager.AppSettings["ida:Tenant"], 
          TokenValidationParameters = new TokenValidationParameters 
          { 
           ValidAudiences = new [] { 
           ConfigurationManager.AppSettings["ida:Audience"],//my swagger SPA needs this 1st one 
           "b2d89382-f4d9-42b6-978b-fabbc8890276",//SPA ClientID 1 
           "e5f9a1d8-0b4b-419c-b7d4-fc5df096d721" //SPA ClientID 2 
           }, 
           RoleClaimType = "roles" //Req'd only if you're doing RBAC 
                 //i.e. web api manifest has "appRoles" 
          } 
         }); 
    } 
    

    編輯

    好吧,基於@ JonathanRupp的反饋,我能扭轉了我是用如上圖所示,在Web API的解決方案,並能修改我的客戶端的JavaScript如下圖所示,使一切正常。

    // Acquire Token for Backend 
        authContext.acquireToken("https://mycorp.net/WebApi.MyCorp.RsrcID_01", function (error, token) { 
    
         // Handle ADAL Error 
         if (error || !token) { 
          printErrorMessage('ADAL Error Occurred: ' + error); 
          return; 
         } 
    
         // Get TodoList Data 
         $.ajax({ 
          type: "GET", 
          crossDomain: true, 
          headers: { 
           'Authorization': 'Bearer ' + token 
          }, 
          url: "https://api.mycorp.net/odata/ToDoItems", 
         }).done(function (data) { 
          // For Each Todo Item Returned, do something 
          var output = data.value.reduce(function (rows, todoItem, index, todos) { 
           //omitted 
          }, ''); 
    
          // Update the UI 
          //omitted 
    
         }).fail(function() { 
          //do something with error 
         }).always(function() { 
          //final UI cleanup 
         }); 
        }); 
    
    +0

    很高興你的事情按照你想要的方式工作,但這通常不是你想要設置的方式。通常,您想使用ADAL.js將用戶登錄到SPA(聽起來您已經工作了),然後讓SPA向ADAL.js請求訪問令牌以調用WebAPI。這聽起來像是你正在SPA調用WebAPI和SPA的應用程序的訪問令牌(WebAPI接受因爲你在這裏列出的代碼),而不是使用它來獲得這個應用程序的訪問令牌。 –

    相關問題