made unused auth codes expired (they're still single-use), refactored auth code service layer
parent
7e9ee525a8
commit
4f12fab56b
|
@ -34,7 +34,10 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||||
@Table(name = "authentication_holder")
|
@Table(name = "authentication_holder")
|
||||||
@NamedQueries ({
|
@NamedQueries ({
|
||||||
@NamedQuery(name = AuthenticationHolderEntity.QUERY_ALL, query = "select a from AuthenticationHolderEntity a"),
|
@NamedQuery(name = AuthenticationHolderEntity.QUERY_ALL, query = "select a from AuthenticationHolderEntity a"),
|
||||||
@NamedQuery(name = AuthenticationHolderEntity.QUERY_GET_UNUSED, query = "select a from AuthenticationHolderEntity a where a.id not in (select t.authenticationHolder.id from OAuth2AccessTokenEntity t) and a.id not in (select r.authenticationHolder.id from OAuth2RefreshTokenEntity r)")
|
@NamedQuery(name = AuthenticationHolderEntity.QUERY_GET_UNUSED, query = "select a from AuthenticationHolderEntity a where " +
|
||||||
|
"a.id not in (select t.authenticationHolder.id from OAuth2AccessTokenEntity t) and " +
|
||||||
|
"a.id not in (select r.authenticationHolder.id from OAuth2RefreshTokenEntity r) and " +
|
||||||
|
"a.id not in (select c.authenticationHolder.id from AuthorizationCodeEntity c)")
|
||||||
})
|
})
|
||||||
public class AuthenticationHolderEntity {
|
public class AuthenticationHolderEntity {
|
||||||
|
|
||||||
|
|
|
@ -16,19 +16,20 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.mitre.oauth2.model;
|
package org.mitre.oauth2.model;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.persistence.Basic;
|
import javax.persistence.Basic;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.FetchType;
|
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Lob;
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.NamedQueries;
|
import javax.persistence.NamedQueries;
|
||||||
import javax.persistence.NamedQuery;
|
import javax.persistence.NamedQuery;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Temporal;
|
||||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity class for authorization codes
|
* Entity class for authorization codes
|
||||||
|
@ -39,17 +40,23 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "authorization_code")
|
@Table(name = "authorization_code")
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
@NamedQuery(name = AuthorizationCodeEntity.QUERY_BY_VALUE, query = "select a from AuthorizationCodeEntity a where a.code = :code")
|
@NamedQuery(name = AuthorizationCodeEntity.QUERY_BY_VALUE, query = "select a from AuthorizationCodeEntity a where a.code = :code"),
|
||||||
|
@NamedQuery(name = AuthorizationCodeEntity.QUERY_EXPIRATION_BY_DATE, query = "select a from AuthorizationCodeEntity a where a.expiration <= :" + AuthorizationCodeEntity.PARAM_DATE)
|
||||||
})
|
})
|
||||||
public class AuthorizationCodeEntity {
|
public class AuthorizationCodeEntity {
|
||||||
|
|
||||||
public static final String QUERY_BY_VALUE = "AuthorizationCodeEntity.getByValue";
|
public static final String QUERY_BY_VALUE = "AuthorizationCodeEntity.getByValue";
|
||||||
|
public static final String QUERY_EXPIRATION_BY_DATE = "AuthorizationCodeEntity.expirationByDate";
|
||||||
|
|
||||||
|
public static final String PARAM_DATE = "date";
|
||||||
|
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
private String code;
|
private String code;
|
||||||
|
|
||||||
private OAuth2Authentication authentication;
|
private AuthenticationHolderEntity authenticationHolder;
|
||||||
|
|
||||||
|
private Date expiration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
|
@ -64,9 +71,10 @@ public class AuthorizationCodeEntity {
|
||||||
* @param code the authorization code
|
* @param code the authorization code
|
||||||
* @param authRequest the AuthoriztionRequestHolder associated with the original code request
|
* @param authRequest the AuthoriztionRequestHolder associated with the original code request
|
||||||
*/
|
*/
|
||||||
public AuthorizationCodeEntity(String code, OAuth2Authentication authRequest) {
|
public AuthorizationCodeEntity(String code, AuthenticationHolderEntity authenticationHolder, Date expiration) {
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.authentication = authRequest;
|
this.authenticationHolder = authenticationHolder;
|
||||||
|
this.expiration = expiration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,20 +111,30 @@ public class AuthorizationCodeEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The authentication in place when this token was created.
|
||||||
* @return the authentication
|
* @return the authentication
|
||||||
*/
|
*/
|
||||||
@Lob
|
@ManyToOne
|
||||||
@Basic(fetch=FetchType.EAGER)
|
@JoinColumn(name = "auth_holder_id")
|
||||||
@Column(name="authentication")
|
public AuthenticationHolderEntity getAuthenticationHolder() {
|
||||||
public OAuth2Authentication getAuthentication() {
|
return authenticationHolder;
|
||||||
return authentication;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param authentication the authentication to set
|
* @param authentication the authentication to set
|
||||||
*/
|
*/
|
||||||
public void setAuthentication(OAuth2Authentication authentication) {
|
public void setAuthenticationHolder(AuthenticationHolderEntity authenticationHolder) {
|
||||||
this.authentication = authentication;
|
this.authenticationHolder = authenticationHolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Basic
|
||||||
|
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
|
||||||
|
@Column(name = "expiration")
|
||||||
|
public Date getExpiration() {
|
||||||
|
return expiration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpiration(Date expiration) {
|
||||||
|
this.expiration = expiration;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.mitre.oauth2.repository;
|
package org.mitre.oauth2.repository;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.mitre.oauth2.model.AuthorizationCodeEntity;
|
import org.mitre.oauth2.model.AuthorizationCodeEntity;
|
||||||
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
|
||||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for saving and consuming OAuth2 authorization codes as AuthorizationCodeEntitys.
|
* Interface for saving and consuming OAuth2 authorization codes as AuthorizationCodeEntitys.
|
||||||
|
@ -37,12 +37,23 @@ public interface AuthorizationCodeRepository {
|
||||||
public AuthorizationCodeEntity save(AuthorizationCodeEntity authorizationCode);
|
public AuthorizationCodeEntity save(AuthorizationCodeEntity authorizationCode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consume an authorization code.
|
* Get an authorization code from the repository by value.
|
||||||
*
|
*
|
||||||
* @param code the authorization code value
|
* @param code the authorization code value
|
||||||
* @return the authentication associated with the code
|
* @return the authentication associated with the code
|
||||||
* @throws InvalidGrantException if no AuthorizationCodeEntity is found with the given value
|
|
||||||
*/
|
*/
|
||||||
public OAuth2Authentication consume(String code) throws InvalidGrantException;
|
public AuthorizationCodeEntity getByCode(String code);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an authorization code from the repository
|
||||||
|
*
|
||||||
|
* @param authorizationCodeEntity
|
||||||
|
*/
|
||||||
|
public void remove(AuthorizationCodeEntity authorizationCodeEntity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A collection of all expired codes.
|
||||||
|
*/
|
||||||
|
public Collection<AuthorizationCodeEntity> getExpiredCodes();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,8 @@ CREATE TABLE IF NOT EXISTS client_authority (
|
||||||
CREATE TABLE IF NOT EXISTS authorization_code (
|
CREATE TABLE IF NOT EXISTS authorization_code (
|
||||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY,
|
id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY,
|
||||||
code VARCHAR(256),
|
code VARCHAR(256),
|
||||||
authentication LONGVARBINARY
|
auth_holder_id BIGINT,
|
||||||
|
expiration TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS client_grant_type (
|
CREATE TABLE IF NOT EXISTS client_grant_type (
|
||||||
|
|
|
@ -52,7 +52,8 @@ CREATE TABLE IF NOT EXISTS client_authority (
|
||||||
CREATE TABLE IF NOT EXISTS authorization_code (
|
CREATE TABLE IF NOT EXISTS authorization_code (
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
code VARCHAR(256),
|
code VARCHAR(256),
|
||||||
authentication LONGBLOB
|
auth_holder_id BIGINT,
|
||||||
|
expiration TIMESTAMP NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS client_grant_type (
|
CREATE TABLE IF NOT EXISTS client_grant_type (
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
<task:scheduled-tasks scheduler="taskScheduler">
|
<task:scheduled-tasks scheduler="taskScheduler">
|
||||||
<task:scheduled ref="defaultOAuth2ProviderTokenService" method="clearExpiredTokens" fixed-delay="300000" initial-delay="600000"/>
|
<task:scheduled ref="defaultOAuth2ProviderTokenService" method="clearExpiredTokens" fixed-delay="300000" initial-delay="600000"/>
|
||||||
<task:scheduled ref="defaultApprovedSiteService" method="clearExpiredSites" fixed-delay="300000" initial-delay="600000"/>
|
<task:scheduled ref="defaultApprovedSiteService" method="clearExpiredSites" fixed-delay="300000" initial-delay="600000"/>
|
||||||
|
<task:scheduled ref="defaultOAuth2AuthorizationCodeService" method="clearExpiredAuthorizationCodes" fixed-delay="300000" initial-delay="600000"/>
|
||||||
</task:scheduled-tasks>
|
</task:scheduled-tasks>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
*/
|
*/
|
||||||
package org.mitre.oauth2.repository.impl;
|
package org.mitre.oauth2.repository.impl;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.PersistenceContext;
|
import javax.persistence.PersistenceContext;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
|
@ -26,8 +29,6 @@ import javax.persistence.TypedQuery;
|
||||||
import org.mitre.oauth2.model.AuthorizationCodeEntity;
|
import org.mitre.oauth2.model.AuthorizationCodeEntity;
|
||||||
import org.mitre.oauth2.repository.AuthorizationCodeRepository;
|
import org.mitre.oauth2.repository.AuthorizationCodeRepository;
|
||||||
import org.mitre.util.jpa.JpaUtil;
|
import org.mitre.util.jpa.JpaUtil;
|
||||||
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
|
||||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -56,27 +57,39 @@ public class JpaAuthorizationCodeRepository implements AuthorizationCodeReposito
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.mitre.oauth2.repository.AuthorizationCodeRepository#consume(java.lang.String)
|
* @see org.mitre.oauth2.repository.AuthorizationCodeRepository#getByCode(java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public OAuth2Authentication consume(String code) throws InvalidGrantException {
|
public AuthorizationCodeEntity getByCode(String code) {
|
||||||
|
|
||||||
TypedQuery<AuthorizationCodeEntity> query = manager.createNamedQuery(AuthorizationCodeEntity.QUERY_BY_VALUE, AuthorizationCodeEntity.class);
|
TypedQuery<AuthorizationCodeEntity> query = manager.createNamedQuery(AuthorizationCodeEntity.QUERY_BY_VALUE, AuthorizationCodeEntity.class);
|
||||||
query.setParameter("code", code);
|
query.setParameter("code", code);
|
||||||
|
|
||||||
AuthorizationCodeEntity result = JpaUtil.getSingleResult(query.getResultList());
|
AuthorizationCodeEntity result = JpaUtil.getSingleResult(query.getResultList());
|
||||||
|
return result;
|
||||||
if (result == null) {
|
|
||||||
throw new InvalidGrantException("JpaAuthorizationCodeRepository: no authorization code found for value " + code);
|
|
||||||
}
|
|
||||||
|
|
||||||
OAuth2Authentication authRequest = result.getAuthentication();
|
|
||||||
|
|
||||||
manager.remove(result);
|
|
||||||
|
|
||||||
return authRequest;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.mitre.oauth2.repository.AuthorizationCodeRepository#remove(org.mitre.oauth2.model.AuthorizationCodeEntity)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void remove(AuthorizationCodeEntity authorizationCodeEntity) {
|
||||||
|
AuthorizationCodeEntity found = manager.find(AuthorizationCodeEntity.class, authorizationCodeEntity.getId());
|
||||||
|
if (found != null) {
|
||||||
|
manager.remove(found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.mitre.oauth2.repository.AuthorizationCodeRepository#getExpiredCodes()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<AuthorizationCodeEntity> getExpiredCodes() {
|
||||||
|
TypedQuery<AuthorizationCodeEntity> query = manager.createNamedQuery(AuthorizationCodeEntity.QUERY_EXPIRATION_BY_DATE, AuthorizationCodeEntity.class);
|
||||||
|
query.setParameter(AuthorizationCodeEntity.PARAM_DATE, new Date()); // this gets anything that's already expired
|
||||||
|
return query.getResultList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,15 @@
|
||||||
*/
|
*/
|
||||||
package org.mitre.oauth2.service.impl;
|
package org.mitre.oauth2.service.impl;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.mitre.oauth2.model.AuthenticationHolderEntity;
|
||||||
import org.mitre.oauth2.model.AuthorizationCodeEntity;
|
import org.mitre.oauth2.model.AuthorizationCodeEntity;
|
||||||
|
import org.mitre.oauth2.repository.AuthenticationHolderRepository;
|
||||||
import org.mitre.oauth2.repository.AuthorizationCodeRepository;
|
import org.mitre.oauth2.repository.AuthorizationCodeRepository;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
||||||
|
@ -27,6 +35,7 @@ import org.springframework.security.oauth2.common.util.RandomValueStringGenerato
|
||||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||||
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
|
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database-backed, random-value authorization code service implementation.
|
* Database-backed, random-value authorization code service implementation.
|
||||||
|
@ -34,11 +43,18 @@ import org.springframework.stereotype.Service;
|
||||||
* @author aanganes
|
* @author aanganes
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service("defaultOAuth2AuthorizationCodeService")
|
||||||
public class DefaultOAuth2AuthorizationCodeService implements AuthorizationCodeServices {
|
public class DefaultOAuth2AuthorizationCodeService implements AuthorizationCodeServices {
|
||||||
|
// Logger for this class
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(DefaultOAuth2AuthorizationCodeService.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AuthorizationCodeRepository repository;
|
private AuthorizationCodeRepository repository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthenticationHolderRepository authenticationHolderRepository;
|
||||||
|
|
||||||
|
private int authCodeExpirationSeconds = 60 * 5; // expire in 5 minutes by default
|
||||||
|
|
||||||
private RandomValueStringGenerator generator = new RandomValueStringGenerator();
|
private RandomValueStringGenerator generator = new RandomValueStringGenerator();
|
||||||
|
|
||||||
|
@ -54,7 +70,15 @@ public class DefaultOAuth2AuthorizationCodeService implements AuthorizationCodeS
|
||||||
public String createAuthorizationCode(OAuth2Authentication authentication) {
|
public String createAuthorizationCode(OAuth2Authentication authentication) {
|
||||||
String code = generator.generate();
|
String code = generator.generate();
|
||||||
|
|
||||||
AuthorizationCodeEntity entity = new AuthorizationCodeEntity(code, authentication);
|
// attach the authorization so that we can look it up later
|
||||||
|
AuthenticationHolderEntity authHolder = new AuthenticationHolderEntity();
|
||||||
|
authHolder.setAuthentication(authentication);
|
||||||
|
authHolder = authenticationHolderRepository.save(authHolder);
|
||||||
|
|
||||||
|
// set the auth code to expire
|
||||||
|
Date expiration = new Date(System.currentTimeMillis() + (getAuthCodeExpirationSeconds() * 1000L));
|
||||||
|
|
||||||
|
AuthorizationCodeEntity entity = new AuthorizationCodeEntity(code, authHolder, expiration);
|
||||||
repository.save(entity);
|
repository.save(entity);
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
|
@ -73,9 +97,34 @@ public class DefaultOAuth2AuthorizationCodeService implements AuthorizationCodeS
|
||||||
@Override
|
@Override
|
||||||
public OAuth2Authentication consumeAuthorizationCode(String code) throws InvalidGrantException {
|
public OAuth2Authentication consumeAuthorizationCode(String code) throws InvalidGrantException {
|
||||||
|
|
||||||
OAuth2Authentication auth = repository.consume(code);
|
AuthorizationCodeEntity result = repository.getByCode(code);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
throw new InvalidGrantException("JpaAuthorizationCodeRepository: no authorization code found for value " + code);
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2Authentication auth = result.getAuthenticationHolder().getAuthentication();
|
||||||
|
|
||||||
|
repository.remove(result);
|
||||||
|
|
||||||
return auth;
|
return auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find and remove all expired auth codes.
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public void clearExpiredAuthorizationCodes() {
|
||||||
|
|
||||||
|
Collection<AuthorizationCodeEntity> codes = repository.getExpiredCodes();
|
||||||
|
|
||||||
|
for (AuthorizationCodeEntity code : codes) {
|
||||||
|
repository.remove(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Removed " + codes.size() + " expired authorization codes.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the repository
|
* @return the repository
|
||||||
|
@ -91,4 +140,18 @@ public class DefaultOAuth2AuthorizationCodeService implements AuthorizationCodeS
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the authCodeExpirationSeconds
|
||||||
|
*/
|
||||||
|
public int getAuthCodeExpirationSeconds() {
|
||||||
|
return authCodeExpirationSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param authCodeExpirationSeconds the authCodeExpirationSeconds to set
|
||||||
|
*/
|
||||||
|
public void setAuthCodeExpirationSeconds(int authCodeExpirationSeconds) {
|
||||||
|
this.authCodeExpirationSeconds = authCodeExpirationSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue