View Javadoc
1   /*
2    * Copyright 2008 Web Cohesion
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.security.oauth.provider.token;
18  
19  import org.springframework.security.core.AuthenticationException;
20  import org.springframework.security.core.Authentication;
21  import org.springframework.beans.factory.InitializingBean;
22  import org.springframework.beans.factory.annotation.Autowired;
23  import org.apache.commons.codec.binary.Base64;
24  
25  import java.util.*;
26  import java.security.SecureRandom;
27  
28  /**
29   * Base implementation for token services that uses random values to generate tokens. Only the persistence mechanism
30   * is left unimplemented.
31   *
32   * This base implementation creates tokens that have an expiration.  For request tokens, the default validity is
33   * 10 minutes.  For access tokens, the default validity is 12 hours.
34   *
35   * @author Ryan Heaton
36   */
37  public abstract class RandomValueProviderTokenServices implements OAuthProviderTokenServices, InitializingBean, OAuthTokenLifecycleRegistry {
38  
39    private Random random;
40    private int requestTokenValiditySeconds = 60 * 10; //default 10 minutes.
41    private int accessTokenValiditySeconds = 60 * 60 * 12; //default 12 hours.
42    private int tokenSecretLengthBytes = 80;
43    private final Collection<OAuthTokenLifecycleListener> lifecycleListeners = new HashSet<OAuthTokenLifecycleListener>();
44  
45    /**
46     * Read a token from persistence.
47     *
48     * @param token The token to read.
49     * @return The token, or null if the token doesn't exist.
50     */
51    protected abstract OAuthProviderTokenImpl readToken(String token);
52  
53    /**
54     * Store a token from persistence.
55     *
56     * @param tokenValue The token value.
57     * @param token The token to store.
58     */
59    protected abstract void storeToken(String tokenValue, OAuthProviderTokenImpl token);
60  
61    /**
62     * Remove a token from persistence.
63     *
64     * @param tokenValue The token to remove.
65     * @return The token that was removed.
66     */
67    protected abstract OAuthProviderTokenImpl removeToken(String tokenValue);
68  
69    /**
70     * Initialze these token services. If no random generator is set, one will be created.
71     *
72     */
73    public void afterPropertiesSet() throws Exception {
74      if (random == null) {
75        random = new SecureRandom();
76      }
77    }
78  
79    public OAuthProviderToken getToken(String token) throws AuthenticationException {
80      OAuthProviderTokenImpl tokenImpl = readToken(token);
81  
82      if (tokenImpl == null) {
83        throw new InvalidOAuthTokenException("Invalid token: " + token);
84      }
85      else if (isExpired(tokenImpl)) {
86        removeToken(token);
87        onTokenRemoved(tokenImpl);
88        throw new ExpiredOAuthTokenException("Expired token.");
89      }
90  
91      return tokenImpl;
92    }
93  
94    /**
95     * Whether the auth token is expired.
96     *
97     * @param authToken The auth token to check for expiration.
98     * @return Whether the auth token is expired.
99     */
100   protected boolean isExpired(OAuthProviderTokenImpl authToken) {
101     if (authToken.isAccessToken()) {
102       if ((authToken.getTimestamp() + (getAccessTokenValiditySeconds() * 1000L)) < System.currentTimeMillis()) {
103         return true;
104       }
105     }
106     else {
107       if ((authToken.getTimestamp() + (getRequestTokenValiditySeconds() * 1000L)) < System.currentTimeMillis()) {
108         return true;
109       }
110     }
111 
112     return false;
113   }
114 
115   public OAuthProviderToken createUnauthorizedRequestToken(String consumerKey, String callbackUrl) throws AuthenticationException {
116     String tokenValue = UUID.randomUUID().toString();
117     byte[] secretBytes = new byte[getTokenSecretLengthBytes()];
118     getRandom().nextBytes(secretBytes);
119     String secret = new String(Base64.encodeBase64(secretBytes));
120     OAuthProviderTokenImplovider/token/OAuthProviderTokenImpl.html#OAuthProviderTokenImpl">OAuthProviderTokenImpl token = new OAuthProviderTokenImpl();
121     token.setAccessToken(false);
122     token.setConsumerKey(consumerKey);
123     token.setCallbackUrl(callbackUrl);
124     token.setUserAuthentication(null);
125     token.setSecret(secret);
126     token.setValue(tokenValue);
127     token.setTimestamp(System.currentTimeMillis());
128     onTokenCreated(token);
129     storeToken(tokenValue, token);
130     return token;
131   }
132 
133   public void authorizeRequestToken(String requestToken, String verifier, Authentication authentication) throws AuthenticationException {
134     OAuthProviderTokenImpl tokenImpl = readToken(requestToken);
135 
136     if (tokenImpl == null) {
137       throw new InvalidOAuthTokenException("Invalid token: " + requestToken);
138     }
139     else if (isExpired(tokenImpl)) {
140       removeToken(requestToken);
141       onTokenRemoved(tokenImpl);
142       throw new ExpiredOAuthTokenException("Expired token.");
143     }
144     else if (tokenImpl.isAccessToken()) {
145       throw new InvalidOAuthTokenException("Request to authorize an access token.");
146     }
147 
148     tokenImpl.setUserAuthentication(authentication);
149     tokenImpl.setTimestamp(System.currentTimeMillis());//reset the expiration.
150     tokenImpl.setVerifier(verifier);
151     storeToken(requestToken, tokenImpl);
152   }
153 
154   public OAuthAccessProviderToken createAccessToken(String requestToken) throws AuthenticationException {
155     OAuthProviderTokenImpl tokenImpl = readToken(requestToken);
156 
157     if (tokenImpl == null) {
158       throw new InvalidOAuthTokenException("Invalid token: " + requestToken);
159     }
160     else if (isExpired(tokenImpl)) {
161       removeToken(requestToken);
162       onTokenRemoved(tokenImpl);
163       throw new ExpiredOAuthTokenException("Expired token.");
164     }
165     else if (tokenImpl.isAccessToken()) {
166       throw new InvalidOAuthTokenException("Not a request token.");
167     }
168     else if (tokenImpl.getUserAuthentication() == null) {
169       throw new InvalidOAuthTokenException("Request token has not been authorized.");
170     }
171 
172     OAuthProviderTokenImpl requestTokenImpl = removeToken(requestToken);
173     if (requestTokenImpl != null) {
174       onTokenRemoved(requestTokenImpl);
175     }
176 
177     String tokenValue = UUID.randomUUID().toString();
178     byte[] secretBytes = new byte[getTokenSecretLengthBytes()];
179     getRandom().nextBytes(secretBytes);
180     String secret = new String(Base64.encodeBase64(secretBytes));
181     OAuthProviderTokenImplovider/token/OAuthProviderTokenImpl.html#OAuthProviderTokenImpl">OAuthProviderTokenImpl token = new OAuthProviderTokenImpl();
182     token.setAccessToken(true);
183     token.setConsumerKey(tokenImpl.getConsumerKey());
184     token.setUserAuthentication(tokenImpl.getUserAuthentication());
185     token.setSecret(secret);
186     token.setValue(tokenValue);
187     token.setTimestamp(System.currentTimeMillis());
188     onTokenCreated(token);
189     storeToken(tokenValue, token);
190     return token;
191   }
192 
193   /**
194    * Logic for handling event firing of a removed token.
195    *
196    * @param token The token that was removed (possibly null).
197    */
198   protected void onTokenRemoved(OAuthProviderTokenImpl token) {
199     for (OAuthTokenLifecycleListener listener : getLifecycleListeners()) {
200       listener.tokenExpired(token);
201     }
202   }
203 
204   /**
205    * Logic for handling event firing of a created token.
206    *
207    * @param token The token that was created.
208    */
209   protected void onTokenCreated(OAuthProviderTokenImpl token) {
210     for (OAuthTokenLifecycleListener listener : getLifecycleListeners()) {
211       listener.tokenCreated(token);
212     }
213   }
214 
215   /**
216    * The length of the token secret in bytes, before being base64-encoded.
217    *
218    * @return The length of the token secret in bytes.
219    */
220   public int getTokenSecretLengthBytes() {
221     return tokenSecretLengthBytes;
222   }
223 
224   /**
225    * The length of the token secret in bytes, before being base64-encoded.
226    *
227    * @param tokenSecretLengthBytes The length of the token secret in bytes, before being base64-encoded.
228    */
229   public void setTokenSecretLengthBytes(int tokenSecretLengthBytes) {
230     this.tokenSecretLengthBytes = tokenSecretLengthBytes;
231   }
232 
233   /**
234    * The random value generator used to create token secrets.
235    *
236    * @return The random value generator used to create token secrets.
237    */
238   public Random getRandom() {
239     return random;
240   }
241 
242   /**
243    * The random value generator used to create token secrets.
244    *
245    * @param random The random value generator used to create token secrets.
246    */
247   public void setRandom(Random random) {
248     this.random = random;
249   }
250 
251   /**
252    * The validity (in seconds) of the unauthenticated request token.
253    *
254    * @return The validity (in seconds) of the unauthenticated request token.
255    */
256   public int getRequestTokenValiditySeconds() {
257     return requestTokenValiditySeconds;
258   }
259 
260   /**
261    * The validity (in seconds) of the unauthenticated request token.
262    *
263    * @param requestTokenValiditySeconds The validity (in seconds) of the unauthenticated request token.
264    */
265   public void setRequestTokenValiditySeconds(int requestTokenValiditySeconds) {
266     this.requestTokenValiditySeconds = requestTokenValiditySeconds;
267   }
268 
269   /**
270    * The validity (in seconds) of the access token.
271    *
272    * @return The validity (in seconds) of the access token.
273    */
274   public int getAccessTokenValiditySeconds() {
275     return accessTokenValiditySeconds;
276   }
277 
278   /**
279    * The validity (in seconds) of the access token.
280    *
281    * @param accessTokenValiditySeconds The validity (in seconds) of the access token.
282    */
283   public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
284     this.accessTokenValiditySeconds = accessTokenValiditySeconds;
285   }
286 
287   /**
288    * The collection of lifecycle listeners for these services.
289    *
290    * @return The collection of lifecycle listeners for these services.
291    */
292   public Collection<OAuthTokenLifecycleListener> getLifecycleListeners() {
293     return lifecycleListeners;
294   }
295 
296   /**
297    * Register lifecycle listener(s) with these token services.
298    *
299    * @param lifecycleListeners The listeners.
300    */
301   @Autowired ( required = false )
302   public void register(OAuthTokenLifecycleListener... lifecycleListeners) {
303     if (lifecycleListeners != null) {
304       this.lifecycleListeners.addAll(Arrays.asList(lifecycleListeners));
305     }
306   }
307 }