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}