2

Working an application requires Refresh Token from OIDC (keycloak) to get authorisation for accessing resources. But it seems like the RefreshToken that returned seems to be expired or leaking.

The issue is that I'm able to log into the application and calls the RefreshToken and pass into my sync gateway method but the response is always 401 invalid.

Not sure how to debug further. Or is there a way I can try to refreshing the RefreshToken.

See code below. [startup.cs]

app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "Cookies",
            AutomaticAuthenticate = true,
            ExpireTimeSpan = TimeSpan.FromMinutes(60)
        });

        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

        var oidcOptions = new OpenIdConnectOptions
        {
            AuthenticationScheme = "oidc",
            SignInScheme = "Cookies",

            Authority = Configuration["keycloak:authority"],
            RequireHttpsMetadata = bool.Parse(Configuration["keycloak:httpMetadata"]),
            PostLogoutRedirectUri = Configuration["keycloak:logoutUri"],
            ClientId = Configuration["keycloak:clientId"],
            ClientSecret = Configuration["keycloak:clientSecret"],
            ResponseType = OpenIdConnectResponseType.Code,
            GetClaimsFromUserInfoEndpoint = true,
            SaveTokens = true,
            CallbackPath = "/signin-oidc",
        };

        oidcOptions.Scope.Clear();
        oidcOptions.Scope.Add("openid");
        app.UseOpenIdConnectAuthentication(oidcOptions);

Method calls the RefreshToken

 [HttpGet("getRec/{id}")]
    public async Task<object> GetFileById(string id)
    {

        var refreshToken = await HttpContext.Authentication.GetTokenAsync("refresh_token");
        //var authenticateInfo = await HttpContext.Authentication.GetAuthenticateInfoAsync("oidc");
        //var refreshToken = authenticateInfo.Properties.Items[".Token.refresh_token"];

        var token = HttpContext.Authentication.GetAuthenticateInfoAsync("refresh_token");
        var val = await AppBal.GetFileById(refreshToken, id);
        return val.Properties["files"];
    }
Alan Chang
  • 149
  • 12

1 Answers1

3

From my understanding there is no automatic way to set off a request token with OIDC... These people here have had a good crack at implementing utilizing a cookie validator: How to handle expired access token in asp.net core using refresh token with OpenId Connect

How to store the token received in AcquireTokenAsync with Active Directory

Essentially on all requests the tokens in the cookie can be validated. you can check for an expired access token, and in that case send a refresh request to keycloak... the access token will be provided and you can update the cookie with the updated access token and refresh token.

For keycloak I would utilize Davids response and instead point to Keycloak (as opposed to Azure:

public class KeycloakRefreshTokenService
{
    private const string HostUrl = "http://localhost:8080/";
    private const string Realm = "TestRealm";
    private string TokenUrl = $"/auth/realms/{Realm}/protocol/openid-connect/token";
    private const string ContentType = "application/x-www-form-urlencoded";

and

 public class KeycloakTokenResponse
{
    [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "token_type", Required = Required.Default)]
    public string TokenType { get; set; }
    [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "expires_in", Required = Required.Default)]
    public int ExpiresIn { get; set; }

    [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "refresh_expires_in", Required = Required.Default)]
    public int RefreshExpiresIn { get; set; }
    [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "not-before-policy", Required = Required.Default)]
    public string NotBeforePolicy { get; set; }
    [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "access_token", Required = Required.Default)]
    public string AccessToken { get; set; }
    [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "refresh_token", Required = Required.Default)]
    public string RefreshToken { get; set; }
    [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "id_token", Required = Required.Default)]
    public string IdToken { get; set; }
    [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "session_state", Required = Required.Default)]
    public string SessionState { get; set; }
}

Otherwise the code should work fine (you can check out the error response POCO

I would also note that when setting the expiry, the format used is incorrect (as the parser will not parse it after it has been refreshed once. So I would change the update of the token expiry to:

context.Properties.Items[".Token.expires_at"] =
                   DateTime.Now.AddSeconds(response.ExpiresIn).ToString("s", System.Globalization.CultureInfo.InvariantCulture);
CalDow
  • 120
  • 7