2016-10-09 2 views
0

OAuthAuthentication을 IS4 및 ASPNetCore와 함께 사용하는 데 문제가 있습니다. DamienBod (감사합니다) AspNetIdentity가있는 Identity Server 샘플은 Windows 서버 2012 ADFS3과 함께 OAuth를 추가하려고 시도했습니다. 외부 ADFS를 사용하여 인증 할 수 있지만 Identity Server 측에서 올바른 결과를 얻지 못했습니다. AccountController.ExternalLoginCallback : var info = await _signInManager.GetExternalLoginInfoAsync()은 NULL을 반환하고 로그인 페이지로 돌아갑니다. 다음 코드는 실험 할 때 의미가 없을 수도있는 몇 가지 사항을 포함 할 수 있습니다. 지금은이 분야에 대한 지식이 그다지 크지 않기 때문에 거의 능력의 끝까지 도달했습니다. 다음과 같이 어떤 도움을 아주 많이 내 코드 파일이 감사 :OAuth 및 ADFS가있는 Identity Server 4

Startup.cs :

`

public Startup(IHostingEnvironment env) 
{ 
var builder = new ConfigurationBuilder() 
.SetBasePath(env.ContentRootPath) 
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); 
     if (env.IsDevelopment()) 
     { 
      builder.AddUserSecrets(); 
     } 

     _environment = env; 

     builder.AddEnvironmentVariables(); 
     Configuration = builder.Build(); 
    } 

    public IConfigurationRoot Configuration { get; } 

    public void ConfigureServices(IServiceCollection services) 
    { 
     var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath, "damienbodserver.pfx"), ""); 

     services.AddMvc(); 

     services.AddDbContext<ApplicationDbContext>(options => 
      options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); 

     services.AddAuthentication(); 

     services.AddIdentity<ApplicationUser, IdentityRole>() 
     .AddEntityFrameworkStores<ApplicationDbContext>() 
     .AddDefaultTokenProviders(); 



     services.AddTransient<IProfileService, IdentityWithAdditionalClaimsProfileService>(); 

     services.AddTransient<IEmailSender, AuthMessageSender>(); 
     services.AddTransient<ISmsSender, AuthMessageSender>(); 

    // services.AddAuthentication(); 
     //var builder = services.AddIdentityServer(options => 
     //{ 
     // //options. 
     //}); 

     services.AddDeveloperIdentityServer() 
      .SetSigningCredential(cert) 
      .AddInMemoryScopes(Config.GetScopes()) 
      .AddInMemoryClients(Config.GetClients()) 
      .AddInMemoryUsers(Config.GetUsers()) 
      .AddAspNetIdentity<ApplicationUser>() 
      .AddProfileService<IdentityWithAdditionalClaimsProfileService>(); 
    } 

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 
    { 
     var certificate = new X509Certificate2(@"D:\Downloads\AspNet5IdentityServerAngularImplicitFlow-master\AspNet5IdentityServerAngularImplicitFlow-master\src\IdentityServerWithIdentitySQLite\signingCertificate.CER"); 

     loggerFactory.AddConsole(Configuration.GetSection("Logging")); 
     loggerFactory.AddDebug(); 

     if (env.IsDevelopment()) 
     { 
      app.UseDeveloperExceptionPage(); 
      app.UseDatabaseErrorPage(); 
      app.UseBrowserLink(); 
     } 
     else 
     { 
      app.UseExceptionHandler("/Home/Error"); 
     } 

     //app.UseJwtBearerAuthentication(new JwtBearerOptions 
     //{ 
     // AutomaticAuthenticate = true, 
     // AutomaticChallenge = true, 
     // SaveToken = true, 
     // TokenValidationParameters = new TokenValidationParameters 
     // { 
     //  //Iss  
     //  ValidateIssuer = true, 
     //  ValidIssuer = "http://FSSTARS-BO-ADFS.linkedgaming.com/adfs/services/trust", 
     //  // ValidIssuer = "7fe2cdc6-4c2d-4d92-bac9-990df31f669c", 

     //  ValidateAudience = true, 
     //  ValidAudience = "microsoft:identityserver:StarsRelyingTrustProvider", 
     //  ValidateIssuerSigningKey = true, 

     //  IssuerSigningKey = new X509SecurityKey(certificate), 

     //  ValidateLifetime = true, 
     //  ClockSkew = TimeSpan.Zero 

     // }, 
     //}); 

     app.UseStaticFiles(); 

     app.UseIdentity(); 

     app.UseCookieAuthentication(new CookieAuthenticationOptions 
     { 
      AuthenticationScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme, 

      AutomaticAuthenticate = false, 
      AutomaticChallenge = false 
     }); 

     app.UseOAuthAuthentication(new OAuthOptions() 
     { 
      DisplayName = "ADFS", 
      //AutomaticAuthenticate = false, 
      //AutomaticChallenge = false, 
      AuthenticationScheme = "OAuth2", 
      SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme, 
      SaveTokens = true, 

      //ClientId = "7fe2cdc6-4c2d-4d92-bac9-990df31f669c", 
      ClientId = "9fe2cdc6-4c2d-4d92-bac9-990df31f669g", 
      ClientSecret = "notneeded", 
      CallbackPath = new   Microsoft.AspNetCore.Http.PathString("/About"), 
      AuthorizationEndpoint = "https://fsstars-bo-adfs.linkedgaming.com/adfs/oauth2/authorize", 
      TokenEndpoint = "https://fsstars-bo-adfs.linkedgaming.com/adfs/oauth2/token", 
      Events = new OAuthEvents 
      { 
       OnRedirectToAuthorizationEndpoint = context => 
       { 
        context.Response.Redirect($"{context.RedirectUri}&resource=StarsRelyingTrustProvider"); 
        return Task.FromResult(0); 
       } 
      }, 


     }); 

     app.UseIdentityServer(); 
     app.UseMvc(routes => 
     { 
      routes.MapRoute(
       name: "default", 
       template: "{controller=Home}/{action=Index}/{id?}"); 
     }); 
    } 
} 
} 
` 

AccountController.cs :

`[Authorize] 
public class AccountController : Controller 
{ 
public AccountController(
IIdentityServerInteractionService interaction, 
IPersistedGrantService persistedGrantService, 
UserManager<ApplicationUser> userManager, 
SignInManager<ApplicationUser> signInManager, 
IEmailSender emailSender, 
ISmsSender smsSender, 
ILoggerFactory loggerFactory) 
{ 
_interaction = interaction; 
_persistedGrantService = persistedGrantService; 
_userManager = userManager; 
_signInManager = signInManager; 
_emailSender = emailSender; 
_smsSender = smsSender; 
_logger = loggerFactory.CreateLogger<AccountController>(); 
} 
    // 
    // GET: /Account/Login 
    [HttpGet] 
    [AllowAnonymous] 
    public IActionResult Login(string returnUrl = null) 
    { 
     ViewData["ReturnUrl"] = returnUrl; 
     return View(); 
    } 

    // 
    // POST: /Account/Login 
    [HttpPost] 
    [AllowAnonymous] 
    [ValidateAntiForgeryToken] 
    public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) 
    { 
     ViewData["ReturnUrl"] = returnUrl; 
     if (ModelState.IsValid) 
     { 
      // This doesn't count login failures towards account lockout 
      // To enable password failures to trigger account lockout, set lockoutOnFailure: true 
      var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false); 
      if (result.Succeeded) 
      { 
       _logger.LogInformation(1, "User logged in."); 
       return RedirectToLocal(returnUrl); 
      } 
      if (result.RequiresTwoFactor) 
      { 
       return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); 
      } 
      if (result.IsLockedOut) 
      { 
       _logger.LogWarning(2, "User account locked out."); 
       return View("Lockout"); 
      } 
      else 
      { 
       ModelState.AddModelError(string.Empty, "Invalid login attempt."); 
       return View(model); 
      } 
     } 

     // If we got this far, something failed, redisplay form 
     return View(model); 
    } 


    // 
    // POST: /Account/LogOff 
    //[HttpPost] 
    //[ValidateAntiForgeryToken] 
    //public async Task<IActionResult> LogOff() 
    //{ 
    // await _signInManager.SignOutAsync(); 
    // _logger.LogInformation(4, "User logged out."); 
    // return RedirectToAction(nameof(HomeController.Index), "Home"); 
    //} 

    // 
    // POST: /Account/ExternalLogin 
    [HttpPost] 
    [AllowAnonymous] 
    [ValidateAntiForgeryToken] 
    public IActionResult ExternalLogin(string provider, string returnUrl = null) 
    { 
     // Request a redirect to the external login provider. 
     var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }); 
     var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); 
     return Challenge(properties, provider); 
    } 

    // 
    // GET: /Account/ExternalLoginCallback 
    [HttpGet] 
    [AllowAnonymous] 
    public async Task<IActionResult> ExternalLoginCallback(
     string returnUrl = null, string remoteError = null) 
    { 
     if (remoteError != null) 
     { 
      ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}"); 
      return View(nameof(Login)); 
     } 
     var info = await _signInManager.GetExternalLoginInfoAsync(); 
     if (info == null) 
     { 
      return RedirectToAction(nameof(Login)); 
     } 

     // Sign in the user with this external login provider if the user already has a login. 
     var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false); 
     if (result.Succeeded) 
     { 
      _logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider); 
      return RedirectToLocal(returnUrl); 
     } 
     if (result.RequiresTwoFactor) 
     { 
      return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl }); 
     } 
     if (result.IsLockedOut) 
     { 
      return View("Lockout"); 
     } 
     else 
     { 
      // If the user does not have an account, then ask the user to create an account. 
      ViewData["ReturnUrl"] = returnUrl; 
      ViewData["LoginProvider"] = info.LoginProvider; 
      var email = info.Principal.FindFirstValue(ClaimTypes.Email); 
      return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email }); 
     } 
    } 

    // 
    // POST: /Account/ExternalLoginConfirmation 
    [HttpPost] 
    [AllowAnonymous] 
    [ValidateAntiForgeryToken] 
    public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null) 
    { 
     if (ModelState.IsValid) 
     { 
      // Get the information about the user from the external login provider 
      var info = await _signInManager.GetExternalLoginInfoAsync(); 
      if (info == null) 
      { 
       return View("ExternalLoginFailure"); 
      } 
      var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; 
      var result = await _userManager.CreateAsync(user); 
      if (result.Succeeded) 
      { 
       result = await _userManager.AddLoginAsync(user, info); 
       if (result.Succeeded) 
       { 
        await _signInManager.SignInAsync(user, isPersistent: false); 
        _logger.LogInformation(6, "User created an account using {Name} provider.", info.LoginProvider); 
        return RedirectToLocal(returnUrl); 
       } 
      } 
      AddErrors(result); 
     } 

     ViewData["ReturnUrl"] = returnUrl; 
     return View(model); 
    } 

    // GET: /Account/ConfirmEmail 
    [HttpGet] 
    [AllowAnonymous] 
    public async Task<IActionResult> ConfirmEmail(string userId, string code) 
    { 
     if (userId == null || code == null) 
     { 
      return View("Error"); 
     } 
     var user = await _userManager.FindByIdAsync(userId); 
     if (user == null) 
     { 
      return View("Error"); 
     } 
     var result = await _userManager.ConfirmEmailAsync(user, code); 
     return View(result.Succeeded ? "ConfirmEmail" : "Error"); 
    } 

    /// <summary> 
    /// Show logout page 
    /// </summary> 
    [HttpGet] 
    public IActionResult Logout(string logoutId) 
    { 
     var vm = new LogoutViewModel 
     { 
      LogoutId = logoutId 
     }; 

     return View(vm); 
    } 

    /// <summary> 
    /// Handle logout page postback 
    /// </summary> 
    [HttpPost] 
    [ValidateAntiForgeryToken] 
    public async Task<IActionResult> Logout(LogoutViewModel model) 
    { 
     var user = HttpContext.User.Identity.Name; 
     var subjectId = HttpContext.User.Identity.GetSubjectId(); 

     // delete authentication cookie 
     await HttpContext.Authentication.SignOutAsync(); 


     // set this so UI rendering sees an anonymous user 
     HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); 

     // get context information (client name, post logout redirect URI and iframe for federated signout) 
     var logout = await _interaction.GetLogoutContextAsync(model.LogoutId); 

     var vm = new LoggedOutViewModel 
     { 
      PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, 
      ClientName = logout?.ClientId, 
      SignOutIframeUrl = logout?.SignOutIFrameUrl 
     }; 


     await _persistedGrantService.RemoveAllGrantsAsync(subjectId, "angular2client"); 

     return View("LoggedOut", vm); 
    } 

    #region Helpers 

    private void AddErrors(IdentityResult result) 
    { 
     foreach (var error in result.Errors) 
     { 
      ModelState.AddModelError(string.Empty, error.Description); 
     } 
    } 

    private Task<ApplicationUser> GetCurrentUserAsync() 
    { 
     return _userManager.GetUserAsync(HttpContext.User); 
    } 

    private IActionResult RedirectToLocal(string returnUrl) 
    { 
     if (Url.IsLocalUrl(returnUrl)) 
     { 
      return Redirect(returnUrl); 
     } 
     else 
     { 
      return RedirectToAction(nameof(HomeController.Index), "Home"); 
     } 
    } 

답변

2

_signInManager.GetExternalLoginInfoAsync() ID가 사용중인 스키마를 알지 못하기 때문에 실패합니다. 정보를 추출하는 GetExternalLoginInfoAsync와 유사한 메소드를 만들어야합니다.

private async Task<ExternalLoginInfo> AuthenticationManager_GetExternalLoginInfoAsync_Workaround() 
     { 
      ExternalLoginInfo loginInfo = null; 
      var info = await HttpContext.Authentication.GetAuthenticateInfoAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); 
      var auth = new AuthenticateContext(IdentityServerConstants.ExternalCookieAuthenticationScheme); 
      // read external identity from the temporary cookie 
      await HttpContext.Authentication.AuthenticateAsync(auth); 

      if (auth.Principal == null || auth.Properties == null) 
      { 
       throw new Exception("External authentication error"); 
      } 

      // retrieve claims of the external user 
      var claims = auth.Principal.Claims.ToList(); 


      // try to determine the unique id of the external user - the most common claim type for that are the sub claim and the NameIdentifier 
      // depending on the external provider, some other claim type might be used 
      var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject); 
      if (userIdClaim == null) 
      { 
       userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier); 
      } 
      if (userIdClaim == null) 
      { 
       throw new Exception("Unknown userid"); 
      } 

      string userObjectID = auth.Principal.FindFirst(ClaimTypes.NameIdentifier).Value; 
      // remove the user id claim from the claims collection and move to the userId property 
      // also set the name of the external authentication provider 
      claims.Remove(userIdClaim); 
      var provider = userIdClaim.Issuer; 
      var userId = userIdClaim.Value; 

      var displayName = auth.Principal.FindFirstValue(ClaimTypes.GivenName); 

      AuthenticationProperties props = null; 


      loginInfo = new ExternalLoginInfo(auth.Principal, userIdClaim.Issuer, userIdClaim.Value, displayName)   

      return loginInfo; 
     } 

그리고 내가 ExternalLoginCallback

var info = await _signInManager.GetExternalLoginInfoAsync(); 
     if (info == null) 
     { 
      info = await AuthenticationManager_GetExternalLoginInfoAsync_Workaround(); 
      if (info == null) 
      { 
       return RedirectToAction(nameof(Login)); 
      } 

     } 

에 추가는 여전히 진행중인 자사의 일을 도움이되기를 바랍니다.