Search code examples
c#tokenidentityserver4

How to properly obtain a token in IDS4 fom a SPA in future-proof way?


I read in IDS4's tutorial that the following is a way to obtain a token.

var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

if (tokenResponse.IsError) { ... }
Console.WriteLine(tokenResponse.Json);

However, it's indicated in by the intellisense that it's about to be declared as obsolete so I checked out the docs for identity model password grant type suggesting the following.

var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
  Address = "https://localhost:44300/connect/token",
  ClientId = "spa",
  ClientSecret = "spa_secret",
  Scope = "MemberApi",
  UserName = user,
  Password = pass
});

I'm not certain what to do next in terms of producing a token. In the object I'm getting, there are AccessToken, IdentityToken, RefreshToken etc. and I'm confused as to which to rely on as the documentation on the differences between those is vague.

As the call goes now, I get an error saying that the client is unauthorized and the tokens are null. The client is declared as follows.

new Client
{
  ClientId = "spa",
  ClientSecrets = { new Secret("spa_secret".Sha256()) },
  ClientName = "SPA client",
  ClientUri = "http://identityserver.io",

  AllowedGrantTypes = GrantTypes.Implicit,
  AllowAccessTokensViaBrowser = true,

  RedirectUris = { "http://localhost:5000/security/credentials" },
  PostLogoutRedirectUris = { "http://localhost:5000/index.html" },
  AllowedCorsOrigins = { "http://localhost:5000", "https://localhost:44300" },

  AllowedScopes =
  {
    IdentityServerConstants.StandardScopes.OpenId,
    IdentityServerConstants.StandardScopes.Profile,
    IdentityServerConstants.StandardScopes.Email,
    "MemberApi",
    "MemberApi.full",
    "MemberApi.limited"
  }
}

Solution

  • It's confusing, as documentation needs to cover a lot of different situations. It's best you concentrate on the implicit flow, as that's a good candidate for a SPA application.

    There is a sample and documentation available from identityServer, take a look at JavaScriptClient.

    Next best thing to do is read the specs. That's the source. IdentityServer implements those specifications. In addition you can read the documentation of IdentityServer.

    That should help you to understand when and where to use the tokens. It depends on the type of client and the chosen flow. You can find information about that here.

    In short the meaning of the tokens:

    • Access Token: a JWT or reference token, used to access a resource. This token is also used to retrieve information from the UserInfo endpoint.
    • Identity Token: contains at least the sub claim, in order to identify the user.
    • Refresh Token: a token to allow a client to obtain a new access token on behalf of the user without requiring interaction with the user (offline access). Only available in certain flows.

    Depending on the flow the response can differ.

    For documentation about how to access a resource (api) with javascript using the access token please read this. When the access token expires you'll need a new token, without having to ask the user to login again. Since the refresh token isn't supported for the implicit flow you can implement a silent token renew as described here.

    About the obsolete code, TokenClient is replaced in favor of HttpClient. Instead of setting up a TokenClient you can now call the extension for the HttpClient. This is a code improvement and doesn't affect the implementation of the flows.