>

oidc-client를 사용하는 Angular 클라이언트가있는 Identity Server 4 응용 프로그램이 있습니다. 문제는 생산에서 방법 addUserSignedOut 입니다  클라이언트에 로그인 한 후에 전화가 멈추지 않습니다. 로컬에서 테스트하여 정상적으로 작동합니다.

배포시 존재하지 않는 쿠키와 관련된 문제 일 수 있습니다. 나는 checksession 를 확인  와이즈 비즈  방법과 쿠키가 비어 있습니다 ( getCookies() ) ) 로그인 후 해결 방법에 대해 잘 알고 있습니까?

이것은 내 IDP 코드입니다 :

document.cookie = ""

IDP 로그 아웃 컨트롤러 :

 public void ConfigureServices(IServiceCollection services)
    {
        string connectionString = Configuration.GetConnectionString("DefaultConnection");
        var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

        services.AddDataProtection()
            .AddKeyManagementOptions(dp => dp.NewKeyLifetime = TimeSpan.FromDays(90));
            //.PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
            //.ProtectKeysWithAzureKeyVault("<keyIdentifier>", "<clientId>", "<clientSecret>");
        services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString));
        services.AddIdentity<ApplicationUser, IdentityRole>(options => {
            options.Password.RequiredLength = 6;            // Passwords must be at least 6 characters
            options.Password.RequireLowercase = true;       // Passwords must have at least one lowercase ('a'-'z')
            options.Password.RequireUppercase = true;       // Passwords must have at least one uppercase ('A'-'Z')
            options.Password.RequireDigit = true;           // Passwords must have at least one digit ('0'-'9')
            options.Password.RequireNonAlphanumeric = true; // Passwords must have at least one non alphanumeric character
            options.Password.RequiredUniqueChars = 1;       // Passwords must use at least 1 different characters
        })
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();
        services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1).AddXmlSerializerFormatters();
        services.Configure<IISOptions>(iis =>
        {
            iis.AuthenticationDisplayName = "Windows";
            iis.AutomaticAuthentication = false;
        });
        services.AddTransient<IProfileService, ProfileService>();

        var builder = services.AddIdentityServer(
            options =>
            {
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;
            }
        )
        // this adds the config data from DB (clients, resources)
        .AddConfigurationStore(options =>
        {
            options.ConfigureDbContext = b =>
                b.UseSqlServer(connectionString,
                    sql => sql.MigrationsAssembly(migrationsAssembly));
        })

        // this adds the operational data from DB (codes, tokens, consents)
        .AddOperationalStore(options =>
        {
            options.ConfigureDbContext = b =>
                b.UseSqlServer(connectionString,
                    sql => sql.MigrationsAssembly(migrationsAssembly));
            // this enables automatic token cleanup. this is optional.
            options.EnableTokenCleanup = true;
        });

        // Add SAML SSO services.
        services.AddSaml(Configuration.GetSection("SAML"));
        builder.Services.Configure<SecurityStampValidatorOptions>(opts =>
        {
            opts.OnRefreshingPrincipal = SecurityStampValidatorCallback.UpdatePrincipal;
        });
        services.AddAuthentication(options => {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
       .AddCookie("Cookies", opts =>
       {
           opts.SlidingExpiration = true;
           opts.ExpireTimeSpan = TimeSpan.FromMinutes(15);
       });
        if (Environment.IsDevelopment() || Environment.EnvironmentName == "local" )
        {
            builder.AddDeveloperSigningCredential();
        }
        else
        {
            X509Certificate2 cert = null;
            using (X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
            {
                certStore.Open(OpenFlags.ReadOnly);
                X509Certificate2Collection certCollection = certStore.Certificates.Find(
                    X509FindType.FindByThumbprint,
                    Configuration.GetSection("Certificate").GetValue<string>("thumbprint"),
                    false);
                // Get the first cert with the thumbprint
                if (certCollection.Count > 0)
                {
                    cert = certCollection[0];
                    Log.Logger.Information($"Successfully loaded cert from registry: {cert.Thumbprint}");
                }
            }
            // Fallback to local file for development
            if (cert == null)
            {
                throw new Exception("Cannot find any Certificate");
            }
            builder.AddSigningCredential(cert);
        }
    }
    public void Configure(IApplicationBuilder app)
    {
        if (Environment.IsDevelopment() || Environment.EnvironmentName == "local")
        {
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();//SSO
        }
        app.UseHttpsRedirection();//SSO
        app.UseStaticFiles();
        app.UseCookiePolicy();//SSO
        app.UseIdentityServer();
        app.UseAuthentication();//SSO
        app.UseSaml();// Use SAML middleware. 
        app.UseMvcWithDefaultRoute();
    }

SAML 로그 아웃 :

   [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Logout(LogoutInputModel model)
    {
        // build a model so the logged out page knows what to display
        var vm = await BuildLoggedOutViewModelAsync(model.LogoutId);
        if (User?.Identity.IsAuthenticated == true)
        {
            // Request logout at the service provider(SAML).
            await InitiateSingleLogout();
            // delete local authentication cookie
            await _signInManager.SignOutAsync();
            // raise the logout event
            await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
        }
        if (vm.PostLogoutRedirectUri != null) {
            return Redirect(vm.PostLogoutRedirectUri);
        }
         // since we don't have a valid context, then we just go back to the home page
        return Redirect("~/");
    }

앵귤러 클라이언트 oidc 클라이언트 설정 :

   private async Task InitiateSingleLogout()
    {
        var ssoState = await _samlIdentityProvider.GetStatusAsync();
        if (await ssoState.CanSloAsync())
        {
            // Request logout at the service provider(s).
            await _samlIdentityProvider.InitiateSloAsync();
        }
     }

로그 아웃 이벤트 처리 :

const settings: any = {
  authority: `${environment.identity_server_url}`,
  client_id: 'js',
  redirect_uri: `${environment.login_redirect}/signin-oidc`,
  response_type: 'id_token token',
  scope: 'openid profile salesforce api1',
  post_logout_redirect_uri: `${environment.login_redirect}`,
  userStore: new WebStorageStateStore({ store: window.localStorage }),
  silent_redirect_uri: `${environment.login_redirect}/signin-oidc-silent`,
  // automaticSilentRenew: true
};

이것은 사용자가 로그인했는지 확인하고 this.mgr.events.addUserSignedOut(() => { this.startSigninMainWindow({ external_logout: true }) }) 에 전화하는 나의 경비입니다 :

signinRedirect()

export class AuthGuardService implements CanActivate { constructor( private authService: AuthService ) { } canActivate() { const isLoggedIn = this.authService.isLoggedInObs(); isLoggedIn.subscribe(loggedin => { if (!loggedin) { this.authService.startSigninMainWindow(); } }); return isLoggedIn; } } 입니다  oidc-client에 대한 모든 논리가있는 클래스.

AuthService

그리고 export class AuthService { mgr: UserManager = new UserManager(settings); userLoadededEvent: EventEmitter<User> = new EventEmitter<User>(); currentUser: User; loggedIn = false; authHeaders: Headers; private accountID: string; private accountToken: string; private internInfoSubject = new BehaviorSubject<InternUser>(null); public readonly internInfo$ = this.internInfoSubject .asObservable() .pipe( filter(user => user !== null), distinctUntilChanged() ); constructor( private router: Router, private internPortalService: InternPortalService, private apiService: ApiService, private http: HttpClient ) { this.mgr.events.addUserSignedOut(() => { this.startSigninMainWindow({ external_logout: true }) }) } getUser() { this.mgr .getUser() .then(user => { console.log('got user', user); this.currentUser = user; this.userLoadededEvent.emit(user); }) .catch(function(err) { console.log(err); }); } startSigninMainWindow(query = {}) {     this.mgr.signinRedirect({extraQueryParams: query}); } endSigninMainWindow() { this.mgr .signinRedirectCallback() .then(user => { console.log('signed in', user); this.currentUser = user; this.setLocalStorage(); }) .catch(function(err) { console.log(err); }); } endSigninSilentMainWindow() { this.mgr .signinSilentCallback() .then(() => { console.log('end silent signed in'); }) .catch(function(err) { console.log(err); }); } changePassword(): Observable<any> { const params = new HttpParams() .set('userId', this.currentUser.profile.sub) .set('passwordAction', '2') .set('redirectUrl', window.location.href); return this.apiService.getIdentityServer('/Token', params); } startSignoutMainWindow() { this.mgr .signoutRedirect() .then(function(resp) { console.log('signed out', resp); // setTimeout(5000, () => { // console.log('testing to see if fired...'); // }); }) .catch(function(err) { console.log(err); }); } isLoggedInObs(): Observable<boolean> { return observableFrom(this.mgr.getUser()).pipe( map<User, boolean>(user => { if (user) { if (!this.internPortalService.getAccountId()) { this.currentUser = user; this.setLocalStorage(); } return true; } else { return false; } }) ); } onSignOut(callback: Function){ this.mgr.events.addUserSignedOut(resp => { console.log("user signed out"); callback(); }); } get authenticationInfo(): InternUser { return this.internInfoSubject.value; } private setLocalStorage() { this.accountID = this.currentUser.profile.account_id; this.accountToken = this.currentUser.access_token; this.internPortalService.setAccountId(this.accountID, this.accountToken); this.internPortalService .getIntern() .subscribe(res => this.updateToken(res)); } updateToken(internUser: any): void { console.log(internUser); if (!internUser) { return; } // TODO: Refactor once BE migration is completed internUser.auth = this.currentUser; internUser.state_locales = this.internPortalService.getStateLocales( internUser.state ); this.internInfoSubject.next(internUser); this.router.navigate(['/intern-portal']); } // TODO: To be removed after QA public updateLocale(counter: number): void { const stateList = ['TX', 'MI', 'AZ', 'NC', 'SC', 'FL', 'NV', 'IN']; const newUser = this.authenticationInfo; newUser.state = stateList[counter]; newUser.state_locales = this.internPortalService.getStateLocales( newUser.state ); console.log(`An user from: ${newUser.state_locales.state} was loaded`); this.internInfoSubject.next(newUser); } logout(): void { localStorage.clear(); this.internInfoSubject.next(null); } }  구성 요소 :

signin-oidc-silent

호스트 앱에서 무한 루프가 발생하는 이유를 모르겠습니다. 실제로 클라이언트를 로컬로 사용하고 호스팅 된 IDP를 가리키면 동일한 무한 루프가 발생합니다.

콘솔에 오류가없고이 경고 만

일부 IDP 로그 :

export class SigninOidcComponent implements OnInit {
  private element: any;
  constructor(private authService: AuthService) {
  }
  ngOnInit() {
    console.log('init oidc logic');
    this.authService.endSigninMainWindow();
  }
}

ResponseValidator._processSigninParams: Response was error login_required

  • 답변 # 1

    마침내 SameSite.None 를 설정할 곳을 찾았습니다.  쿠키는 세션 상태를 확인할 때 브라우저가 IS로 다시 보낼 수 있도록합니다. 내 경우에는 ASP.NET Core Identity 를 사용하고 있습니다.  코드는 다음과 같습니다.

    builder.Services.ConfigureApplicationCookie(options =>
    {    
        options.Cookie.IsEssential = true;           
        // we need to disable to allow iframe for authorize requests
        options.Cookie.SameSite = AspNetCore.Http.SameSiteMode.None;
    });
    
    

    후 :

    var builder = services.AddIdentityServer(options =>
    ...
    
    

  • 이전 ENV 설정이있는 스케일 도커 컨테이너
  • 다음 functional programming - SML에서 레코드 값을 업데이트하는 방법은 무엇입니까?