title: UserID sub-claim generated, but disappears from Identity Server 4 tokens, causing UserManager.GetUser to fail
body: <p>I am building a React SPA with Identity Server 4 and dotnetcore 3.1. The Identity Server client is defined in <code>appsettings.json</code> using the <code>IdentityServerSPA</code> profile. Using <code>oidc-client</code> on the frontend, I can login successfully. Inspecting the <code>oidc</code> object, I can confirm that:</p>
  <ul>
  <li>token_type: <code>Bearer</code></li>
  <li>scope: <code>openid profile OpenWorkShopAPI</code></li>
  <li>profile.sub: (my user ID)</li>
  <li>profile.name: (my username)</li>
  </ul>
  <p>If I subsequently try to call the API (either with <code>Authorization: Bearer</code> as the <code>id_token</code> or the <code>access_token</code>), there is no <code>sub</code> claim present. Therefore, <code>_userManager.GetUserAsync(User);</code> fails.</p>
  <p>Enumerating and printing the claims, I see:</p>
  <pre><code>Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;nbf&quot; &quot;1601597732&quot;
  Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;exp&quot; &quot;1601598032&quot;
  Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;iss&quot; &quot;http://dev.openwork.shop:5000&quot;
  Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;aud&quot; &quot;OpenWorkShopAPI&quot;
  Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;iat&quot; &quot;1601597732&quot;
  Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;at_hash&quot; &quot;MoAqNfND0ct1mUFKpUtgcg&quot;
  Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;s_hash&quot; &quot;D81HZF_ii2r0i5-4_ZxnLA&quot;
  Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;sid&quot; &quot;cdMH0nFKdikldL1Gy3S3Eg&quot;
  Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier&quot; &quot;fdf0023d-7ae9-4aaf-87fe-0f320b869171&quot;
  Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;auth_time&quot; &quot;1601582136&quot;
  Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;http://schemas.microsoft.com/identity/claims/identityprovider&quot; &quot;local&quot;
  Claim: &quot;System.Security.Claims.ClaimsIdentity&quot; &quot;http://schemas.microsoft.com/claims/authnmethodsreferences&quot; &quot;pwd&quot;
  </code></pre>
  <p>Based upon this, I  can work around this by accessing the name claim to find the ID directly:</p>
  <pre><code>string CurrentUserId =&gt; User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
  </code></pre>
  <p>But that seems neither correct nor ideal.</p>
  <p>I have noticed that all of my MySQL tables (except <code>AspNetUsers</code>) remain empty. There are no values in <code>AspNetUserClaims</code>, for example. Yet, as I showed above, the <code>oidc-client</code> is able to successfully see the <code>sub</code> claim for the user ID.</p>
  <p>Since I am using the <code>DefaultIdentity</code> and <code>AddApiAuthorization</code>, I do not expect to need to add any special logic to implement such sub-claims, and am not sure why it is missing on the API calls.</p>
  <p>Configuration:</p>
  <pre><code>services.AddDefaultIdentity&lt;UserProfile&gt;(options =&gt; {
          options.SignIn.RequireConfirmedAccount = true;
          options.SignIn.RequireConfirmedEmail = true;
          options.Stores.MaxLengthForKeys = 64;
        }).AddEntityFrameworkStores&lt;OWSData&gt;();
  
        WebHostOptions webHost = configuration.GetSection(&quot;WebHost&quot;).Get&lt;WebHostOptions&gt;();
        string root = webHost.Url;
  
        services.AddIdentityServer((options) =&gt; {
          options.PublicOrigin = root;
          options.IssuerUri = root;
          options.UserInteraction.LoginUrl = $&quot;{root}/account/login&quot;;
          options.UserInteraction.LogoutUrl = $&quot;{root}/account/logout&quot;;
          options.UserInteraction.ErrorUrl = $&quot;{root}/account/error&quot;;
          options.UserInteraction.ConsentUrl = $&quot;{root}/account/terms-of-service&quot;;
          options.UserInteraction.DeviceVerificationUrl = $&quot;{root}/account/device-verification&quot;;
        }).AddApiAuthorization&lt;UserProfile, OWSData&gt;(options =&gt; {
          // Serilog.Log.Information(&quot;Clients: {@clients}&quot;, options.Clients);
        });
        services.AddScoped&lt;ICurrentUser, CurrentUser&gt;();
        services.AddTransient&lt;IReturnUrlParser, AuthReturnUrl&gt;();
  
        // Auth (JWT)
        services.AddAuthentication(options =&gt; {
                   // options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                 })
                .AddCookie((options) =&gt; {
                   webHost.ConfigureCookie(CookieName, options.Cookie);
                   options.LoginPath = &quot;/account/login&quot;;
                   options.AccessDeniedPath = &quot;/account/denied&quot;;
                   options.LogoutPath = &quot;/account/logout&quot;;
                   options.SlidingExpiration = true;
                 })
                .AddIdentityServerJwt()
                .AddGoogle(options =&gt; {
                   ConfigureOAuth(options, &quot;Google&quot;, configuration, webHost);
                 })
                .AddGitHub(options =&gt; {
                   ConfigureOAuth(options, &quot;GitHub&quot;, configuration, webHost);
                   options.Scope.Add(&quot;user:email&quot;);
                   options.EnterpriseDomain = configuration[&quot;GitHub:EnterpriseDomain&quot;];
                 });
  
        services.AddAuthorization(options =&gt; {
          // options.DefaultPolicy = new AuthorizationPolicy();
        });
  
        // Auth (Password)
        services.Configure&lt;IdentityOptions&gt;(options =&gt; {
          //Password settings
          options.Password.RequireDigit = true;
          options.Password.RequireLowercase = true;
          options.Password.RequireNonAlphanumeric = true;
          options.Password.RequireUppercase = true;
          options.Password.RequiredLength = 6;
          options.Password.RequiredUniqueChars = 1;
  
          //Lockout settings
          options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
          options.Lockout.MaxFailedAccessAttempts = 5;
          options.Lockout.AllowedForNewUsers = true;
  
          //User settings
          options.User.AllowedUserNameCharacters =
            &quot;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@&quot;;
          options.User.RequireUniqueEmail = false;
        });
  
        // Cookies
        services.ConfigureApplicationCookie(options =&gt; {
          webHost.ConfigureCookie(CookieName, options.Cookie);
        });
  </code></pre>