2017-04-20 229 views
1

第一件事第一:我知道這是一個大的帖子,但我跟蹤這個問題幾個星期,我收集了很多信息可能是問題的根源。Asp.NET核心OpenIddict invalid_grant

我正在使用OpenIddict身份驗證的Angular2應用程序。我在客戶端應用程序上獲取access_token,refresh_token。我可以使用refresh_token來獲取新的access_token,一切正常。幾乎。

在某些時候,我從服務器獲取錯誤響應:

POST https://mydomain:2000/api/authorization/token 400(錯誤請求)

和響應:

error:"invalid_grant" 
error_description:"Invalid ticket" 

我三重檢查一切和refresh_token我發送的是正確的。

關於設計:
在向服務器發出請求之前,我檢查access_token是否過期。如果過期,我發送請求以獲取帶有refresh_token的新access_token。

它適用於隨機時間,但在一些隨機時間(重複)refresh_token變得無效。
我雖然它與AddEphemeralSigningKey有關,我把它改爲AddSigningCertificate(詳細信息請參見this線程。)

我認爲,IIS在經過一段時間的活動後會殺死Kestrel。我的應用程序池的配置是:

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

我懷疑新的請求之後,OpenIddict錯誤地去地下室refresh_token,因爲紅隼重新啓動?或者我錯了?

我也檢查OpenIddict表和OpenIddictApplications,OpenIddictAuthorizations和OpenIddictScopes都是空的。只有OpenIddictTokens包含一些數據(以及所有的類型都是refresh_token):

OpenIddictTokens

我會想到,這是refresh_tokens地方保存。 哪裏?也許這是源代碼問題,爲什麼我的refresh_tokens在一些隨機時間後失效(也許當Kestrel重新啓動時)。

IIS日誌:

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. 

這裏是我的啓動。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()); 
    } 
} 

控制檯的截圖: Getting refresh_token Sending refresh_token

更新:
最終我不得不這樣做是:

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

不要忘記添加權限到IIS的目錄:密鑰文件夾。

回答

1

我認爲,refresh_tokens保存在某個地方。哪裏?

不通。由OpenIddict發佈的授權碼,刷新令牌和訪問令牌(使用默認格式時)爲自包含,並且從未出於安全原因(僅與主題相關的元數據或與令牌關聯的授權標識符一起)存儲。

您看到的問題很可能是由於您尚未配置環境以正確保留由OpenIddict依賴的ASP.NET Core Data堆棧使用的加密密鑰來加密其令牌所導致的問題。你可以閱讀OpenIddict: 401 errors when two or more service instance count瞭解更多關於如何解決這個問題的信息。

+0

@Pinpint:我認爲這是事實。當另一個用戶登錄時會出現問題。有沒有推薦的方法來執行此操作。我檢查你的例子,閱讀相關主題。也許幾行代碼我該怎麼做。 謝謝。 – Makla