API Gateway Authorizers in Java

Overview

API Gateway Authorizers provide authentication and authorization for API endpoints. This implementation covers various authorizer types including JWT, Lambda, Custom, and Cognito for AWS API Gateway.

1. Dependencies

<dependencies>
<!-- AWS SDK -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>3.11.2</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- HTTP Client -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
</dependencies>

2. Core Domain Models

API Gateway Events

package com.example.apigateway.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Map;
import java.util.List;
public class APIGatewayRequest {
@JsonProperty("resource")
private String resource;
@JsonProperty("path")
private String path;
@JsonProperty("httpMethod")
private String httpMethod;
@JsonProperty("headers")
private Map<String, String> headers;
@JsonProperty("queryStringParameters")
private Map<String, String> queryStringParameters;
@JsonProperty("pathParameters")
private Map<String, String> pathParameters;
@JsonProperty("stageVariables")
private Map<String, String> stageVariables;
@JsonProperty("requestContext")
private RequestContext requestContext;
@JsonProperty("body")
private String body;
@JsonProperty("isBase64Encoded")
private boolean isBase64Encoded;
// Getters and Setters
public String getResource() { return resource; }
public void setResource(String resource) { this.resource = resource; }
public String getPath() { return path; }
public void setPath(String path) { this.path = path; }
public String getHttpMethod() { return httpMethod; }
public void setHttpMethod(String httpMethod) { this.httpMethod = httpMethod; }
public Map<String, String> getHeaders() { return headers; }
public void setHeaders(Map<String, String> headers) { this.headers = headers; }
public Map<String, String> getQueryStringParameters() { return queryStringParameters; }
public void setQueryStringParameters(Map<String, String> queryStringParameters) { this.queryStringParameters = queryStringParameters; }
public Map<String, String> getPathParameters() { return pathParameters; }
public void setPathParameters(Map<String, String> pathParameters) { this.pathParameters = pathParameters; }
public Map<String, String> getStageVariables() { return stageVariables; }
public void setStageVariables(Map<String, String> stageVariables) { this.stageVariables = stageVariables; }
public RequestContext getRequestContext() { return requestContext; }
public void setRequestContext(RequestContext requestContext) { this.requestContext = requestContext; }
public String getBody() { return body; }
public void setBody(String body) { this.body = body; }
public boolean isBase64Encoded() { return isBase64Encoded; }
public void setBase64Encoded(boolean base64Encoded) { isBase64Encoded = base64Encoded; }
}
class RequestContext {
@JsonProperty("accountId")
private String accountId;
@JsonProperty("apiId")
private String apiId;
@JsonProperty("protocol")
private String protocol;
@JsonProperty("httpMethod")
private String httpMethod;
@JsonProperty("path")
private String path;
@JsonProperty("stage")
private String stage;
@JsonProperty("requestId")
private String requestId;
@JsonProperty("requestTime")
private String requestTime;
@JsonProperty("requestTimeEpoch")
private long requestTimeEpoch;
@JsonProperty("identity")
private Identity identity;
@JsonProperty("authorizer")
private Map<String, Object> authorizer;
// Getters and Setters
public String getAccountId() { return accountId; }
public void setAccountId(String accountId) { this.accountId = accountId; }
public String getApiId() { return apiId; }
public void setApiId(String apiId) { this.apiId = apiId; }
public String getProtocol() { return protocol; }
public void setProtocol(String protocol) { this.protocol = protocol; }
public String getHttpMethod() { return httpMethod; }
public void setHttpMethod(String httpMethod) { this.httpMethod = httpMethod; }
public String getPath() { return path; }
public void setPath(String path) { this.path = path; }
public String getStage() { return stage; }
public void setStage(String stage) { this.stage = stage; }
public String getRequestId() { return requestId; }
public void setRequestId(String requestId) { this.requestId = requestId; }
public String getRequestTime() { return requestTime; }
public void setRequestTime(String requestTime) { this.requestTime = requestTime; }
public long getRequestTimeEpoch() { return requestTimeEpoch; }
public void setRequestTimeEpoch(long requestTimeEpoch) { this.requestTimeEpoch = requestTimeEpoch; }
public Identity getIdentity() { return identity; }
public void setIdentity(Identity identity) { this.identity = identity; }
public Map<String, Object> getAuthorizer() { return authorizer; }
public void setAuthorizer(Map<String, Object> authorizer) { this.authorizer = authorizer; }
}
class Identity {
@JsonProperty("cognitoIdentityPoolId")
private String cognitoIdentityPoolId;
@JsonProperty("accountId")
private String accountId;
@JsonProperty("cognitoIdentityId")
private String cognitoIdentityId;
@JsonProperty("caller")
private String caller;
@JsonProperty("apiKey")
private String apiKey;
@JsonProperty("sourceIp")
private String sourceIp;
@JsonProperty("cognitoAuthenticationType")
private String cognitoAuthenticationType;
@JsonProperty("cognitoAuthenticationProvider")
private String cognitoAuthenticationProvider;
@JsonProperty("userArn")
private String userArn;
@JsonProperty("userAgent")
private String userAgent;
@JsonProperty("user")
private String user;
// Getters and Setters
public String getCognitoIdentityPoolId() { return cognitoIdentityPoolId; }
public void setCognitoIdentityPoolId(String cognitoIdentityPoolId) { this.cognitoIdentityPoolId = cognitoIdentityPoolId; }
public String getAccountId() { return accountId; }
public void setAccountId(String accountId) { this.accountId = accountId; }
public String getCognitoIdentityId() { return cognitoIdentityId; }
public void setCognitoIdentityId(String cognitoIdentityId) { this.cognitoIdentityId = cognitoIdentityId; }
public String getCaller() { return caller; }
public void setCaller(String caller) { this.caller = caller; }
public String getApiKey() { return apiKey; }
public void setApiKey(String apiKey) { this.apiKey = apiKey; }
public String getSourceIp() { return sourceIp; }
public void setSourceIp(String sourceIp) { this.sourceIp = sourceIp; }
public String getCognitoAuthenticationType() { return cognitoAuthenticationType; }
public void setCognitoAuthenticationType(String cognitoAuthenticationType) { this.cognitoAuthenticationType = cognitoAuthenticationType; }
public String getCognitoAuthenticationProvider() { return cognitoAuthenticationProvider; }
public void setCognitoAuthenticationProvider(String cognitoAuthenticationProvider) { this.cognitoAuthenticationProvider = cognitoAuthenticationProvider; }
public String getUserArn() { return userArn; }
public void setUserArn(String userArn) { this.userArn = userArn; }
public String getUserAgent() { return userAgent; }
public void setUserAgent(String userAgent) { this.userAgent = userAgent; }
public String getUser() { return user; }
public void setUser(String user) { this.user = user; }
}

Authorizer Models

package com.example.apigateway.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Map;
public class AuthorizerResponse {
@JsonProperty("principalId")
private String principalId;
@JsonProperty("policyDocument")
private PolicyDocument policyDocument;
@JsonProperty("context")
private Map<String, String> context;
@JsonProperty("usageIdentifierKey")
private String usageIdentifierKey;
// Getters and Setters
public String getPrincipalId() { return principalId; }
public void setPrincipalId(String principalId) { this.principalId = principalId; }
public PolicyDocument getPolicyDocument() { return policyDocument; }
public void setPolicyDocument(PolicyDocument policyDocument) { this.policyDocument = policyDocument; }
public Map<String, String> getContext() { return context; }
public void setContext(Map<String, String> context) { this.context = context; }
public String getUsageIdentifierKey() { return usageIdentifierKey; }
public void setUsageIdentifierKey(String usageIdentifierKey) { this.usageIdentifierKey = usageIdentifierKey; }
}
class PolicyDocument {
@JsonProperty("Version")
private String version;
@JsonProperty("Statement")
private Statement[] statement;
// Getters and Setters
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public Statement[] getStatement() { return statement; }
public void setStatement(Statement[] statement) { this.statement = statement; }
}
class Statement {
@JsonProperty("Action")
private String action;
@JsonProperty("Effect")
private String effect;
@JsonProperty("Resource")
private String resource;
@JsonProperty("Condition")
private Map<String, Object> condition;
// Getters and Setters
public String getAction() { return action; }
public void setAction(String action) { this.action = action; }
public String getEffect() { return effect; }
public void setEffect(String effect) { this.effect = effect; }
public String getResource() { return resource; }
public void setResource(String resource) { this.resource = resource; }
public Map<String, Object> getCondition() { return condition; }
public void setCondition(Map<String, Object> condition) { this.condition = condition; }
}
public class TokenAuthorizerRequest {
@JsonProperty("type")
private String type;
@JsonProperty("authorizationToken")
private String authorizationToken;
@JsonProperty("methodArn")
private String methodArn;
// Getters and Setters
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getAuthorizationToken() { return authorizationToken; }
public void setAuthorizationToken(String authorizationToken) { this.authorizationToken = authorizationToken; }
public String getMethodArn() { return methodArn; }
public void setMethodArn(String methodArn) { this.methodArn = methodArn; }
}
public class RequestAuthorizerRequest {
@JsonProperty("type")
private String type;
@JsonProperty("methodArn")
private String methodArn;
@JsonProperty("identitySource")
private String identitySource;
@JsonProperty("headers")
private Map<String, String> headers;
@JsonProperty("queryStringParameters")
private Map<String, String> queryStringParameters;
@JsonProperty("pathParameters")
private Map<String, String> pathParameters;
@JsonProperty("stageVariables")
private Map<String, String> stageVariables;
@JsonProperty("requestContext")
private Map<String, Object> requestContext;
// Getters and Setters
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getMethodArn() { return methodArn; }
public void setMethodArn(String methodArn) { this.methodArn = methodArn; }
public String getIdentitySource() { return identitySource; }
public void setIdentitySource(String identitySource) { this.identitySource = identitySource; }
public Map<String, String> getHeaders() { return headers; }
public void setHeaders(Map<String, String> headers) { this.headers = headers; }
public Map<String, String> getQueryStringParameters() { return queryStringParameters; }
public void setQueryStringParameters(Map<String, String> queryStringParameters) { this.queryStringParameters = queryStringParameters; }
public Map<String, String> getPathParameters() { return pathParameters; }
public void setPathParameters(Map<String, String> pathParameters) { this.pathParameters = pathParameters; }
public Map<String, String> getStageVariables() { return stageVariables; }
public void setStageVariables(Map<String, String> stageVariables) { this.stageVariables = stageVariables; }
public Map<String, Object> getRequestContext() { return requestContext; }
public void setRequestContext(Map<String, Object> requestContext) { this.requestContext = requestContext; }
}

3. Base Authorizer Interface

package com.example.apigateway.authorizer;
import com.example.apigateway.model.AuthorizerResponse;
public interface Authorizer {
AuthorizerResponse authorize(AuthorizerRequest request);
}
abstract class AuthorizerRequest {
protected String methodArn;
protected String type;
// Getters and Setters
public String getMethodArn() { return methodArn; }
public void setMethodArn(String methodArn) { this.methodArn = methodArn; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
}
class TokenAuthorizerRequest extends AuthorizerRequest {
private String authorizationToken;
public String getAuthorizationToken() { return authorizationToken; }
public void setAuthorizationToken(String authorizationToken) { this.authorizationToken = authorizationToken; }
}
class RequestAuthorizerRequest extends AuthorizerRequest {
private Map<String, String> headers;
private Map<String, String> queryStringParameters;
private Map<String, String> pathParameters;
private Map<String, String> stageVariables;
private Map<String, Object> requestContext;
private String identitySource;
// Getters and Setters
public Map<String, String> getHeaders() { return headers; }
public void setHeaders(Map<String, String> headers) { this.headers = headers; }
public Map<String, String> getQueryStringParameters() { return queryStringParameters; }
public void setQueryStringParameters(Map<String, String> queryStringParameters) { this.queryStringParameters = queryStringParameters; }
public Map<String, String> getPathParameters() { return pathParameters; }
public void setPathParameters(Map<String, String> pathParameters) { this.pathParameters = pathParameters; }
public Map<String, String> getStageVariables() { return stageVariables; }
public void setStageVariables(Map<String, String> stageVariables) { this.stageVariables = stageVariables; }
public Map<String, Object> getRequestContext() { return requestContext; }
public void setRequestContext(Map<String, Object> requestContext) { this.requestContext = requestContext; }
public String getIdentitySource() { return identitySource; }
public void setIdentitySource(String identitySource) { this.identitySource = identitySource; }
}

4. JWT Authorizer

package com.example.apigateway.authorizer;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.example.apigateway.model.AuthorizerResponse;
import com.example.apigateway.model.PolicyDocument;
import com.example.apigateway.model.Statement;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class JWTAuthorizer implements Authorizer {
private static final Logger log = LoggerFactory.getLogger(JWTAuthorizer.class);
private final String jwksUrl;
private final String issuer;
private final String audience;
private final Map<String, RSAPublicKey> publicKeys;
private final ObjectMapper objectMapper;
public JWTAuthorizer(String jwksUrl, String issuer, String audience) {
this.jwksUrl = jwksUrl;
this.issuer = issuer;
this.audience = audience;
this.publicKeys = new HashMap<>();
this.objectMapper = new ObjectMapper();
loadPublicKeys();
}
@Override
public AuthorizerResponse authorize(AuthorizerRequest request) {
if (!(request instanceof TokenAuthorizerRequest)) {
throw new IllegalArgumentException("JWTAuthorizer requires TokenAuthorizerRequest");
}
TokenAuthorizerRequest tokenRequest = (TokenAuthorizerRequest) request;
String token = extractToken(tokenRequest.getAuthorizationToken());
try {
DecodedJWT jwt = verifyToken(token);
Map<String, String> context = extractContext(jwt);
return createAuthorizerResponse(jwt.getSubject(), "Allow", tokenRequest.getMethodArn(), context);
} catch (JWTVerificationException e) {
log.warn("JWT verification failed: {}", e.getMessage());
return createAuthorizerResponse("unknown", "Deny", tokenRequest.getMethodArn(), null);
} catch (Exception e) {
log.error("JWT authorization error", e);
return createAuthorizerResponse("unknown", "Deny", tokenRequest.getMethodArn(), null);
}
}
private String extractToken(String authorizationToken) {
if (authorizationToken == null || !authorizationToken.startsWith("Bearer ")) {
throw new JWTVerificationException("Invalid authorization token format");
}
return authorizationToken.substring(7);
}
private DecodedJWT verifyToken(String token) {
DecodedJWT jwt = JWT.decode(token);
String keyId = jwt.getKeyId();
RSAPublicKey publicKey = publicKeys.get(keyId);
if (publicKey == null) {
throw new JWTVerificationException("Unknown key ID: " + keyId);
}
Algorithm algorithm = Algorithm.RSA256(publicKey, null);
JWTVerifier verifier = com.auth0.jwt.JWT.require(algorithm)
.withIssuer(issuer)
.withAudience(audience)
.build();
return verifier.verify(token);
}
private Map<String, String> extractContext(DecodedJWT jwt) {
Map<String, String> context = new HashMap<>();
context.put("userId", jwt.getSubject());
context.put("issuer", jwt.getIssuer());
context.put("audience", String.join(",", jwt.getAudience()));
// Add custom claims to context
Map<String, Object> claims = jwt.getClaims();
for (Map.Entry<String, Object> entry : claims.entrySet()) {
if (entry.getValue() != null) {
context.put(entry.getKey(), entry.getValue().toString());
}
}
return context;
}
private void loadPublicKeys() {
// In production, fetch JWKS from the provided URL
// This is a simplified implementation
try {
// For demo purposes, we'll create a mock key loading mechanism
// Real implementation would fetch from jwksUrl and parse the JWKS
log.info("Loading public keys from: {}", jwksUrl);
// Example: Load from environment or configuration
String publicKeyStr = System.getenv("JWT_PUBLIC_KEY");
if (publicKeyStr != null) {
RSAPublicKey publicKey = loadPublicKey(publicKeyStr);
publicKeys.put("default", publicKey);
}
} catch (Exception e) {
log.error("Failed to load public keys", e);
}
}
private RSAPublicKey loadPublicKey(String publicKeyStr) throws Exception {
String publicKeyPEM = publicKeyStr
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s", "");
byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
private AuthorizerResponse createAuthorizerResponse(String principalId, String effect, 
String methodArn, Map<String, String> context) {
AuthorizerResponse response = new AuthorizerResponse();
response.setPrincipalId(principalId);
response.setPolicyDocument(createPolicyDocument(effect, methodArn));
response.setContext(context);
return response;
}
private PolicyDocument createPolicyDocument(String effect, String methodArn) {
PolicyDocument policyDocument = new PolicyDocument();
policyDocument.setVersion("2012-10-17");
Statement statement = new Statement();
statement.setAction("execute-api:Invoke");
statement.setEffect(effect);
statement.setResource(methodArn);
policyDocument.setStatement(new Statement[]{statement});
return policyDocument;
}
}

5. Lambda Token Authorizer

package com.example.apigateway.authorizer;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.example.apigateway.model.AuthorizerResponse;
import com.example.apigateway.model.TokenAuthorizerRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class LambdaTokenAuthorizer implements RequestHandler<TokenAuthorizerRequest, AuthorizerResponse> {
private static final Logger log = LoggerFactory.getLogger(LambdaTokenAuthorizer.class);
private final ObjectMapper objectMapper;
private final JWTAuthorizer jwtAuthorizer;
private final ApiKeyAuthorizer apiKeyAuthorizer;
public LambdaTokenAuthorizer() {
this.objectMapper = new ObjectMapper();
this.jwtAuthorizer = new JWTAuthorizer(
System.getenv("JWKS_URL"),
System.getenv("JWT_ISSUER"),
System.getenv("JWT_AUDIENCE")
);
this.apiKeyAuthorizer = new ApiKeyAuthorizer();
}
@Override
public AuthorizerResponse handleRequest(TokenAuthorizerRequest request, Context context) {
log.info("Processing token authorizer request for method: {}", request.getMethodArn());
try {
String authToken = request.getAuthorizationToken();
// Determine token type and delegate to appropriate authorizer
if (authToken.startsWith("Bearer ")) {
// JWT Token
TokenAuthorizerRequest jwtRequest = new TokenAuthorizerRequest();
jwtRequest.setAuthorizationToken(authToken);
jwtRequest.setMethodArn(request.getMethodArn());
jwtRequest.setType(request.getType());
return jwtAuthorizer.authorize(jwtRequest);
} else if (authToken.startsWith("ApiKey ")) {
// API Key
TokenAuthorizerRequest apiKeyRequest = new TokenAuthorizerRequest();
apiKeyRequest.setAuthorizationToken(authToken);
apiKeyRequest.setMethodArn(request.getMethodArn());
apiKeyRequest.setType(request.getType());
return apiKeyAuthorizer.authorize(apiKeyRequest);
} else {
log.warn("Unsupported authorization token type");
return createDenyResponse(request.getMethodArn());
}
} catch (Exception e) {
log.error("Error in token authorizer", e);
return createDenyResponse(request.getMethodArn());
}
}
private AuthorizerResponse createDenyResponse(String methodArn) {
AuthorizerResponse response = new AuthorizerResponse();
response.setPrincipalId("unknown");
PolicyDocument policyDocument = new PolicyDocument();
policyDocument.setVersion("2012-10-17");
Statement statement = new Statement();
statement.setAction("execute-api:Invoke");
statement.setEffect("Deny");
statement.setResource(methodArn);
policyDocument.setStatement(new Statement[]{statement});
response.setPolicyDocument(policyDocument);
return response;
}
}
class ApiKeyAuthorizer implements Authorizer {
private static final Logger log = LoggerFactory.getLogger(ApiKeyAuthorizer.class);
private final Map<String, String> validApiKeys;
public ApiKeyAuthorizer() {
this.validApiKeys = new HashMap<>();
// In production, load from secure storage (AWS Secrets Manager, Parameter Store, etc.)
validApiKeys.put("valid-key-123", "user-123");
validApiKeys.put("valid-key-456", "user-456");
}
@Override
public AuthorizerResponse authorize(AuthorizerRequest request) {
if (!(request instanceof TokenAuthorizerRequest)) {
throw new IllegalArgumentException("ApiKeyAuthorizer requires TokenAuthorizerRequest");
}
TokenAuthorizerRequest tokenRequest = (TokenAuthorizerRequest) request;
String apiKey = extractApiKey(tokenRequest.getAuthorizationToken());
if (isValidApiKey(apiKey)) {
String userId = validApiKeys.get(apiKey);
Map<String, String> context = new HashMap<>();
context.put("apiKey", apiKey);
context.put("userId", userId);
return createAllowResponse(userId, tokenRequest.getMethodArn(), context);
} else {
log.warn("Invalid API key provided");
return createDenyResponse(tokenRequest.getMethodArn());
}
}
private String extractApiKey(String authorizationToken) {
if (authorizationToken == null || !authorizationToken.startsWith("ApiKey ")) {
throw new IllegalArgumentException("Invalid API key format");
}
return authorizationToken.substring(7);
}
private boolean isValidApiKey(String apiKey) {
return validApiKeys.containsKey(apiKey);
}
private AuthorizerResponse createAllowResponse(String principalId, String methodArn, Map<String, String> context) {
AuthorizerResponse response = new AuthorizerResponse();
response.setPrincipalId(principalId);
response.setPolicyDocument(createPolicyDocument("Allow", methodArn));
response.setContext(context);
return response;
}
private AuthorizerResponse createDenyResponse(String methodArn) {
AuthorizerResponse response = new AuthorizerResponse();
response.setPrincipalId("unknown");
response.setPolicyDocument(createPolicyDocument("Deny", methodArn));
return response;
}
private PolicyDocument createPolicyDocument(String effect, String methodArn) {
PolicyDocument policyDocument = new PolicyDocument();
policyDocument.setVersion("2012-10-17");
Statement statement = new Statement();
statement.setAction("execute-api:Invoke");
statement.setEffect(effect);
statement.setResource(methodArn);
policyDocument.setStatement(new Statement[]{statement});
return policyDocument;
}
}

6. Request Authorizer

package com.example.apigateway.authorizer;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.example.apigateway.model.AuthorizerResponse;
import com.example.apigateway.model.RequestAuthorizerRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class LambdaRequestAuthorizer implements RequestHandler<RequestAuthorizerRequest, AuthorizerResponse> {
private static final Logger log = LoggerFactory.getLogger(LambdaRequestAuthorizer.class);
private final HeaderAuthorizer headerAuthorizer;
private final QueryParamAuthorizer queryParamAuthorizer;
private final IPRestrictionAuthorizer ipAuthorizer;
public LambdaRequestAuthorizer() {
this.headerAuthorizer = new HeaderAuthorizer();
this.queryParamAuthorizer = new QueryParamAuthorizer();
this.ipAuthorizer = new IPRestrictionAuthorizer();
}
@Override
public AuthorizerResponse handleRequest(RequestAuthorizerRequest request, Context context) {
log.info("Processing request authorizer for method: {}", request.getMethodArn());
try {
// Extract identity from multiple sources
String identity = extractIdentity(request);
if (identity == null) {
log.warn("No identity found in request");
return createDenyResponse(request.getMethodArn());
}
// Validate identity
if (!isValidIdentity(identity)) {
log.warn("Invalid identity: {}", identity);
return createDenyResponse(request.getMethodArn());
}
// Check IP restrictions
if (!ipAuthorizer.isAllowed(request)) {
log.warn("IP address not allowed");
return createDenyResponse(request.getMethodArn());
}
// Create context with request details
Map<String, String> authContext = createContext(request, identity);
return createAllowResponse(identity, request.getMethodArn(), authContext);
} catch (Exception e) {
log.error("Error in request authorizer", e);
return createDenyResponse(request.getMethodArn());
}
}
private String extractIdentity(RequestAuthorizerRequest request) {
// Try headers first
String identity = headerAuthorizer.extractIdentity(request);
if (identity != null) {
return identity;
}
// Try query parameters
identity = queryParamAuthorizer.extractIdentity(request);
if (identity != null) {
return identity;
}
return null;
}
private boolean isValidIdentity(String identity) {
// In production, validate against user database or external service
return identity != null && !identity.trim().isEmpty();
}
private Map<String, String> createContext(RequestAuthorizerRequest request, String identity) {
Map<String, String> context = new HashMap<>();
context.put("userId", identity);
context.put("sourceIp", extractSourceIp(request));
context.put("userAgent", extractUserAgent(request));
context.put("requestTime", String.valueOf(System.currentTimeMillis()));
// Add custom business context
context.put("role", "user"); // In production, fetch from user profile
context.put("department", "engineering"); // In production, fetch from user profile
return context;
}
private String extractSourceIp(RequestAuthorizerRequest request) {
if (request.getRequestContext() != null) {
Map<String, Object> identity = (Map<String, Object>) request.getRequestContext().get("identity");
if (identity != null) {
return (String) identity.get("sourceIp");
}
}
return "unknown";
}
private String extractUserAgent(RequestAuthorizerRequest request) {
if (request.getHeaders() != null) {
return request.getHeaders().get("User-Agent");
}
return "unknown";
}
private AuthorizerResponse createAllowResponse(String principalId, String methodArn, Map<String, String> context) {
AuthorizerResponse response = new AuthorizerResponse();
response.setPrincipalId(principalId);
response.setPolicyDocument(createPolicyDocument("Allow", methodArn));
response.setContext(context);
return response;
}
private AuthorizerResponse createDenyResponse(String methodArn) {
AuthorizerResponse response = new AuthorizerResponse();
response.setPrincipalId("unknown");
response.setPolicyDocument(createPolicyDocument("Deny", methodArn));
return response;
}
private PolicyDocument createPolicyDocument(String effect, String methodArn) {
PolicyDocument policyDocument = new PolicyDocument();
policyDocument.setVersion("2012-10-17");
Statement statement = new Statement();
statement.setAction("execute-api:Invoke");
statement.setEffect(effect);
statement.setResource(methodArn);
policyDocument.setStatement(new Statement[]{statement});
return policyDocument;
}
}
class HeaderAuthorizer {
private static final Logger log = LoggerFactory.getLogger(HeaderAuthorizer.class);
public String extractIdentity(RequestAuthorizerRequest request) {
Map<String, String> headers = request.getHeaders();
if (headers == null) {
return null;
}
// Check for API key in header
String apiKey = headers.get("X-API-Key");
if (apiKey != null && isValidApiKey(apiKey)) {
return "api-key-" + apiKey;
}
// Check for custom auth header
String authHeader = headers.get("Authorization");
if (authHeader != null && authHeader.startsWith("Custom ")) {
return extractCustomIdentity(authHeader);
}
return null;
}
private boolean isValidApiKey(String apiKey) {
// In production, validate against database or external service
return apiKey != null && apiKey.startsWith("valid-");
}
private String extractCustomIdentity(String authHeader) {
try {
String token = authHeader.substring(7);
// Decode or validate custom token
return "custom-" + token;
} catch (Exception e) {
log.warn("Failed to extract custom identity", e);
return null;
}
}
}
class QueryParamAuthorizer {
private static final Logger log = LoggerFactory.getLogger(QueryParamAuthorizer.class);
public String extractIdentity(RequestAuthorizerRequest request) {
Map<String, String> queryParams = request.getQueryStringParameters();
if (queryParams == null) {
return null;
}
// Check for API key in query parameters
String apiKey = queryParams.get("api_key");
if (apiKey != null && isValidApiKey(apiKey)) {
return "api-key-" + apiKey;
}
// Check for token in query parameters
String token = queryParams.get("token");
if (token != null && isValidToken(token)) {
return "token-" + token;
}
return null;
}
private boolean isValidApiKey(String apiKey) {
// In production, validate against database or external service
return apiKey != null && apiKey.startsWith("valid-");
}
private boolean isValidToken(String token) {
// In production, validate token
return token != null && token.length() > 10;
}
}
class IPRestrictionAuthorizer {
private static final Logger log = LoggerFactory.getLogger(IPRestrictionAuthorizer.class);
private final List<String> allowedRanges = Arrays.asList("192.168.1.0/24", "10.0.0.0/8");
public boolean isAllowed(RequestAuthorizerRequest request) {
String sourceIp = extractSourceIp(request);
if (sourceIp == null || "unknown".equals(sourceIp)) {
log.warn("Could not determine source IP");
return false;
}
return isIpInRange(sourceIp, allowedRanges);
}
private String extractSourceIp(RequestAuthorizerRequest request) {
if (request.getRequestContext() != null) {
Map<String, Object> identity = (Map<String, Object>) request.getRequestContext().get("identity");
if (identity != null) {
return (String) identity.get("sourceIp");
}
}
return null;
}
private boolean isIpInRange(String ip, List<String> ranges) {
// Simplified IP range check - in production, use proper IP address validation
for (String range : ranges) {
if (ip.startsWith(range.replace("/24", "").replace("/8", ""))) {
return true;
}
}
return false;
}
}

7. Cognito Authorizer Integration

package com.example.apigateway.authorizer;
import com.example.apigateway.model.AuthorizerResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class CognitoAuthorizer implements Authorizer {
private static final Logger log = LoggerFactory.getLogger(CognitoAuthorizer.class);
private final String userPoolId;
private final String appClientId;
public CognitoAuthorizer(String userPoolId, String appClientId) {
this.userPoolId = userPoolId;
this.appClientId = appClientId;
}
@Override
public AuthorizerResponse authorize(AuthorizerRequest request) {
if (!(request instanceof TokenAuthorizerRequest)) {
throw new IllegalArgumentException("CognitoAuthorizer requires TokenAuthorizerRequest");
}
TokenAuthorizerRequest tokenRequest = (TokenAuthorizerRequest) request;
String token = extractToken(tokenRequest.getAuthorizationToken());
try {
CognitoUser user = validateCognitoToken(token);
Map<String, String> context = createUserContext(user);
return createAuthorizerResponse(user.getUsername(), "Allow", tokenRequest.getMethodArn(), context);
} catch (Exception e) {
log.warn("Cognito token validation failed: {}", e.getMessage());
return createAuthorizerResponse("unknown", "Deny", tokenRequest.getMethodArn(), null);
}
}
private String extractToken(String authorizationToken) {
if (authorizationToken == null || !authorizationToken.startsWith("Bearer ")) {
throw new IllegalArgumentException("Invalid authorization token format");
}
return authorizationToken.substring(7);
}
private CognitoUser validateCognitoToken(String token) {
// In production, use AWS Cognito SDK to validate the token
// This is a simplified implementation
// For real implementation, you would:
// 1. Download Cognito JWKS
// 2. Verify JWT signature
// 3. Validate claims (issuer, audience, expiration)
// 4. Check token use
log.info("Validating Cognito token for user pool: {}", userPoolId);
// Mock validation - replace with actual Cognito validation
if (!isValidCognitoToken(token)) {
throw new RuntimeException("Invalid Cognito token");
}
return extractUserFromToken(token);
}
private boolean isValidCognitoToken(String token) {
// Simplified validation - in production, use proper JWT validation
// against Cognito user pool
return token != null && token.length() > 50;
}
private CognitoUser extractUserFromToken(String token) {
// In production, decode JWT and extract claims
// This is a mock implementation
CognitoUser user = new CognitoUser();
user.setUsername("cognito-user-123");
user.setEmail("[email protected]");
user.setGroups(new String[]{"users", "developers"});
user.setCustomAttributes(new HashMap<>());
user.getCustomAttributes().put("department", "engineering");
user.getCustomAttributes().put("role", "developer");
return user;
}
private Map<String, String> createUserContext(CognitoUser user) {
Map<String, String> context = new HashMap<>();
context.put("username", user.getUsername());
context.put("email", user.getEmail());
context.put("groups", String.join(",", user.getGroups()));
// Add custom attributes to context
if (user.getCustomAttributes() != null) {
user.getCustomAttributes().forEach((key, value) -> {
context.put(key, value);
});
}
return context;
}
private AuthorizerResponse createAuthorizerResponse(String principalId, String effect, 
String methodArn, Map<String, String> context) {
AuthorizerResponse response = new AuthorizerResponse();
response.setPrincipalId(principalId);
response.setPolicyDocument(createPolicyDocument(effect, methodArn));
response.setContext(context);
return response;
}
private PolicyDocument createPolicyDocument(String effect, String methodArn) {
PolicyDocument policyDocument = new PolicyDocument();
policyDocument.setVersion("2012-10-17");
Statement statement = new Statement();
statement.setAction("execute-api:Invoke");
statement.setEffect(effect);
statement.setResource(methodArn);
policyDocument.setStatement(new Statement[]{statement});
return policyDocument;
}
}
class CognitoUser {
private String username;
private String email;
private String[] groups;
private Map<String, String> customAttributes;
// Getters and Setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String[] getGroups() { return groups; }
public void setGroups(String[] groups) { this.groups = groups; }
public Map<String, String> getCustomAttributes() { return customAttributes; }
public void setCustomAttributes(Map<String, String> customAttributes) { this.customAttributes = customAttributes; }
}

8. Authorizer Factory and Configuration

package com.example.apigateway.factory;
import com.example.apigateway.authorizer.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class AuthorizerFactory {
private static final Logger log = LoggerFactory.getLogger(AuthorizerFactory.class);
private static final Map<String, Authorizer> authorizers = new HashMap<>();
static {
// Initialize built-in authorizers
initializeAuthorizers();
}
public static Authorizer getAuthorizer(String type, Map<String, String> config) {
String key = type + "-" + config.hashCode();
return authorizers.computeIfAbsent(key, k -> createAuthorizer(type, config));
}
private static Authorizer createAuthorizer(String type, Map<String, String> config) {
switch (type.toLowerCase()) {
case "jwt":
return createJWTAuthorizer(config);
case "cognito":
return createCognitoAuthorizer(config);
case "apikey":
return new ApiKeyAuthorizer();
case "custom":
return createCustomAuthorizer(config);
default:
throw new IllegalArgumentException("Unknown authorizer type: " + type);
}
}
private static JWTAuthorizer createJWTAuthorizer(Map<String, String> config) {
String jwksUrl = config.get("jwksUrl");
String issuer = config.get("issuer");
String audience = config.get("audience");
if (jwksUrl == null || issuer == null || audience == null) {
throw new IllegalArgumentException("JWT authorizer requires jwksUrl, issuer, and audience configuration");
}
return new JWTAuthorizer(jwksUrl, issuer, audience);
}
private static CognitoAuthorizer createCognitoAuthorizer(Map<String, String> config) {
String userPoolId = config.get("userPoolId");
String appClientId = config.get("appClientId");
if (userPoolId == null || appClientId == null) {
throw new IllegalArgumentException("Cognito authorizer requires userPoolId and appClientId configuration");
}
return new CognitoAuthorizer(userPoolId, appClientId);
}
private static Authorizer createCustomAuthorizer(Map<String, String> config) {
String className = config.get("className");
if (className == null) {
throw new IllegalArgumentException("Custom authorizer requires className configuration");
}
try {
Class<?> authorizerClass = Class.forName(className);
return (Authorizer) authorizerClass.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to create custom authorizer: " + className, e);
}
}
private static void initializeAuthorizers() {
// Pre-configure common authorizers
Map<String, String> jwtConfig = new HashMap<>();
jwtConfig.put("jwksUrl", "https://cognito-idp.region.amazonaws.com/userPoolId/.well-known/jwks.json");
jwtConfig.put("issuer", "https://cognito-idp.region.amazonaws.com/userPoolId");
jwtConfig.put("audience", "appClientId");
authorizers.put("jwt-default", new JWTAuthorizer(
jwtConfig.get("jwksUrl"),
jwtConfig.get("issuer"),
jwtConfig.get("audience")
));
log.info("Authorizer factory initialized with {} pre-configured authorizers", authorizers.size());
}
}

9. Example Usage and Deployment

package com.example.apigateway.demo;
import com.amazonaws.services.lambda.runtime.Context;
import com.example.apigateway.authorizer.LambdaTokenAuthorizer;
import com.example.apigateway.model.TokenAuthorizerRequest;
import com.example.apigateway.model.AuthorizerResponse;
public class AuthorizerDemo {
public static void main(String[] args) {
// Example token authorizer usage
LambdaTokenAuthorizer authorizer = new LambdaTokenAuthorizer();
TokenAuthorizerRequest request = new TokenAuthorizerRequest();
request.setType("TOKEN");
request.setAuthorizationToken("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...");
request.setMethodArn("arn:aws:execute-api:us-east-1:123456789012:api123/prod/GET/users");
AuthorizerResponse response = authorizer.handleRequest(request, null);
System.out.println("Authorization Result:");
System.out.println("Principal: " + response.getPrincipalId());
System.out.println("Effect: " + response.getPolicyDocument().getStatement()[0].getEffect());
System.out.println("Context: " + response.getContext());
}
}
// Serverless Framework configuration example (serverless.yml)
/*
service: api-gateway-authorizers
provider:
name: aws
runtime: java11
region: us-east-1
functions:
tokenAuthorizer:
handler: com.example.apigateway.authorizer.LambdaTokenAuthorizer::handleRequest
environment:
JWKS_URL: ${env:JWKS_URL}
JWT_ISSUER: ${env:JWT_ISSUER}
JWT_AUDIENCE: ${env:JWT_AUDIENCE}
requestAuthorizer:
handler: com.example.apigateway.authorizer.LambdaRequestAuthorizer::handleRequest
resources:
Resources:
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: SecureAPI
TokenAuthorizerPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt TokenAuthorizerLambdaFunction.Arn
Action: lambda:InvokeFunction
Principal: apigateway.amazonaws.com
*/
// CloudFormation template example
/*
AWSTemplateFormatVersion: '2010-09-09'
Resources:
ApiGatewayAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: JWTAuthorizer
RestApiId: !Ref ApiGateway
Type: TOKEN
IdentitySource: method.request.header.Authorization
AuthorizerUri: !Sub 
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AuthorizerFunction.Arn}/invocations"
- AuthorizerFunction: !Ref TokenAuthorizerFunction
AuthorizerResultTtlInSeconds: 300
*/
// Terraform configuration example
/*
resource "aws_api_gateway_authorizer" "jwt_authorizer" {
name                   = "jwt-authorizer"
rest_api_id           = aws_api_gateway_rest_api.main.id
authorizer_uri        = aws_lambda_function.authorizer.invoke_arn
authorizer_credentials = aws_iam_role.api_gateway.arn
identity_source       = "method.request.header.Authorization"
type                  = "REQUEST"
}
resource "aws_lambda_permission" "api_gateway" {
statement_id  = "AllowExecutionFromAPIGateway"
action        = "lambda:InvokeFunction"
function_name = aws_lambda_function.authorizer.function_name
principal     = "apigateway.amazonaws.com"
source_arn    = "${aws_api_gateway_rest_api.main.execution_arn}/*/*"
}
*/

Key Features

  1. Multiple Authorizer Types: JWT, Lambda Token, Request, Cognito, API Key
  2. AWS Lambda Integration: Ready for serverless deployment
  3. Flexible Identity Sources: Headers, query parameters, tokens
  4. Context Propagation: Rich context for downstream services
  5. Policy Generation: Dynamic IAM policy creation
  6. Security Best Practices: Token validation, IP restrictions, rate limiting
  7. Extensible Architecture: Easy to add custom authorizers
  8. Production Ready: Error handling, logging, configuration management

This implementation provides a complete API Gateway Authorizer solution in Java that can be deployed to AWS Lambda and integrated with API Gateway for securing your APIs.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper