gpt4 book ai didi

c# - 使用 Azure AD 登录后无法使用 WebApp 静默获取 token ?

转载 作者:行者123 更新时间:2023-12-03 01:41:40 26 4
gpt4 key购买 nike

我将 WebApp 和 WebAPI 注册到同一个 Azure AD 中。

我正在尝试从 WebApp 调用 WebAPI。

我已将服务 WebAPI 添加到我的 Azure AD Applcaition 中的 WebApp 中。像下面这样- enter image description here

当我运行 WebAPI 时,登录成功后它会显示登录屏幕,我可以访问 WebAPI 方法。这是正常行为。

当我运行 WebApp 时,它将执行相同的登录屏幕,登录成功后我可以看到 WebApp。

现在我想从 WebApp 调用 WebAPI 方法,但我不需要 WebAPI 的登录屏幕,因为当我运行 WebApp 时,我将获得登录屏幕登录后,我希望通过使用相同的用户,我应该能够访问 WebAPI,而无需再次执行登录操作,因为我拥有适用于两者的 token WebApp 和 WebAPI。

WebAPI 代码 -

启动.Auth.cs

public partial class Startup
{
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
private static string aadInstance = EnsureTrailingSlash(ConfigurationManager.AppSettings["ida:AADInstance"]);
private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];

public static readonly string Authority = aadInstance + tenantId;

// This is the resource ID of the AAD Graph API. We'll need this to request a token to call the Graph API.
string graphResourceId = "https://graph.windows.net";

public void ConfigureAuth(IAppBuilder app)
{
ApplicationDbContext db = new ApplicationDbContext();

app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

app.UseCookieAuthentication(new CookieAuthenticationOptions());

app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,

Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId).Result;

return Task.FromResult(0);
}
}
});
}

private static string EnsureTrailingSlash(string value)
{
if (value == null)
{
value = string.Empty;
}

if (!value.EndsWith("/", StringComparison.Ordinal))
{
return value + "/";
}

return value;
}
}

TestController.cs

[Authorize]
public class TestController : ApiController
{
[HttpGet]
[Route("api/getdata")]
public IEnumerable<string> GetData()
{
return new string[] { "value1", "value2" };
}
}

Web 应用程序代码 -

Startup.Auth.cs

public partial class Startup
{
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
private static string aadInstance = EnsureTrailingSlash(ConfigurationManager.AppSettings["ida:AADInstance"]);
private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];

public static readonly string Authority = aadInstance + tenantId;

// This is the resource ID of the AAD Graph API. We'll need this to request a token to call the Graph API.
string graphResourceId = "https://graph.windows.net";

public void ConfigureAuth(IAppBuilder app)
{
ApplicationDbContext db = new ApplicationDbContext();

app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

app.UseCookieAuthentication(new CookieAuthenticationOptions());

app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,

Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId).Result;

return Task.FromResult(0);
}
}
});
}

private static string EnsureTrailingSlash(string value)
{
if (value == null)
{
value = string.Empty;
}

if (!value.EndsWith("/", StringComparison.Ordinal))
{
return value + "/";
}

return value;
}
}

HomeController.cs

[Authorize]
public class HomeController : Controller
{
private static string clientIdWebApp = ConfigurationManager.AppSettings["ida:clientIdWebApp"];
private static string clientIdWebApi = ConfigurationManager.AppSettings["ida:clientIdWebApi"];
private static string clientSecretWebApp = ConfigurationManager.AppSettings["ida:clientSecretWebApp"];
private static string aadInstance = (ConfigurationManager.AppSettings["ida:AADInstance"]);
private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static string PostLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
Uri redirectUri = new Uri(PostLogoutRedirectUri);
public static readonly string Authority = aadInstance + tenantId;

public ActionResult Index()
{
return View();
}

public async System.Threading.Tasks.Task<ActionResult> About()
{
ViewBag.Message = "Your application description page.";
try
{
AuthenticationResult result = null;

string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Startup.Authority, new ADALTokenCache(userObjectID));
ClientCredential credential = new ClientCredential(clientIdWebApp, clientSecretWebApp);
//AcquireTokenSilentAsync should have to work as i'm accessing WebAPI using same user I logged in to WebApp
result = authContext.AcquireTokenSilentAsync(clientIdWebApi,credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId)).Result;
// gettign exception {"Failed to acquire token silently as no token was found in the cache. Call method AcquireToken"} but I got match id into cache.
// and if use AcquireToken instead then it works but api response is login html //page instead of api output
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://MYWEBAPI/api/getdata");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);

// Return the user's profile in the view.
if (response.IsSuccessStatusCode)
{
string responseString = await response.Content.ReadAsStringAsync();
}
}
catch (AdalException ex)
{
}
return View();
}
}

AdalTokenCache.cs - WebApp 和 WebAPI 相同

 public class ADALTokenCache : TokenCache
{
private ApplicationDbContext db = new ApplicationDbContext();
private string userId;
private UserTokenCache Cache;

public ADALTokenCache(string signedInUserId)
{
// associate the cache to the current user of the web app
userId = signedInUserId;
this.AfterAccess = AfterAccessNotification;
this.BeforeAccess = BeforeAccessNotification;
this.BeforeWrite = BeforeWriteNotification;
// look up the entry in the database
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
// place the entry in memory
this.Deserialize((Cache == null) ? null : MachineKey.Unprotect(Cache.cacheBits,"ADALCache"));
}

// clean up the database
public override void Clear()
{
base.Clear();
var cacheEntry = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
db.UserTokenCacheList.Remove(cacheEntry);
db.SaveChanges();
}

// Notification raised before ADAL accesses the cache.
// This is your chance to update the in-memory copy from the DB, if the in-memory version is stale
void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
if (Cache == null)
{
// first time access
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
}
else
{
// retrieve last write from the DB
var status = from e in db.UserTokenCacheList
where (e.webUserUniqueId == userId)
select new
{
LastWrite = e.LastWrite
};

// if the in-memory copy is older than the persistent copy
if (status.First().LastWrite > Cache.LastWrite)
{
// read from from storage, update in-memory copy
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
}
}
this.Deserialize((Cache == null) ? null : MachineKey.Unprotect(Cache.cacheBits, "ADALCache"));
}

// Notification raised after ADAL accessed the cache.
// If the HasStateChanged flag is set, ADAL changed the content of the cache
void AfterAccessNotification(TokenCacheNotificationArgs args)
{
// if state changed
if (this.HasStateChanged)
{
if (Cache == null)
{
Cache = new UserTokenCache
{
webUserUniqueId = userId
};
}

Cache.cacheBits = MachineKey.Protect(this.Serialize(), "ADALCache");
Cache.LastWrite = DateTime.Now;

// update the DB and the lastwrite
db.Entry(Cache).State = Cache.UserTokenCacheId == 0 ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
this.HasStateChanged = false;
}
}

void BeforeWriteNotification(TokenCacheNotificationArgs args)
{
// if you want to ensure that no concurrent write take place, use this notification to place a lock on the entry
}

public override void DeleteItem(TokenCacheItem item)
{
base.DeleteItem(item);
}
}

最重要的是,我发现 webapi 也有 AccountController ,其登录代码与 webapp 相同。这种情况应该怎么办?

 public class AccountController : BaseMvcController
{
public void SignIn()
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}

public void SignOut()
{
string callbackUrl = Url.Action("SignOutCallback", "Account", routeValues: null, protocol: Request.Url.Scheme);

HttpContext.GetOwinContext().Authentication.SignOut(
new AuthenticationProperties { RedirectUri = callbackUrl },
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
}

public ActionResult SignOutCallback()
{
if (Request.IsAuthenticated)
{
// Redirect to home page if the user is authenticated.
return RedirectToAction("Index", "Home");
}

return View();
}
}

最佳答案

AcquireTokenSilentAsync 方法可以为您提供帮助,前提是您的应用程序之前已获取目标资源(在您的情况下是后端 Web API)的有效 token 至少一次,并且已缓存该 token 以供后续使用使用。

您可能会收到此错误,因为您甚至没有真正向 Web API 进行过身份验证(即获取了 Web API 的有效 token 并传递了一次),因此缓存中没有任何内容可供使用。

简单地说,您将无法使用 AcquireTokenSilentAsync 进行首次身份验证。

为了进一步理解,请查看您在问题本身中共享的 GitHub 示例。 Secure a backend web API

  • 示例代码首先使用授权代码流获取 Web API 的有效 token 。

  • 仅当第一个有效 token 存在时,它才会被缓存,并且后续调用可以通过 authContext.AcquireTokenSilentAsync 来处理。这也作为示例文档的一部分明确说明。

  • resourceID. The App ID URI of the web API, which you created when you registered the web API in Azure AD

  • tokenCache. An object that caches the access tokens. See Token caching.

If AcquireTokenByAuthorizationCodeAsync succeeds, ADAL caches the token. Later, you can get the token from the cache by calling AcquireTokenSilentAsync

示例代码

  1. 首次使用授权代码流程获取有效 token

    //OpenID Connect 中间件在获取授权码时发送此事件。

    public override async Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
    {
    string authorizationCode = context.ProtocolMessage.Code;
    string authority = "https://login.microsoftonline.com/" + tenantID
    string resourceID = "https://tailspin.onmicrosoft.com/surveys.webapi" // App ID URI
    ClientCredential credential = new ClientCredential(clientId, clientSecret);

    AuthenticationContext authContext = new AuthenticationContext(authority, tokenCache);
    AuthenticationResult authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(
    authorizationCode, new Uri(redirectUri), credential, resourceID);

    // If successful, the token is in authResult.AccessToken
    }
  2. 稍后,您可以通过调用 AcquireTokenSilentAsync 从缓存中获取 token :

    AuthenticationContext authContext = new AuthenticationContext(authority, tokenCache);
    var result = await authContext.AcquireTokenSilentAsync(resourceID, credential, new UserIdentifier(userId, UserIdentifierType.UniqueId));

关于c# - 使用 Azure AD 登录后无法使用 WebApp 静默获取 token ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52745656/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com