001package com.nimbusds.openid.connect.provider.spi.grants; 002 003 004import java.util.ArrayList; 005import java.util.List; 006 007import net.jcip.annotations.Immutable; 008import net.minidev.json.JSONObject; 009import org.checkerframework.checker.nullness.qual.Nullable; 010 011import com.nimbusds.oauth2.sdk.ParseException; 012import com.nimbusds.oauth2.sdk.id.Audience; 013import com.nimbusds.oauth2.sdk.id.Subject; 014import com.nimbusds.oauth2.sdk.util.CollectionUtils; 015import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 016 017 018/** 019 * Base token specification. 020 */ 021@Immutable 022public class TokenSpec { 023 024 025 /** 026 * The token lifetime, in seconds. For access tokens zero and negative 027 * means not specified (to let the Connect2id server apply the default 028 * configured access token lifetime). For refresh tokens zero means no 029 * lifetime limit and negative means not specified (to let the 030 * Connect2id server apply the default configured refresh token 031 * lifetime). 032 */ 033 private final long lifetime; 034 035 036 /** 037 * Explicit list of audiences for the token, {@code null} if none. 038 */ 039 private final @Nullable List<Audience> audList; 040 041 042 /** 043 * The subject in impersonation and delegation cases, {@code null} if 044 * not applicable. 045 */ 046 private final @Nullable Subject impersonatedSubject; 047 048 049 /** 050 * Creates a new token specification. No explicit token audience is 051 * specified. No subject in impersonation and delegation cases is 052 * specified. 053 * 054 * @param lifetime The token lifetime, in seconds. For access tokens 055 * zero and negative means not specified (to let the 056 * Connect2id server apply the default configured 057 * access token lifetime). For refresh tokens zero 058 * means no lifetime limit and negative means not 059 * specified (to let the Connect2id server apply the 060 * default configured refresh token lifetime). 061 */ 062 public TokenSpec(final long lifetime) { 063 064 this(lifetime, null, null); 065 } 066 067 068 /** 069 * Creates a new token specification. 070 * 071 * @param lifetime The token lifetime, in seconds. For 072 * access tokens zero and negative means not 073 * specified (to let the Connect2id 074 * server apply the default configured 075 * access token lifetime). For refresh 076 * tokens zero means no lifetime limit and 077 * negative means not specified (to let the 078 * Connect2id server apply the default 079 * configured refresh token lifetime). 080 * @param audList Explicit list of audiences for the token, 081 * {@code null} if not specified. 082 * @param impersonatedSubject The subject in impersonation and 083 * delegation cases, {@code null} if not 084 * applicable. 085 */ 086 public TokenSpec(final long lifetime, final @Nullable List<Audience> audList, final @Nullable Subject impersonatedSubject) { 087 088 this.lifetime = lifetime; 089 this.audList = audList; 090 this.impersonatedSubject = impersonatedSubject; 091 } 092 093 094 /** 095 * Returns the token lifetime. 096 * 097 * @return The token lifetime, in seconds. For access tokens zero and 098 * negative means not specified (to let the Connect2id server 099 * apply the default configured access token lifetime). For 100 * refresh tokens zero means no lifetime limit and negative 101 * means not specified (to let the Connect2id server apply the 102 * default configured refresh token lifetime). 103 */ 104 public long getLifetime() { 105 106 return lifetime; 107 } 108 109 110 /** 111 * Returns the explicit list of audiences for the token. 112 * 113 * @return The explicit list of audiences for the token, {@code null} 114 * if not specified. 115 */ 116 public @Nullable List<Audience> getAudience() { 117 118 return audList; 119 } 120 121 122 /** 123 * Returns the subject in impersonation and delegation cases. 124 * 125 * @return The subject in impersonation and delegation cases, 126 * {@code null} if not applicable. 127 */ 128 public @Nullable Subject getImpersonatedSubject() { 129 130 return impersonatedSubject; 131 } 132 133 134 /** 135 * Returns a JSON object representation of this token specification. 136 * 137 * @return The JSON object. 138 */ 139 public JSONObject toJSONObject() { 140 141 JSONObject o = new JSONObject(); 142 143 if (lifetime >= 0L) { 144 o.put("lifetime", lifetime); 145 } 146 147 if (CollectionUtils.isNotEmpty(audList)) { 148 149 List<String> sl = new ArrayList<>(audList.size()); 150 151 for (Audience aud: audList) { 152 sl.add(aud.getValue()); 153 } 154 155 o.put("audience", sl); 156 } 157 158 if (impersonatedSubject != null) { 159 o.put("impersonated_sub", impersonatedSubject.getValue()); 160 } 161 162 return o; 163 } 164 165 166 @Override 167 public String toString() { 168 169 return toJSONObject().toJSONString(); 170 } 171 172 173 /** 174 * Parses a token specification from the specified JSON object. 175 * 176 * @param jsonObject The JSON object. Must not be {@code null}. 177 * 178 * @return The token specification. 179 * 180 * @throws ParseException If parsing failed. 181 */ 182 public static TokenSpec parse(final JSONObject jsonObject) 183 throws ParseException { 184 185 long lifetime = -1L; 186 187 if (jsonObject.containsKey("lifetime")) { 188 lifetime = JSONObjectUtils.getLong(jsonObject, "lifetime"); 189 } 190 191 List<Audience> audList = null; 192 193 if (jsonObject.containsKey("audience")) { 194 audList = Audience.create(JSONObjectUtils.getStringList(jsonObject, "audience")); 195 } 196 197 Subject impersonatedSub = null; 198 199 if (jsonObject.containsKey("impersonated_sub")) { 200 try { 201 impersonatedSub = new Subject(JSONObjectUtils.getString(jsonObject, "impersonated_sub")); 202 } catch (IllegalArgumentException e) { 203 // On invalid subject 204 throw new ParseException(e.getMessage(), e); 205 } 206 } 207 208 return new TokenSpec(lifetime, audList, impersonatedSub); 209 } 210}