如何配置 ASP.Net Core 2.0 API 以使用 Azure AD 作为身份提供者
How to configure an ASP.Net Core 2.0 API to use Azure AD as Identity Provider
好的,所以我正在 VS2017 中为 ASP.Net Core 2.0 API 创建一个新项目。我已经设置了 Azure AD,并在向导中设置了一个新项目,我 select 更改身份验证和 schhose "Work or School accont" 然后输入我的 Azure AD 的名称(即 mycompany.onmicrosoft.com)。项目已创建,我可以在 Startup.cs
中看到添加此代码
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));
services.AddMvc();
}
我可以看到添加到 appSettings.json 文件的设置
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "mycompany.onmicrosoft.com",
"TenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
其中 TenentID 是 Azure AD 的目录 ID
ClientID 是新创建的 API 的 ApplicationID 值,因为它现在已在 Azure AD 中注册。
这一切都有道理。
但是如果我 运行 VS2017 中的项目然后导航到 https://localhost:44348/api/values 位置,我将收到 401 未授权。
我遗漏的那部分我必须在我的 Azure AD 应用程序注册中注册我的 https://localhost:44348 browser instance somehow in Azure in order to identify it as the approved client application for my testing. But I am not clear where to do that. Do I just register https://localhost:44348 我应该在应用程序注册中为 Anew API 生成一个密钥在 Azure AD 中进行项目并将该密钥作为秘密传递到我的身份验证 header 中?
如果我想使用 Postman 进行测试怎么办?我该怎么做?我是否必须以某种方式在 Azure AD 中注册邮递员?
我看过很多 Google 页面,有很多示例展示了如何从网页进行交互式登录,然后在 Azure AD 中注册该网页 sign-in url但不是简单地尝试从 VS2017 调试或 Postman 测试 API 时如何做到这一点。
我该怎么做?
编辑 - 阅读评论后,我创建了一个控制台应用程序并将其注册到我的 Azure AD 应用程序注册中并创建了一个密钥。我在这里为可能试图理解此进程服务器到服务器 OAUTH2 进程的任何其他人提供它。
归功于 this GitHub 存储库,以获取以下代码设计方面的帮助;
这是控制台应用程序代码
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Configuration;
using System.Globalization;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
namespace API.TestConsole
{
class Program
{
/// <summary>
/// The AAD Instance is the instance of Azure.
/// </summary>
/// <remarks>
/// Example: https://login.microsoftonline.com/{0}
/// </remarks>
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
/// <summary>
// The Tenant is the Directory ID of the Azure AD tenant in which this application is registered.
/// </summary>
private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
/// <summary>
/// The Client ID is used by this application to uniquely identify itself to Azure AD.
/// </summary>
/// <remarks>
/// This value is obtained when this application is registered in Azure AD
/// </remarks>
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
/// <summary>
// The App Key is a credential used by this application to authenticate to Azure AD.
/// </summary>
/// <remarks>
/// This value is generated when this application is registered in Azure AD and assigned a key
/// </remarks>
private static string appKey = ConfigurationManager.AppSettings["ida:AppKey"];
/// <summary>
// The Authority is the sign-in URL of the tenant.
/// </summary>
/// <remarks>
/// This is a string combination of the aadInstance and the tenant
/// </remarks>
static string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
/// <summary>
/// The ApplicationID of the evsApi service in Azure
/// </summary>
private static string apiResourceId = ConfigurationManager.AppSettings["api:ApiResourceId"];
/// <summary>
/// The base URL address of the Api service
/// </summary>
private static string apiBaseAddress = ConfigurationManager.AppSettings["api:ApiBaseAddress"];
private static HttpClient httpClient = new HttpClient();
private static AuthenticationContext authContext = null;
private static ClientCredential clientCredential = null;
static void Main(string[] args)
{
// As a test, call the test Api, values endpoint 10 times with a short delay between calls
authContext = new AuthenticationContext(authority);
clientCredential = new ClientCredential(clientId, appKey);
for (int i = 0; i < 10; i++)
{
Thread.Sleep(3000);
GetValues().Wait();
}
Console.WriteLine("Press ENTER to exit.");
Console.ReadLine();
}
static async Task GetValues()
{
// Get an access token from Azure AD using client credentials.
// If the attempt to get a token fails because the server is unavailable, retry twice after 3 seconds each.
AuthenticationResult result = null;
int retryCount = 0;
bool retry = false;
do
{
retry = false;
try
{
// ADAL includes an in memory cache, so this call will only send a message to the server if the cached token is expired.
result = await authContext.AcquireTokenAsync(apiResourceId, clientCredential);
}
catch (AdalException ex)
{
if (ex.ErrorCode == "temporarily_unavailable")
{
retry = true;
retryCount++;
Thread.Sleep(3000);
}
Console.WriteLine(
String.Format($"An error occurred while acquiring a token\nTime: " +
$"{DateTime.Now.ToString()}\nError: {ex.ToString()}\nRetry: {retry.ToString()}\n"));
}
} while ((retry == true) && (retryCount < 3));
if (result == null)
{
Console.WriteLine("Canceling attempt to contact the test API service.\n");
return;
}
// Add the access token to the authorization header of the request.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
// Call the values endpoint in the test API service. This is an HTTP GET.
Console.WriteLine("Retrieving values from Values endpoint at {0}", DateTime.Now.ToString());
HttpResponseMessage response = await httpClient.GetAsync(apiBaseAddress + "/api/values");
if (response.IsSuccessStatusCode)
{
// Read the response and output it to the console.
string s = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Values Result: {s}\n");
}
else
{
Console.WriteLine("Failed to retrieve Values\nError: {0}\n", response.ReasonPhrase);
}
}
}
}
这里是 App.Config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<appSettings>
<add key="ida:AADInstance" value="https://login.microsoftonline.com/{0}" />
<!-- This is the Directory ID value of the Azure AD -->
<add key="ida:Tenant" value="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
<!-- This is the Application ID value of this test console application as it is
registered in the Azure AD app registration in the portal directory -->
<add key="ida:ClientId" value="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
<!-- This is the Key value of this test console application, as it is
generated in the keys section for "Test Console Key" in the Azure AD app registration
for this test console application in the portal directory -->
<add key="ida:AppKey" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
<!-- This is the Application ID value of the test api application as it is
registered in the Azure AD app registration in the portal directory -->
<add key="api:apiResourceId" value="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
<!-- This is the custom domain URL assigned to the test app service in the Azure
portal -->
<add key="api:apiBaseAddress" value="https://testapi.mycompany.com" />
</appSettings>
</configuration>
简而言之,您需要一个访问令牌。
您如何获得访问令牌?通过像 OAuth Client Credentials 这样的身份验证流程:https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service.
或者您可能需要使用 OpenID Connect:https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-openid-connect-code。
客户端凭据将作为应用进行调用,而 OIDC(和其他一些流程)允许您作为用户调用 API。
除非您添加一些权限,否则您必须以用户身份调用:https://joonasw.net/view/defining-permissions-and-roles-in-aad
无论如何,您必须注册将调用 API 的应用程序,并授予它访问 API 的权限。
好的,所以我正在 VS2017 中为 ASP.Net Core 2.0 API 创建一个新项目。我已经设置了 Azure AD,并在向导中设置了一个新项目,我 select 更改身份验证和 schhose "Work or School accont" 然后输入我的 Azure AD 的名称(即 mycompany.onmicrosoft.com)。项目已创建,我可以在 Startup.cs
中看到添加此代码 public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));
services.AddMvc();
}
我可以看到添加到 appSettings.json 文件的设置
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "mycompany.onmicrosoft.com",
"TenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
其中 TenentID 是 Azure AD 的目录 ID ClientID 是新创建的 API 的 ApplicationID 值,因为它现在已在 Azure AD 中注册。
这一切都有道理。
但是如果我 运行 VS2017 中的项目然后导航到 https://localhost:44348/api/values 位置,我将收到 401 未授权。
我遗漏的那部分我必须在我的 Azure AD 应用程序注册中注册我的 https://localhost:44348 browser instance somehow in Azure in order to identify it as the approved client application for my testing. But I am not clear where to do that. Do I just register https://localhost:44348 我应该在应用程序注册中为 Anew API 生成一个密钥在 Azure AD 中进行项目并将该密钥作为秘密传递到我的身份验证 header 中?
如果我想使用 Postman 进行测试怎么办?我该怎么做?我是否必须以某种方式在 Azure AD 中注册邮递员?
我看过很多 Google 页面,有很多示例展示了如何从网页进行交互式登录,然后在 Azure AD 中注册该网页 sign-in url但不是简单地尝试从 VS2017 调试或 Postman 测试 API 时如何做到这一点。
我该怎么做?
编辑 - 阅读评论后,我创建了一个控制台应用程序并将其注册到我的 Azure AD 应用程序注册中并创建了一个密钥。我在这里为可能试图理解此进程服务器到服务器 OAUTH2 进程的任何其他人提供它。
归功于 this GitHub 存储库,以获取以下代码设计方面的帮助;
这是控制台应用程序代码
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Configuration;
using System.Globalization;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
namespace API.TestConsole
{
class Program
{
/// <summary>
/// The AAD Instance is the instance of Azure.
/// </summary>
/// <remarks>
/// Example: https://login.microsoftonline.com/{0}
/// </remarks>
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
/// <summary>
// The Tenant is the Directory ID of the Azure AD tenant in which this application is registered.
/// </summary>
private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
/// <summary>
/// The Client ID is used by this application to uniquely identify itself to Azure AD.
/// </summary>
/// <remarks>
/// This value is obtained when this application is registered in Azure AD
/// </remarks>
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
/// <summary>
// The App Key is a credential used by this application to authenticate to Azure AD.
/// </summary>
/// <remarks>
/// This value is generated when this application is registered in Azure AD and assigned a key
/// </remarks>
private static string appKey = ConfigurationManager.AppSettings["ida:AppKey"];
/// <summary>
// The Authority is the sign-in URL of the tenant.
/// </summary>
/// <remarks>
/// This is a string combination of the aadInstance and the tenant
/// </remarks>
static string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
/// <summary>
/// The ApplicationID of the evsApi service in Azure
/// </summary>
private static string apiResourceId = ConfigurationManager.AppSettings["api:ApiResourceId"];
/// <summary>
/// The base URL address of the Api service
/// </summary>
private static string apiBaseAddress = ConfigurationManager.AppSettings["api:ApiBaseAddress"];
private static HttpClient httpClient = new HttpClient();
private static AuthenticationContext authContext = null;
private static ClientCredential clientCredential = null;
static void Main(string[] args)
{
// As a test, call the test Api, values endpoint 10 times with a short delay between calls
authContext = new AuthenticationContext(authority);
clientCredential = new ClientCredential(clientId, appKey);
for (int i = 0; i < 10; i++)
{
Thread.Sleep(3000);
GetValues().Wait();
}
Console.WriteLine("Press ENTER to exit.");
Console.ReadLine();
}
static async Task GetValues()
{
// Get an access token from Azure AD using client credentials.
// If the attempt to get a token fails because the server is unavailable, retry twice after 3 seconds each.
AuthenticationResult result = null;
int retryCount = 0;
bool retry = false;
do
{
retry = false;
try
{
// ADAL includes an in memory cache, so this call will only send a message to the server if the cached token is expired.
result = await authContext.AcquireTokenAsync(apiResourceId, clientCredential);
}
catch (AdalException ex)
{
if (ex.ErrorCode == "temporarily_unavailable")
{
retry = true;
retryCount++;
Thread.Sleep(3000);
}
Console.WriteLine(
String.Format($"An error occurred while acquiring a token\nTime: " +
$"{DateTime.Now.ToString()}\nError: {ex.ToString()}\nRetry: {retry.ToString()}\n"));
}
} while ((retry == true) && (retryCount < 3));
if (result == null)
{
Console.WriteLine("Canceling attempt to contact the test API service.\n");
return;
}
// Add the access token to the authorization header of the request.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
// Call the values endpoint in the test API service. This is an HTTP GET.
Console.WriteLine("Retrieving values from Values endpoint at {0}", DateTime.Now.ToString());
HttpResponseMessage response = await httpClient.GetAsync(apiBaseAddress + "/api/values");
if (response.IsSuccessStatusCode)
{
// Read the response and output it to the console.
string s = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Values Result: {s}\n");
}
else
{
Console.WriteLine("Failed to retrieve Values\nError: {0}\n", response.ReasonPhrase);
}
}
}
}
这里是 App.Config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<appSettings>
<add key="ida:AADInstance" value="https://login.microsoftonline.com/{0}" />
<!-- This is the Directory ID value of the Azure AD -->
<add key="ida:Tenant" value="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
<!-- This is the Application ID value of this test console application as it is
registered in the Azure AD app registration in the portal directory -->
<add key="ida:ClientId" value="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
<!-- This is the Key value of this test console application, as it is
generated in the keys section for "Test Console Key" in the Azure AD app registration
for this test console application in the portal directory -->
<add key="ida:AppKey" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
<!-- This is the Application ID value of the test api application as it is
registered in the Azure AD app registration in the portal directory -->
<add key="api:apiResourceId" value="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
<!-- This is the custom domain URL assigned to the test app service in the Azure
portal -->
<add key="api:apiBaseAddress" value="https://testapi.mycompany.com" />
</appSettings>
</configuration>
简而言之,您需要一个访问令牌。
您如何获得访问令牌?通过像 OAuth Client Credentials 这样的身份验证流程:https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service.
或者您可能需要使用 OpenID Connect:https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-openid-connect-code。
客户端凭据将作为应用进行调用,而 OIDC(和其他一些流程)允许您作为用户调用 API。
除非您添加一些权限,否则您必须以用户身份调用:https://joonasw.net/view/defining-permissions-and-roles-in-aad
无论如何,您必须注册将调用 API 的应用程序,并授予它访问 API 的权限。