HTTP Topic
Authentication and Authorization in HTTP
What You'll Learn:
-
HTTP authentication schemes (Basic, Bearer, Digest)
-
OAuth2 and JWT token-based authentication
-
Implementing security in REST APIs
Authentication and Authorization in HTTP — Detailed Explanation
HTTP is a stateless protocol, meaning it does not remember users from one request to the next.
To protect resources, web apps and APIs need two key mechanisms:
1. Authentication — “Who are you?”
Authentication verifies a user’s identity.
It answers the question:
➡️ “Is this really the person they claim to be?”
Common ways to authenticate in HTTP:
Basic Authentication (Simple but insecure)
Client sends username and password on each request, encoded in Base64.
Header example:
Authorization: Basic dXNlcjpwYXNzd29yZA==
❗ Not secure unless using HTTPS
❗ Password is sent every time
Token-Based Authentication
After logging in, the server gives the client a token.
The client sends this token with each request.
Header:
Authorization: Bearer <token>
Token = proof that the user is authenticated.
JWT (JSON Web Token)
A popular type of token used in REST APIs.
Structure:
header.payload.signature
Contains:
-
User ID
-
Expiration time
-
Permissions
✔ Stateless
✔ No server session storage needed
API Keys (for server-to-server interactions)
Used by systems, not humans.
Header:
x-api-key: 12345abcde
Used when customers call APIs (e.g., OpenAI API keys).
OAuth 2.0 (used by Google, Facebook login)
Allows third-party apps to access your data without sharing your password.
Example:
-
Login with Google
-
App receives an access token
-
App uses token to access user info
2. Authorization — “What can you do?”
Authorization determines what actions or resources an authenticated user is allowed to access.
It answers the question:
“What is this user allowed to do?”
Authorization happens after authentication.
Example Scenario
| Step | Meaning |
|---|---|
| Authentication | User logs in as “alice” |
| Authorization | Alice is allowed to access /profile, but not /admin |
Common Authorization Methods
Role-Based Access Control (RBAC)
Users are assigned roles such as:
-
admin -
editor -
viewer
Authorization checks:
if(user.role == "admin") { allow } else { deny }
Permission-Based Access Control
More granular than roles.
Example permissions:
-
CAN_EDIT_POST -
CAN_DELETE_USER -
CAN_VIEW_DASHBOARD
Roles can contain multiple permissions.
Attribute-Based Access Control (ABAC)
Decisions based on:
-
User attributes (age, department)
-
Resource attributes (file owner)
-
Environment attributes (time of day)
Example rule:
User can access document if user.department == document.department
How Authentication and Authorization Work Together
Step-by-step request flow:
-
User logs in
→ Server authenticates credentials -
Server issues token
→ JWT, session ID, API key -
User sends token with every request
-
Server checks token validity
→ Authentication -
Server checks what user is allowed to do
→ Authorization -
Server returns data or denies access
HTTP Headers Used for Auth
Authorization header
Main header for sending credentials/tokens:
Authorization: Bearer <jwt-token>
Set-Cookie header
For session-based authentication:
Set-Cookie: sessionId=abc123; HttpOnly
Custom headers
x-api-key: <your-api-key>
Detailed Explanation:
HTTP Authentication Schemes
1. Basic Authentication:
# Request
GET /api/protected HTTP/1.1
Authorization: Basic dXNlcjpwYXNzd29yZA==
# The token is base64 encoded "username:password"
2. Bearer Token Authentication:
# Request
GET /api/protected HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# Response for invalid token
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example", error="invalid_token"
3. Digest Authentication:
# Server challenge
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest realm="test", nonce="abc123"
# Client response
Authorization: Digest username="user", realm="test", nonce="abc123",
uri="/api/protected", response="hashed_response"
OAuth2 Flows
Authorization Code Flow:
1. Client redirects to authorization server 2. User authenticates and grants consent 3. Authorization server returns authorization code 4. Client exchanges code for access token 5. Client uses access token to access resources
Client Credentials Flow (Server-to-Server):
POST /oauth/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=client123&client_secret=secret456
Response:
json
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read write"
}
JWT (JSON Web Tokens)
JWT Structure:
Header.Payload.Signature
JWT Payload Example:
json
{
"sub": "1234567890",
"name": "John Doe",
"email": "john@example.com",
"roles": ["USER", "ADMIN"],
"iat": 1516239022,
"exp": 1516242622
}
Using JWT in Requests:
GET /api/protected HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Spring Security Implementation
Security Configuration:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
return http.build();
}
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri("https://idp.example.com/.well-known/jwks.json").build();
}
private Converter<Jwt, AbstractAuthenticationToken> jwtAuthenticationConverter() {
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(jwt -> {
Collection<String> roles = jwt.getClaimAsStringList("roles");
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
});
return converter;
}
}
JWT Token Service:
@Service
public class JwtTokenService {
private final JwtEncoder jwtEncoder;
private final JwtDecoder jwtDecoder;
public String generateToken(User user) {
Instant now = Instant.now();
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer("my-app")
.issuedAt(now)
.expiresAt(now.plus(1, ChronoUnit.HOURS))
.subject(user.getId().toString())
.claim("name", user.getName())
.claim("email", user.getEmail())
.claim("roles", user.getRoles())
.build();
return jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
}
public boolean validateToken(String token) {
try {
Jwt jwt = jwtDecoder.decode(token);
return jwt.getExpiresAt().isAfter(Instant.now());
} catch (Exception e) {
return false;
}
}
}
API Key Authentication
Custom API Key Filter:
@Component
public class ApiKeyAuthFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
String apiKey = getApiKeyFromRequest(request);
if (apiKey != null) {
Authentication auth = authenticateWithApiKey(apiKey);
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(request, response);
}
private String getApiKeyFromRequest(HttpServletRequest request) {
String header = request.getHeader("X-API-Key");
if (header != null) {
return header;
}
String param = request.getParameter("api_key");
if (param != null) {
return param;
}
return null;
}
private Authentication authenticateWithApiKey(String apiKey) {
User user = userService.findByApiKey(apiKey);
if (user == null) {
throw new BadCredentialsException("Invalid API key");
}
return new ApiKeyAuthenticationToken(user, apiKey, user.getAuthorities());
}
}
Rate Limiting with Authentication
Rate Limit Filter:
@Component
public class RateLimitFilter extends OncePerRequestFilter {
private final RateLimiter rateLimiter;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
String clientId = getClientIdentifier(request);
if (!rateLimiter.tryAcquire(clientId)) {
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.getWriter().write("Rate limit exceeded");
return;
}
chain.doFilter(request, response);
}
private String getClientIdentifier(HttpServletRequest request) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated()) {
return auth.getName(); // User ID or username
}
return request.getRemoteAddr(); // IP address as fallback
}
}
Security Headers for APIs
Secure Response Headers:
@Component
public class SecurityHeadersFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
// Prevent MIME sniffing
httpResponse.setHeader("X-Content-Type-Options", "nosniff");
// Prevent clickjacking
httpResponse.setHeader("X-Frame-Options", "DENY");
// Enable HSTS
httpResponse.setHeader("Strict-Transport-Security",
"max-age=31536000; includeSubDomains");
// Content Security Policy
httpResponse.setHeader("Content-Security-Policy",
"default-src 'self'; script-src 'self'");
chain.doFilter(request, response);
}
}
Comments (0)
No comments yet. Be the first to comment!