I've recently setup IdentityServer v3 and its running like a dream, however I'm having troubles with the OWIN middleware.
I would like to use the hybrid flow so I can refresh tokens in the backend without the user having to redirect back to the IdentityServer to get a new access token every 5 minutes (which is also odd as its set to have a lifetime of 1 hour on the server).
I'm using the following config in startup and I'm getting the tokens fine, but it never seems to try and refresh the access token once it's expired. Do I need some custom logic somewhere to refresh my tokens?
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
ClientSecret = clientSecret, //Not sure what this does?
Authority = "https://auth.example.com",
RedirectUri = "http://website.example.com",
PostLogoutRedirectUri = "http://website.example.com",
ResponseType = "code id_token token",
Scope = "openid profile email write read offline_access",
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// filter "protocol" claims
var claims = new List<Claim>(from c in n.AuthenticationTicket.Identity.Claims
where c.Type != "iss" &&
c.Type != "aud" &&
c.Type != "nbf" &&
c.Type != "exp" &&
c.Type != "iat" &&
c.Type != "nonce" &&
c.Type != "c_hash" &&
c.Type != "at_hash"
select c);
// get userinfo data
var userInfoClient = new UserInfoClient(
new Uri(n.Options.Authority + "/connect/userinfo"),
n.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
userInfo.Claims.ToList().ForEach(ui => claims.Add(new Claim(ui.Item1, ui.Item2)));
// get access and refresh token
var tokenClient = new OAuth2Client(
new Uri(n.Options.Authority + "/connect/token"),
clientId,
clientSecret);
var response = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri);
claims.Add(new Claim("access_token", response.AccessToken));
claims.Add(new Claim("expires_at", DateTime.UtcNow.AddSeconds(response.ExpiresIn).ToLocalTime().ToString(CultureInfo.InvariantCulture)));
claims.Add(new Claim("refresh_token", response.RefreshToken));
claims.Add(new Claim("id_token", n.ProtocolMessage.IdToken));
//Does this help?
n.AuthenticationTicket.Properties.AllowRefresh = true;
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(
claims.Distinct(new ClaimComparer()),
n.AuthenticationTicket.Identity.AuthenticationType),
n.AuthenticationTicket.Properties);
},
RedirectToIdentityProvider = async n =>
{
// if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var id = n.OwinContext.Authentication.User.FindFirst("id_token");
if (id != null)
{
var idTokenHint = id.Value;
n.ProtocolMessage.IdTokenHint = idTokenHint;
}
}
}
}
});
I'm also using the following in my ApiClient (RestSharp) which talks to my resource api
public class MyTokenAuthenticator : IAuthenticator
{
public void Authenticate(IRestClient client, IRestRequest request)
{
var tokenClaim = ClaimsPrincipal.Current.Claims.FirstOrDefault(c => c.Type.Equals("access_token"));
if (tokenClaim != null && !String.IsNullOrWhiteSpace(tokenClaim.Value))
request.AddHeader("Authorization", String.Format("Bearer {0}", tokenClaim.Value));
}
}