title: Manually generate IdentityServer4 reference token and save to PersistedGrants table
body: <p>I have learnt <code>IdentityServer4</code> for a week and successfully implemented a simple authentication flow with <code>ResourceOwnedPassword</code> flow.</p>
  
  <p>Now, I'm implementing <strong>Google authentication with IdentityServer4</strong> by following <a href="https://www.blinkingcaret.com/2017/05/03/external-login-providers-in-asp-net-core/" rel="nofollow noreferrer">this tutorial</a></p>
  
  <p>This is what I am doing:</p>
  
  <p><strong>Startup.cs</strong></p>
  
  <pre><code>public void ConfigureServices(IServiceCollection services)
  {
      //...
  
      const string connectionString = @"Data Source=.\SQLEXPRESS;database=IdentityServer4.Quickstart.EntityFramework-2.0.0;trusted_connection=yes;";
      var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
  
      services.AddIdentityServer()
          .AddDeveloperSigningCredential()
          .AddProfileService&lt;IdentityServerProfileService&gt;()
          .AddResourceOwnerValidator&lt;IdentityResourceOwnerPasswordValidator&gt;()
          // this adds the config data from DB (clients, resources)
          .AddConfigurationStore(options =&gt;
          {
              options.ConfigureDbContext = builder =&gt;
              {
                  builder.UseSqlServer(connectionString,
                      sql =&gt; sql.MigrationsAssembly(migrationsAssembly));
              };
  
          })
          // this adds the operational data from DB (codes, tokens, consents)
          .AddOperationalStore(options =&gt;
          {
              options.ConfigureDbContext = builder =&gt;
                  builder.UseSqlServer(connectionString,
                      sql =&gt; sql.MigrationsAssembly(migrationsAssembly));
              // this enables automatic token cleanup. this is optional.
              options.EnableTokenCleanup = true;
              options.TokenCleanupInterval = 30;
          });
  
      // Add jwt validation.
      services.AddAuthentication(options =&gt;
          {
              options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
              options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
          })
          .AddIdentityServerAuthentication(options =&gt;
          {
              // base-address of your identityserver
              options.Authority = "https://localhost:44386";
  
              options.ClaimsIssuer = "https://localhost:44386";
  
              // name of the API resource
              options.ApiName = "api1";
              options.ApiSecret = "secret";
  
              options.RequireHttpsMetadata = false;
  
              options.SupportedTokens = SupportedTokens.Reference;
  
          });
  
      //...
  }
  </code></pre>
  
  <p>** Google controller (Which is for handling returned token from Google ** </p>
  
  <pre><code>public class GLoginController : Controller
      {
          #region Properties
  
          private readonly IPersistedGrantStore _persistedGrantStore;
  
          private readonly IUserFactory _userFactory;
  
          private readonly IBaseTimeService _baseTimeService;
  
          private readonly ITokenCreationService _tokenCreationService;
  
          private readonly IReferenceTokenStore _referenceTokenStore;
  
          private readonly IBaseEncryptionService _baseEncryptionService;
  
          #endregion
  
          #region Constructor
  
          public GLoginController(IPersistedGrantStore persistedGrantStore,
              IBaseTimeService basetimeService,
              ITokenCreationService tokenCreationService,
              IReferenceTokenStore referenceTokenStore,
              IBaseEncryptionService baseEncryptionService,
              IUserFactory userFactory)
          {
              _persistedGrantStore = persistedGrantStore;
              _baseTimeService = basetimeService;
              _userFactory = userFactory;
              _tokenCreationService = tokenCreationService;
              _referenceTokenStore = referenceTokenStore;
              _baseEncryptionService = baseEncryptionService;
          }
  
          #endregion
  
          #region Methods
  
          [HttpGet("login")]
          [AllowAnonymous]
          public IActionResult Login()
          {
              var authenticationProperties = new AuthenticationProperties
              {
                  RedirectUri = "/api/google/handle-external-login"
              };
  
              return Challenge(authenticationProperties, "Google");
          }
  
          [HttpGet("handle-external-login")]
          //[Authorize("ExternalCookie")]
          [AllowAnonymous]
          public async Task&lt;IActionResult&gt; HandleExternalLogin()
          {
              //Here we can retrieve the claims
              var authenticationResult = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
              var principal = authenticationResult.Principal;
  
              var emailAddress = principal.FindFirst(ClaimTypes.Email)?.Value;
              if (string.IsNullOrEmpty(emailAddress))
                  return NotFound(new ApiMessageViewModel("Email is not found"));
  
              // Find user by using username.
              var loadUserConditions = new LoadUserModel();
              loadUserConditions.Usernames = new HashSet&lt;string&gt; { emailAddress };
              loadUserConditions.Pagination = new PaginationValueObject(1, 1);
  
              // Find users asynchronously.
              var loadUsersResult = await _userFactory.FindUsersAsync(loadUserConditions);
              var user = loadUsersResult.FirstOrDefault();
  
              // User is not defined.
              if (user == null)
              {
                  user = new User(Guid.NewGuid(), emailAddress);
                  user.Email = emailAddress;
                  user.HashedPassword = _baseEncryptionService.Md5Hash("abcde12345-");
                  user.JoinedTime = _baseTimeService.DateTimeUtcToUnix(DateTime.UtcNow);
                  user.Kind = UserKinds.Google;
                  user.Status = UserStatuses.Active;
  
                  //await _userFactory.AddUserAsync(user);
              }
              else
              {
                  // User is not google account.
                  if (user.Kind != UserKinds.Google)
                      return Forbid("User is not allowed to access system.");
              }
  
              var token = new Token(IdentityServerConstants.TokenTypes.IdentityToken);
              var userCredential = new UserCredential(user);
  
              token.Claims = userCredential.GetClaims();
              token.AccessTokenType = AccessTokenType.Reference;
              token.ClientId = "ro.client";
              token.CreationTime = DateTime.UtcNow;
              token.Audiences = new[] {"api1"};
              token.Lifetime = 3600;
  
  
              return Ok();
          }
  
          #endregion
      }
  
  </code></pre>
  
  <p>Everything is fine, I can get claims returned from <strong>Google OAuth2</strong>, find users in database using Google email address and register them if they dont have any acounts.</p>
  
  <p>My question is: How can I use Google OAuth2 claims that I receive in <code>HandleExternalLogin</code> method to generate a <strong>Reference Token</strong>, save it to <strong>PersistedGrants</strong> table and return to client.</p>
  
  <p>This means when user access <code>https://localhost:44386/api/google/login</code>, after being redirected to Google consent screen, they can receive <code>access_token</code>, <code>refresh_token</code> that has been generated by <code>IdentityServer4</code>.</p>
  
  <p>Thank you,</p>