2017-04-20 24 views
1

Das erste, was zuerst: Ich weiß, das große Beitrag, aber ich dieses Problem für mehrere Wochen Tracking, und ich sammeln eine Vielzahl von Informationen, was die Quelle des Problems sein kann.Asp.NET Kern OpenIddict invalid_grant

Ich verwende Angular2-Anwendung mit OpenIddict-Authentifizierung. Ich bekomme access_token, refresh_token in der Client-App. Ich kann refresh_token verwenden, um neues access_token zu bekommen, alles funktioniert. Fast.

Irgendwann bekomme ich Fehler Antwort vom Server:

POST https://mydomain:2000/api/authorization/token 400 (Bad Request)

und Antwort:

error:"invalid_grant" 
error_description:"Invalid ticket" 

I triple alles und die refresh_token geprüft Ich sende ist richtig.

Über Design:
Bevor ich Anforderung an den Server mache ich, wenn access_token überprüfen verfallen. Wenn ablaufen, sende ich Anfrage, um neues access_token mit refresh_token zu erhalten.

Und es funktioniert für zufällige Zeit, aber zu einer zufälligen Zeit (Wiederholung) refresh_token wird ungültig.
Ich habe zwar etwas mit AddEphemeralSigningKey zu tun, und ich ändere es in AddSigningCertificate. (Details sind in this Thread.)

Ich denke, dass IIS Kestrel nach einiger Zeit der Inaktivität tötet. My Application Pool config:

StartMode: OnDemand 
Idle Time-out (minutes): 20 
Idle Time-out (action): Terminate 

Ich vermute, dass nach dem neuen Antrag gestellt, OpenIddict falsch de-Krypta refresh_token, weil Kestrel Neustart? Oder ich liege falsch?

Ich überprüfe auch OpenIddict Tabellen und OpenIddictApplications, OpenIddictAuthorizations und OpenIddictScopes sind alle leer. Nur OpenIddictTokens enthalten einige Daten (und alle sind Typ refresh_token):

OpenIddictTokens

Ich würde erwarten, dass refresh_tokens irgendwo gespeichert werden. Wo? Vielleicht ist das das Ursprungsproblem, warum sind meine refresh_tokens nach einer zufälligen Zeit ungültig (vielleicht wenn Kestrel neu gestartet wird).

IIS-Protokoll:

Hosting environment: Production 
Content root path: D:\Podatki\OpPISWeb\WWWProduction 
Now listening on: http://localhost:1408 
Application started. Press Ctrl+C to shut down. 
fail: AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerMiddleware[0] 
     The token request was rejected because the authorization code or the refresh token was invalid. 
fail: AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerMiddleware[0] 
     The token request was rejected because the authorization code or the refresh token was invalid. 

Hier sind meine Startup.cs:

public void ConfigureServices(IServiceCollection services) 
{ 
    try 
    { 
     services.Configure<IISOptions>(options => 
     { 
     }); 

     services.AddMvc(); 
     services.AddMvcCore().AddDataAnnotations(); 

     services.AddEntityFrameworkSqlServer(); 

     services.AddScoped<UserStore<AppUser, AppRole, AppDbContext, int, AppUserClaim, AppUserRole, AppUserLogin, AppUserToken, AppRoleClaim>, AppUserStore>(); 
     services.AddScoped<UserManager<AppUser>, AppUserManager>(); 
     services.AddScoped<RoleManager<AppRole>, AppRoleManager>(); 
     services.AddScoped<SignInManager<AppUser>, AppSignInManager>(); 
     services.AddScoped<RoleStore<AppRole, AppDbContext, int, AppUserRole, AppRoleClaim>, AppRoleStore>(); 

     var connection = Configuration["ConnectionStrings:Web"]; 
     services.AddDbContext<AppDbContext>(options => 
     { 
      options.UseSqlServer(connection); 
      options.UseOpenIddict<int>(); 
      if (this.env.IsDevelopment()) 
       options.EnableSensitiveDataLogging(); 
     }); 


     services 
      .AddIdentity<AppUser, AppRole>() 
      .AddUserStore<AppUserStore>() 
      .AddUserManager<AppUserManager>() 
      .AddRoleStore<AppRoleStore>() 
      .AddRoleManager<AppRoleManager>() 
      .AddSignInManager<AppSignInManager>() 
      .AddDefaultTokenProviders(); 

     services.Configure<IdentityOptions>(options => 
      { 
       options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name; 
       options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject; 
       options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role; 
      }); 

     services.AddOpenIddict<int>(options => 
     { 
      options.AddEntityFrameworkCoreStores<AppDbContext>(); 
      options.AddMvcBinders(); 
      options.EnableTokenEndpoint("/API/authorization/token"); 
      options.AllowPasswordFlow(); 
      options.AllowRefreshTokenFlow(); 
      options.AllowCustomFlow("urn:ietf:params:oauth:grant-type:google_identity_token"); 
      options.AllowCustomFlow("urn:ietf:params:oauth:grant-type:logedin"); 
      options.UseJsonWebTokens(); 
      if (this.env.IsDevelopment()) 
       options.AddEphemeralSigningKey(); 
      else 
       options.AddSigningCertificate(new FileStream(
        Directory.GetCurrentDirectory() + "/Resources/cert.pfx", FileMode.Open), "password"); 
      options.SetAccessTokenLifetime(TimeSpan.FromMinutes(30)); 
      options.SetRefreshTokenLifetime(TimeSpan.FromDays(14)); 
      if (this.env.IsDevelopment()) 
       options.DisableHttpsRequirement(); 
     }); 

     services.AddSingleton<DbSeeder>(); 
     services.AddSingleton<IConfiguration>(c => { return Configuration; }); 

    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex.ToString()); 
     throw; 
    } 
} 

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, DbSeeder dbSeeder) 
{ 
    loggerFactory.AddConsole(this.Configuration.GetSection("Logging")); 
    loggerFactory.AddDebug(); 

    if (env.IsDevelopment()) 
    { 
     app.UseDeveloperExceptionPage(); 
     app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions 
     { 
      HotModuleReplacement = true 
     }); 
    } 
    app.UseStaticFiles(); 

    app.UseStaticFiles(new StaticFileOptions() 
    { 
     FileProvider = new PhysicalFileProvider(this.Configuration["Directories:Upload"]), 
     RequestPath = new PathString("/Files") 
    }); 

    app.UseOpenIddict(); 

    var JwtOptions = new JwtBearerOptions() 
    { 
     Authority = this.Configuration["Authentication:OpenIddict:Authority"], 
     Audience = "OpPISWeb", 
     AutomaticAuthenticate = true, 
     AutomaticChallenge = true, 

     RequireHttpsMetadata = false 
    }; 
    JwtOptions.RequireHttpsMetadata = !env.IsDevelopment(); 
    app.UseJwtBearerAuthentication(JwtOptions); 

    app.UseMvc(); 

    using (var context = new AppDbContext(this.Configuration)) 
    { 
     context.Database.Migrate(); 
    } 
    try 
    { 
     dbSeeder.SeedAsync(); 
    } 
    catch (AggregateException e) 
    { 
     throw new Exception(e.ToString()); 
    } 
} 

Console Screenshots: Getting refresh_token Sending refresh_token

Update:
Am Ende alles, was ich tun musste, ist:

services.AddDataProtection() 
        .SetApplicationName(this.Configuration["Authentication:ApplicationId"]) 
        .PersistKeysToFileSystem(new DirectoryInfo(this.Configuration["Directories:Keys"])); 

Vergessen Sie nicht, Rechte hinzufügen zu IIS für Verzeichnisse: Schlüsselordner.

Antwort

1

Ich würde erwarten, dass refresh_tokens irgendwo gespeichert werden. Woher?

Nirgends. Die von OpenIddict ausgegebenen Berechtigungscodes, Aktualisierungstoken und Zugriffstoken (bei Verwendung des Standardformats) sind in sich abgeschlossen und werden nie aus Sicherheitsgründen gespeichert (nur Metadaten wie der Betreff oder die mit dem Token verbundene Berechtigungskennung).

Das Problem, das Sie sehen, wird wahrscheinlich dadurch verursacht, dass Sie Ihre Umgebung nicht so konfiguriert haben, dass die vom ASP.NET Core Data Protection-Stack verwendeten Verschlüsselungsschlüssel korrekt beibehalten werden, damit OpenIddict seine Tokens verschlüsselt. Sie können OpenIddict: 401 errors when two or more service instance count für weitere Informationen lesen, wie Sie das beheben können.

+0

@Pinpint: Ich denke, das ist wahr. Problem tritt auf, wenn sich ein anderer Benutzer anmeldet. Gibt es eine empfohlene Vorgehensweise? Ich überprüfe deine Beispiele, lese verwandte Themen. Vielleicht ein paar Zeilen Code, was soll ich tun. Danke. – Makla