/// <summary> /// Create the claims to be used in the back-channel logout token. /// </summary> /// <param name="request"></param> /// <returns>The claims to include in the token.</returns> protected Task <IEnumerable <Claim> > CreateClaimsForTokenAsync(BackChannelLogoutRequest request) { if (request.SessionIdRequired && request.SessionId == null) { throw new ArgumentException("Client requires SessionId", nameof(request.SessionId)); } var json = "{\"" + OidcConstants.Events.BackChannelLogout + "\":{} }"; var claims = new List <Claim> { new Claim(JwtClaimTypes.Subject, request.SubjectId), new Claim(JwtClaimTypes.Audience, request.ClientId), new Claim(JwtClaimTypes.IssuedAt, Clock.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64), new Claim(JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId(16, CryptoRandom.OutputFormat.Hex)), new Claim(JwtClaimTypes.Events, json, IdentityServerConstants.ClaimValueTypes.Json) }; if (request.SessionId != null) { claims.Add(new Claim(JwtClaimTypes.SessionId, request.SessionId)); } return(Task.FromResult(claims.AsEnumerable())); }
/// <summary> /// Creates the JWT used for the back-channel logout notification. /// </summary> /// <param name="request"></param> /// <returns>The token.</returns> protected virtual async Task <string> CreateTokenAsync(BackChannelLogoutRequest request) { var claims = await CreateClaimsForTokenAsync(request); if (claims.Any(x => x.Type == JwtClaimTypes.Nonce)) { throw new InvalidOperationException("nonce claim is not allowed in the back-channel signout token."); } return(await Tools.IssueJwtAsync(DefaultLogoutTokenLifetime, claims)); }
/// <inheritdoc/> public async Task <IEnumerable <BackChannelLogoutRequest> > GetBackChannelLogoutNotificationsAsync(LogoutNotificationContext context) { var backChannelLogouts = new List <BackChannelLogoutRequest>(); foreach (var clientId in context.ClientIds) { var client = await _clientStore.FindEnabledClientByIdAsync(clientId); if (client != null) { if (client.BackChannelLogoutUri.IsPresent()) { var back = new BackChannelLogoutRequest { ClientId = clientId, LogoutUri = client.BackChannelLogoutUri, SubjectId = context.SubjectId, SessionId = context.SessionId, SessionIdRequired = client.BackChannelLogoutSessionRequired }; backChannelLogouts.Add(back); } } } if (backChannelLogouts.Any()) { var msg = backChannelLogouts.Select(x => x.LogoutUri).Aggregate((x, y) => x + ", " + y); _logger.LogDebug("Client back-channel logout URLs: {0}", msg); } else { _logger.LogDebug("No client back-channel logout URLs"); } return(backChannelLogouts); }
/// <summary> /// Creates the form-url-encoded payload (as a dictionary) to send to the client. /// </summary> /// <param name="request"></param> /// <returns></returns> protected async Task <Dictionary <string, string> > CreateFormPostPayloadAsync(BackChannelLogoutRequest request) { var token = await CreateTokenAsync(request); var data = new Dictionary <string, string> { { OidcConstants.BackChannelLogoutRequest.LogoutToken, token } }; return(data); }
/// <summary> /// Performs the HTTP POST of the logout payload to the client. /// </summary> /// <param name="client"></param> /// <param name="data"></param> /// <returns></returns> protected virtual Task PostLogoutJwt(BackChannelLogoutRequest client, Dictionary <string, string> data) { return(HttpClient.PostAsync(client.LogoutUri, data)); }
/// <summary> /// Performs the back-channel logout for a single client. /// </summary> /// <param name="request"></param> protected virtual async Task SendLogoutNotificationAsync(BackChannelLogoutRequest request) { var data = await CreateFormPostPayloadAsync(request); await PostLogoutJwt(request, data); }