使用刷新令牌获取新的访问令牌后,我想使用该访问令牌更新客户端Cookie。
我的客户端能够使用ajax登录并调用我的REST API,但是当该第一授权到期时,该API调用自然将不再起作用。
我有一个使用自己的REST API的.NET Web应用程序。该API是同一项目的一部分。它没有自己的启动配置。
在每个请求的标头中发送cookie时,它需要具有新的未过期的访问令牌,这样我才不会收到该请求的“用户未授权”。
现在,我可以使用刷新令牌来获取新令牌,但是cookie的值没有更改,因此我认为我需要在客户端发送任何请求之前更新cookie以反映新的访问令牌。 / p>
下面是我的混合客户端:
using IdentityModel.Client;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Net;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Cts.HomeService.Web.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var identityServerSection = (IdentityServerSectionHandler)System.Configuration.ConfigurationManager.GetSection("identityserversection");
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager()
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "localTestClient",
Authority = "http://localhost:5000",
RedirectUri = identityServerSection.Identity.RedirectUri,
Scope = "openid profile offline_access",
ResponseType = "code id_token",
RequireHttpsMetadata = false,
PostLogoutRedirectUri = identityServerSection.Identity.RedirectUri,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role",
},
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
var tokenClient = new TokenClient(
"http://localhost:5000/connect/token",
"localTestClient",
"");
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(
n.Code, n.RedirectUri);
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
// use the access token to retrieve claims from userinfo
var userInfoClient = new UserInfoClient(
"http://localhost:5000/connect/userinfo");
var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
// create new identity
var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(userInfoResponse.Claims);
id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", tokenResponse.IdentityToken));
id.AddClaim(new Claim("sid", n.AuthenticationTicket.Identity.FindFirst("sid").Value));
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
n.AuthenticationTicket.Properties);
},
RedirectToIdentityProvider = n =>
{
{
// so here I'll grab the access token
if (isAccessTokenExpired()) {
var cancellationToken = new CancellationToken();
var newAccessToken = context.GetNewAccessTokenAsync(refresh_token, null, cancellationToken);
// now what?
}
// if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
}
return Task.FromResult(0);
}
}
}
});
}
}
}
我研究了很多事情,但是cookie的价值始终保持不变。我已经考虑过删除旧的cookie,而只是手动构建新的cookie,但这需要以正确的方式对其进行加密,并且闻起来很有趣,肯定不是惯用的方式。
我觉得我必须缺少一些简单的东西。我希望可以使用一种简单的“ UpdateCookie(newToken)”方法,并且已经尝试使用SignIn()和SignOut(),但这些方法对我来说并没有奏效,实际上似乎根本没有与Cookie进行交互。
答案 0 :(得分:1)
这就是我的工作方式,添加以下几行:
initialize'
C:/Ruby25-x64/lib/ruby/gems/2.5.0/gems/selenium-webdriver-3.141.0/lib/selenium/webdriver/remote/response.rb:69:in
然后在AuthorizationCodeReceived中将其添加到末尾:
<?php
//Debug mode on
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Fetch session last typed data
session_start();
$_SESSION['name'] = (isset($_POST['name']) ? $_POST['name'] : '');
$_SESSION['phone'] = (isset($_POST['phone']) ? $_POST['phone'] : '');
$_SESSION['gpa'] = (isset($_POST['gpa']) ? $_POST['gpa'] : '');
$_SESSION['type'] = (isset($_POST['type']) ? $_POST['type'] : '');
//Define Errors
$name_err ='Veuillez entrer le nom';
$phone_err = 'Veuillez entrer le numéro de telephone';
$gpa_err = 'Veuillez entrer le gpa';
$type_err ='Veuillez entrer le type';
//Define Placeholders
$name_ph ='name';
$phone_ph = 'Numéro de téléphone';
$gpa_ph = 'Entrez le gpa';
$type_ph ='le type';
// Vars fetch data from form for email and form use
$name = (isset($_POST['name']) ? $_POST['name'] : '');
$phone = (isset($_POST['phone']) ? $_POST['phone'] : '');
$gpa = (isset($_POST['gpa']) ? $_POST['gpa'] : '');
$type = (isset($_POST['type']) ? $_POST['type'] : '');
// User ip Address
$ipadd = $_SERVER['REMOTE_ADDR'];
// Include db config file
require_once "db.php";
mysql_select_db(DB_NAME);
//Safe database record and telegram or whatsapp message
This is where I need you help :)
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<br>
<center><img src="/images/School-Logo.png" alt="school" style="width:123px;height:93px;"></center>
<link rel="icon" rel="shortcut icon" type="image/x-icon" href="/images/favicon.ico">
<title>Récuperation et envoi de donnes scolaires</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.css">
<link rel="stylesheet" href="/css/custom.css">
<style type="text/css">
body{ font: 14px sans-serif; }
.wrapper{ width: 350px; padding: 20px; }
</style>
</head>
<body>
<center>
<h1>School system</h1>
<h3>Collecting Storing data and sending notifications</h3>
<div class="wrapper">
<!–– Informations -->
<div class="studentform">
<b>
Note:
<br>
<ul>
<li>Entrez les informations</li>
<br>
</ul>
</b>
</div>
<br>
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" id="stud" method="post">
<div class="form-group <?php echo (!empty($name_err)) ? 'has-error' : ''; ?>">
<p align="left">Name <?php echo $name; ?> <font color="red">*</font> </p>
<input type="text" name="name" class="form-control" placeholder="<?php echo $name_ph;?>" value="<?php echo $_SESSION['name'];?>">
<span class="help-block"><?php echo $name_err; ?></span>
</div>
<div class="form-group <?php echo (!empty($phone_err)) ? 'has-error' : ''; ?>">
<p align="left">Contact <?php echo $phone; ?> <font color="red">*</font></p>
<input type="text" name="phone" class="form-control" placeholder="<?php echo $phone_ph;?>" value="<?php echo $_SESSION['phone'];?>" align="left" >
<span class="help-block"><?php echo $phone_err; ?></span>
</div>
<div class="form-group <?php echo (!empty($gpa_err)) ? 'has-error' : ''; ?>">
<p align="left">GPA <font color="red">*</font></p>
<input type="text" name="gpa" class="form-control" placeholder="<?php echo $gpa_ph;?>" value="<?php echo $_SESSION['gpa'];?>" align="left">
<span class="help-block"><?php echo $gpa_err; ?></span>
</div>
<div class="form-group <?php echo (!empty($type_err)) ? 'has-error' : ''; ?>">
<p align="left">C <font color="red">*</font></p>
<select name="type" form="stud" class="pickupselect" style="width:100%;">
<option value="bachelor">Bachelor</option>
<option value="master">Master</option>
</select>
<span class="help-block"><?php echo $type_err; ?></span>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Entrez les informations" align="left">
</div>
</form>
</div> </center>
</body>
<footer>
<center>school data 2019</center>
<br>
</footer>
</html>
newIdentity是您的声明身份,希望对您有所帮助。
答案 1 :(得分:0)
我最近遇到了同样的问题,解决方法是:
UseTokenLifetime = false
中设置OpenIdConnectAuthenticationOptions
用于配置OAuth中间件(否则,会话cookie的生存期将设置为访问令牌的生存期,通常为一小时)CookieAuthenticationProvider
来验证访问令牌的有效期IConfidentialClientApplication.AcquireTokenSilent()
方法调用)IIdentity
方法使用获取的访问令牌构建一个新的ISecurityTokenValidator.ValidateToken()
对象IAuthenticationManager.SignIn(properties, freshIdentity)
更新会话cookie 以下是使刷新令牌与OWIN cookie中间件一起使用的完整解决方案:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using EPiServer.Logging;
using Microsoft.Identity.Client;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Host.SystemWeb;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
namespace MyApp
{
public class OwinStartup
{
public void Configuration(IAppBuilder app)
{
var openIdConnectOptions = new OpenIdConnectAuthenticationOptions
{
UseTokenLifetime = false,
// ...
};
var msalAppBuilder = new MsalAppBuilder();
var refreshTokenHandler = new RefreshTokenHandler(msalAppBuilder, openIdConnectOptions);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieManager = new SystemWebChunkingCookieManager(),
Provider = new RefreshTokenCookieAuthenticationProvider(refreshTokenHandler)
});
}
}
public class RefreshTokenCookieAuthenticationProvider : CookieAuthenticationProvider
{
private readonly RefreshTokenHandler _refreshTokenHandler;
private static readonly ILogger _log = LogManager.GetLogger();
public RefreshTokenCookieAuthenticationProvider(RefreshTokenHandler refreshTokenHandler)
{
_refreshTokenHandler = refreshTokenHandler;
}
public override async Task ValidateIdentity(CookieValidateIdentityContext context)
{
var exp = context.Identity?.FindFirst("exp")?.Value;
if (string.IsNullOrEmpty(exp))
{
return;
}
var utcNow = DateTimeOffset.UtcNow;
var expiresUtc = DateTimeOffset.FromUnixTimeSeconds(long.Parse(exp));
var maxMinsBeforeExpires = TimeSpan.FromMinutes(2);
if (expiresUtc - utcNow >= maxMinsBeforeExpires)
{
return;
}
try
{
var freshIdentity = await _refreshTokenHandler.TryRefreshAccessTokenAsync(context.Identity);
if (freshIdentity != null)
{
context.ReplaceIdentity(freshIdentity);
context.OwinContext.Authentication.SignIn(context.Properties, (ClaimsIdentity) freshIdentity);
}
else
{
context.RejectIdentity();
}
}
catch (Exception ex)
{
_log.Error("Can't refresh user token", ex);
context.RejectIdentity();
}
}
}
public class RefreshTokenHandler
{
private readonly MsalAppBuilder _msalAppBuilder;
private readonly OpenIdConnectAuthenticationOptions _openIdConnectOptions;
public RefreshTokenHandler(
MsalAppBuilder msalAppBuilder,
OpenIdConnectAuthenticationOptions openIdConnectOptions)
{
_msalAppBuilder = msalAppBuilder;
_openIdConnectOptions = openIdConnectOptions;
}
public async Task<IIdentity> TryRefreshAccessTokenAsync(IIdentity identity, CancellationToken ct = default)
{
try
{
var idToken = await GetFreshIdTokenAsync(identity, ct);
var freshIdentity = await GetFreshIdentityAsync(idToken, ct);
return freshIdentity;
}
catch (MsalUiRequiredException)
{
return null;
}
}
private async Task<string> GetFreshIdTokenAsync(IIdentity identity, CancellationToken ct)
{
var principal = new ClaimsPrincipal(identity);
var app = _msalAppBuilder.BuildConfidentialClientApplication(principal);
var accounts = await app.GetAccountsAsync();
var result = await app.AcquireTokenSilent(new[] {"openid"}, accounts.FirstOrDefault()).ExecuteAsync(ct);
return result.IdToken;
}
private async Task<IIdentity> GetFreshIdentityAsync(string idToken, CancellationToken ct)
{
var validationParameters = await CreateTokenValidationParametersAsync(ct);
var principal = _openIdConnectOptions.SecurityTokenValidator.ValidateToken(idToken, validationParameters, out _);
var identity = (ClaimsIdentity) principal.Identity;
return identity;
}
// This is additional code for cases with multiple issuers - can be skipped if this configuration is static
private async Task<TokenValidationParameters> CreateTokenValidationParametersAsync(CancellationToken ct)
{
var validationParameters = _openIdConnectOptions.TokenValidationParameters.Clone();
var configuration = await _openIdConnectOptions.ConfigurationManager.GetConfigurationAsync(ct);
validationParameters.ValidIssuers = (validationParameters.ValidIssuers ?? new string[0])
.Union(new[] {configuration.Issuer})
.ToList();
validationParameters.IssuerSigningKeys = (validationParameters.IssuerSigningKeys ?? new SecurityKey[0])
.Union(configuration.SigningKeys)
.ToList();
return validationParameters;
}
}
// From official samples: https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi/blob/master/TaskWebApp/Utils/MsalAppBuilder.cs
public class MsalAppBuilder
{
public IConfidentialClientApplication BuildConfidentialClientApplication(ClaimsPrincipal currentUser)
{
// ...
}
}
}