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 java.util.*;
020
021/**
022 * SPI for persisting {@link OidcSession} instances and resolving them back from a session-cookie value.
023 *
024 * <p>
025 * Two implementations are bundled:
026 * <ul>
027 *    <li>{@link SignedCookieSessionStore} &mdash; the documented default.  Stateless: the session is
028 *       HMAC-signed and stored entirely in the cookie value.  Scales horizontally and survives restarts
029 *       with no shared store, but <b>cannot be server-revoked</b> (so it does not support back-channel
030 *       logout &mdash; see {@link #supportsServerSideRevocation()}).
031 *    <li>{@link InMemorySessionStore} &mdash; the simpler single-instance / dev option.  Server-side
032 *       indexed by {@code sub} and {@code sid}, so it <b>does</b> support back-channel logout.  Lost on
033 *       restart; breaks under horizontal scaling without sticky sessions.
034 * </ul>
035 *
036 * <p>
037 * Distributed stores (Redis / JDBC) are caller-supplied via this SPI.  A distributed store that wants
038 * to participate in back-channel logout must implement {@link #invalidateBySubject(String)} /
039 * {@link #invalidateBySessionId(String)} and report {@link #supportsServerSideRevocation()} as
040 * <jk>true</jk>.
041 *
042 * <p>
043 * Implementations must be thread-safe.
044 *
045 * <h5 class='section'>See Also:</h5><ul>
046 *    <li class='jc'>{@link OidcSession}
047 *    <li class='jc'>{@link InMemorySessionStore}
048 *    <li class='jc'>{@link SignedCookieSessionStore}
049 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/OidcRelyingParty">OIDC Relying Party Login (juneau-rest-server-auth-oidc-rp)</a>
050 * </ul>
051 *
052 * @since 10.0.0
053 */
054public interface SessionStore {
055
056   /**
057    * Persists the supplied session and returns the opaque value to set as the session cookie.
058    *
059    * <p>
060    * For a server-side store this is typically a random session id used as a lookup key; for a
061    * stateless cookie store it is the entire signed session payload.
062    *
063    * @param session The session to persist.  Must not be <jk>null</jk>.
064    * @return The cookie value the client should send back on subsequent requests.  Never <jk>null</jk>.
065    */
066   String createSessionCookieValue(OidcSession session);
067
068   /**
069    * Resolves a session from a cookie value.
070    *
071    * @param cookieValue The cookie value previously returned by {@link #createSessionCookieValue}.
072    *    Must not be <jk>null</jk>.
073    * @return The resolved session, or {@link Optional#empty()} if absent, expired, invalid, or
074    *    tampered.
075    */
076   Optional<OidcSession> lookup(String cookieValue);
077
078   /**
079    * Invalidates the session identified by the supplied cookie value.  No-op if absent.
080    *
081    * @param cookieValue The cookie value.  Must not be <jk>null</jk>.
082    */
083   void invalidate(String cookieValue);
084
085   /**
086    * Returns whether this store can revoke sessions server-side (a prerequisite for back-channel
087    * logout).
088    *
089    * <p>
090    * Defaults to <jk>false</jk> &mdash; stateless cookie stores cannot revoke a session the server
091    * never retained.
092    *
093    * @return <jk>true</jk> if {@link #invalidateBySubject(String)} / {@link #invalidateBySessionId(String)}
094    *    are supported.
095    */
096   default boolean supportsServerSideRevocation() {
097      return false;
098   }
099
100   /**
101    * Invalidates every session belonging to the supplied subject ({@code sub}).
102    *
103    * <p>
104    * Used by {@link OidcRelyingParty#backChannelLogout} when the IdP-pushed logout token carries a
105    * {@code sub} but no {@code sid}.
106    *
107    * @param subject The subject identifier.  Must not be <jk>null</jk> or blank.
108    * @return The number of sessions invalidated.
109    * @throws UnsupportedOperationException If this store does not support server-side revocation.
110    */
111   default int invalidateBySubject(String subject) {
112      throw new UnsupportedOperationException("This SessionStore does not support server-side revocation (back-channel logout requires an in-memory or distributed store).");
113   }
114
115   /**
116    * Invalidates the session(s) bound to the supplied IdP session id ({@code sid}).
117    *
118    * <p>
119    * Used by {@link OidcRelyingParty#backChannelLogout} when the IdP-pushed logout token carries a
120    * {@code sid}.
121    *
122    * @param sid The IdP session id.  Must not be <jk>null</jk> or blank.
123    * @return The number of sessions invalidated.
124    * @throws UnsupportedOperationException If this store does not support server-side revocation.
125    */
126   default int invalidateBySessionId(String sid) {
127      throw new UnsupportedOperationException("This SessionStore does not support server-side revocation (back-channel logout requires an in-memory or distributed store).");
128   }
129}