001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.rest.server.auth.oidc.rp; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.time.*; 022import java.util.*; 023 024import org.apache.juneau.rest.server.auth.*; 025import org.apache.juneau.rest.auth.oauth.flow.*; 026 027/** 028 * Immutable record describing a logged-in OpenID Connect Relying Party session. 029 * 030 * <p> 031 * Created by {@link OidcRelyingParty#completeLogin} after a successful login and persisted via a 032 * {@link SessionStore}. The {@link #principal()} carries the verified ID-token claims and is what the 033 * {@link OidcSessionAuthFilter} surfaces to {@code RoleBasedRestGuard} / {@code @Auth Principal}. 034 * 035 * <p> 036 * Two fields support server-side (back-channel) revocation: {@link #subject()} (the {@code sub} claim) 037 * and {@link #sid()} (the optional {@code sid} session-id claim). A server-side-indexed 038 * {@link SessionStore} keys on these so an IdP-pushed logout token can invalidate the matching 039 * session(s). 040 * 041 * @param id The opaque server-side session id. For {@link InMemorySessionStore} this is the cache key; 042 * for {@link SignedCookieSessionStore} it is a stable identifier embedded in the signed cookie. Never 043 * {@code null}. 044 * @param subject The ID-token {@code sub} claim. Never {@code null} or blank. 045 * @param sid The ID-token {@code sid} (session id) claim, if the IdP issued one. Used for back-channel 046 * logout targeting a single session rather than all of a subject's sessions. 047 * @param principal The verified ID-token claims as a {@link ClaimsPrincipal}. Never {@code null}. 048 * @param roles The roles extracted from the configured roles claim. Never {@code null}; may be empty. 049 * @param token The tokens returned from the token endpoint (access / refresh / id), if the store retains 050 * them. {@link SignedCookieSessionStore} does NOT retain tokens (stateless cookies don't carry them), 051 * so this is {@link Optional#empty()} for cookie-backed sessions. 052 * @param createdAt The instant the session was created. Never {@code null}. 053 * @param expiresAt The instant the session expires (bounded independently of token lifetime). Never 054 * {@code null}. 055 * @since 10.0.0 056 */ 057public record OidcSession( 058 String id, 059 String subject, 060 Optional<String> sid, 061 ClaimsPrincipal principal, 062 Set<String> roles, 063 Optional<OAuthToken> token, 064 Instant createdAt, 065 Instant expiresAt) { 066 067 /** 068 * Compact constructor enforcing non-null fields and defensively copying the role set. 069 */ 070 public OidcSession { 071 Objects.requireNonNull(id, "id"); 072 Objects.requireNonNull(subject, "subject"); 073 Objects.requireNonNull(sid, "sid"); 074 Objects.requireNonNull(principal, "principal"); 075 Objects.requireNonNull(token, "token"); 076 Objects.requireNonNull(createdAt, "createdAt"); 077 Objects.requireNonNull(expiresAt, "expiresAt"); 078 roles = roles == null 079 ? Collections.emptySet() 080 : u(cp(roles)); 081 } 082 083 /** 084 * Returns whether this session is expired relative to the supplied instant. 085 * 086 * @param now The reference instant. 087 * @return <jk>true</jk> if {@code now} is at or after {@link #expiresAt()}. 088 */ 089 public boolean isExpired(Instant now) { 090 Objects.requireNonNull(now, "now"); 091 return !now.isBefore(expiresAt); 092 } 093}