OAuth 2.0 Grundlagen: Authorization Code & Access Token
OAuth 2.0 ist der Industriestandard für die Delegierung von Autorisierung. Es ermöglicht Anwendungen, im Namen eines Benutzers auf Ressourcen zuzugreifen, ohne die Benutzerkennungen preiszugeben.
Was ist OAuth 2.0?
OAuth 2.0 ist ein Framework für die Autorisierung, das es Drittanwendungen ermöglicht, begrenzten Zugriff auf geschützte Ressourcen zu erhalten, ohne Benutzerkennungen auszutauschen.
Kernkonzepte von OAuth 2.0
- Delegierung: Benutzer delegieren Zugriff an Anwendungen
- Token-basiert: Statt Passwörtern werden Tokens verwendet
- Bereichsbasiert: Zugriff auf definierte Bereiche (Scopes)
- Sicher: Reduziert Angriffsfläche durch Token mit begrenzter Lebensdauer
OAuth 2.0 Rollen
Die vier Hauptrollen
// OAuth 2.0 Rollen als Java Klassen
public class OAuthRoles {
// Resource Owner: Der Benutzer, der die Ressource besitzt
public static class ResourceOwner {
private String userId;
private String username;
private List<String> ownedResources;
public ResourceOwner(String userId, String username) {
this.userId = userId;
this.username = username;
this.ownedResources = new ArrayList<>();
}
public boolean ownsResource(String resourceId) {
return ownedResources.contains(resourceId);
}
// Genehmigt oder lehnt Zugriff ab
public boolean authorizeAccess(String clientId, List<String> scopes) {
// Business Logik für die Genehmigung
return true; // Vereinfacht
}
}
// Resource Server: Hostet die geschützten Ressourcen
public static class ResourceServer {
private Map<String, ProtectedResource> resources;
private TokenValidator tokenValidator;
public ResourceServer() {
this.resources = new HashMap<>();
this.tokenValidator = new JWTTokenValidator();
}
public ProtectedResource getResource(String resourceId, String accessToken) {
if (!tokenValidator.isValid(accessToken)) {
throw new UnauthorizedException("Invalid token");
}
TokenInfo tokenInfo = tokenValidator.getTokenInfo(accessToken);
if (!tokenInfo.hasScope("read")) {
throw new ForbiddenException("Insufficient scope");
}
ProtectedResource resource = resources.get(resourceId);
if (resource == null) {
throw new NotFoundException("Resource not found");
}
return resource;
}
}
// Authorization Server: Authentifiziert den Benutzer und stellt Tokens aus
public static class AuthorizationServer {
private ClientRegistry clientRegistry;
private UserRegistry userRegistry;
private TokenService tokenService;
public AuthorizationServer() {
this.clientRegistry = new ClientRegistry();
this.userRegistry = new UserRegistry();
this.tokenService = new JWTTokenService();
}
public AuthorizationCode generateAuthorizationCode(
String clientId, String userId, List<String> scopes) {
if (!clientRegistry.isValidClient(clientId)) {
throw new InvalidClientException("Unknown client");
}
AuthorizationCode code = new AuthorizationCode(
UUID.randomUUID().toString(),
clientId,
userId,
scopes,
Instant.now().plusSeconds(600) // 10 Minuten gültig
);
return code;
}
public TokenResponse exchangeCodeForTokens(String code, String clientId, String clientSecret) {
AuthorizationCode authCode = validateAuthorizationCode(code, clientId);
if (!clientRegistry.authenticateClient(clientId, clientSecret)) {
throw new InvalidClientException("Authentication failed");
}
// Access Token und Refresh Token erstellen
String accessToken = tokenService.createAccessToken(
authCode.getUserId(),
authCode.getScopes()
);
String refreshToken = tokenService.createRefreshToken(
authCode.getUserId()
);
return new TokenResponse(accessToken, refreshToken, 3600, "Bearer");
}
private AuthorizationCode validateAuthorizationCode(String code, String clientId) {
// Implementierung zur Validierung des Authorization Codes
return new AuthorizationCode(code, clientId, "user123",
Arrays.asList("read", "write"), Instant.now());
}
}
// Client: Die Anwendung, die Zugriff anfordert
public static class Client {
private String clientId;
private String clientSecret;
private List<String> redirectUris;
private List<String> allowedScopes;
public Client(String clientId, String clientSecret) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.redirectUris = new ArrayList<>();
this.allowedScopes = Arrays.asList("read", "write", "profile");
}
public String initiateAuthorizationFlow(List<String> requestedScopes) {
// Authorization Request erstellen
String authUrl = String.format(
"https://auth.example.com/authorize?" +
"response_type=code&" +
"client_id=%s&" +
"redirect_uri=%s&" +
"scope=%s&" +
"state=%s",
clientId,
"https://client.example.com/callback",
String.join(" ", requestedScopes),
UUID.randomUUID().toString()
);
return authUrl;
}
public TokenResponse exchangeCodeForTokens(String code) {
// Token Request an Authorization Server senden
return tokenService.exchangeCode(code, clientId, clientSecret);
}
}
}
Authorization Code Flow
Der sicherste OAuth 2.0 Flow
public class AuthorizationCodeFlow {
// Schritt 1: Authorization Request
public String buildAuthorizationRequest() {
StringBuilder request = new StringBuilder("https://auth.example.com/authorize?");
request.append("response_type=code");
request.append("&client_id=client123");
request.append("&redirect_uri=https://client.example.com/callback");
request.append("&scope=read%20write%20profile");
request.append("&state=xyz123"); // CSRF Protection
return request.toString();
}
// Schritt 2: User Authorization
public void handleUserAuthorization(String userId, String clientId, List<String> scopes) {
// Benutzer wird zur Anmeldeseite weitergeleitet
// Nach erfolgreicher Authentifizierung:
if (userConsentsToScopes(userId, scopes)) {
AuthorizationCode code = authorizationServer.generateAuthorizationCode(
clientId, userId, scopes
);
// Redirect mit Authorization Code
String redirectUri = String.format(
"https://client.example.com/callback?code=%s&state=xyz123",
code.getValue()
);
redirectToClient(redirectUri);
} else {
// Benutzer hat abgelehnt
redirectToClient("https://client.example.com/callback?error=access_denied");
}
}
// Schritt 3: Token Exchange
public TokenResponse exchangeCodeForTokens(String code, String state) {
// State validieren (CSRF Protection)
if (!isValidState(state)) {
throw new SecurityException("Invalid state parameter");
}
// Token Request an Authorization Server
Map<String, String> tokenRequest = new HashMap<>();
tokenRequest.put("grant_type", "authorization_code");
tokenRequest.put("code", code);
tokenRequest.put("redirect_uri", "https://client.example.com/callback");
tokenRequest.put("client_id", "client123");
tokenRequest.put("client_secret", "secret123");
// HTTP POST Request senden
TokenResponse response = httpClient.postForm(
"https://auth.example.com/token",
tokenRequest
);
return response;
}
// Schritt 4: Resource Access
public ProtectedResource accessResource(String accessToken, String resourceId) {
// Access Token im Authorization Header senden
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + accessToken);
return httpClient.get(
"https://api.example.com/resources/" + resourceId,
headers
);
}
}
Spring Security OAuth 2.0 Implementierung
@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/private/**").authenticated()
.anyRequest().denyAll()
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/oauth2/authorization/my-client")
.authorizationEndpoint(authorization -> authorization
.baseUri("/oauth2/authorize")
)
.redirectionEndpoint(redirection -> redirection
.baseUri("/oauth2/callback/*")
)
.userInfoEndpoint(userInfo -> userInfo
.userService(customOAuth2UserService())
)
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.decoder(jwtDecoder())
.jwtAuthenticationConverter(jwtAuthenticationConverter())
)
);
return http.build();
}
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri("https://auth.example.com/.well-known/jwks.json")
.build();
}
@Bean
public Converter<Jwt, UsernamePasswordAuthenticationToken> jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter();
authoritiesConverter.setAuthorityPrefix("ROLE_");
authoritiesConverter.setAuthoritiesClaimName("roles");
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
return converter;
}
@Bean
public OAuth2UserService<OAuth2UserRequest, OAuth2User> customOAuth2UserService() {
return new CustomOAuth2UserService();
}
}
// Custom OAuth2 User Service
@Service
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate =
new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
// Benutzerinformationen verarbeiten und in Datenbank speichern
String provider = userRequest.getClientRegistration().getRegistrationId();
String providerId = oAuth2User.getAttribute("id");
User user = findOrCreateUser(provider, providerId, oAuth2User);
return new CustomOAuth2User(user, oAuth2User.getAttributes());
}
private User findOrCreateUser(String provider, String providerId, OAuth2User oAuth2User) {
// Implementierung zur Benutzererstellung/-suche
return userRepository.findByProviderAndProviderId(provider, providerId)
.orElseGet(() -> createUser(provider, providerId, oAuth2User));
}
private User createUser(String provider, String providerId, OAuth2User oAuth2User) {
User user = new User();
user.setProvider(provider);
user.setProviderId(providerId);
user.setEmail(oAuth2User.getAttribute("email"));
user.setName(oAuth2User.getAttribute("name"));
user.setRoles(Arrays.asList("USER"));
return userRepository.save(user);
}
}
Token Management
JWT Token Implementierung
@Component
public class JWTTokenService {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private int jwtExpiration;
@Value("${jwt.refresh-expiration}")
private int refreshExpiration;
public String createAccessToken(String userId, List<String> scopes) {
return createToken(userId, scopes, jwtExpiration);
}
public String createRefreshToken(String userId) {
return createToken(userId, Arrays.asList("refresh"), refreshExpiration);
}
private String createToken(String userId, List<String> scopes, int expiration) {
Instant now = Instant.now();
Instant expiry = now.plusSeconds(expiration);
return Jwts.builder()
.setSubject(userId)
.claim("scopes", scopes)
.claim("type", scopes.contains("refresh") ? "refresh" : "access")
.setIssuedAt(Date.from(now))
.setExpiration(Date.from(expiry))
.signWith(SignatureAlgorithm.HS256, jwtSecret)
.compact();
}
public Claims validateToken(String token) {
try {
return Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException e) {
throw new TokenExpiredException("Token expired");
} catch (JwtException e) {
throw new InvalidTokenException("Invalid token");
}
}
public String refreshToken(String refreshToken) {
Claims claims = validateToken(refreshToken);
if (!"refresh".equals(claims.get("type"))) {
throw new InvalidTokenException("Not a refresh token");
}
String userId = claims.getSubject();
List<String> scopes = Arrays.asList("read", "write"); // Default scopes
return createAccessToken(userId, scopes);
}
}
// Token Controller
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private JWTTokenService tokenService;
@PostMapping("/token")
public ResponseEntity<TokenResponse> exchangeCodeForToken(
@RequestBody TokenRequest tokenRequest) {
try {
// Authorization Code validieren
AuthorizationCode code = validateAuthorizationCode(tokenRequest.getCode());
// Tokens erstellen
String accessToken = tokenService.createAccessToken(
code.getUserId(),
code.getScopes()
);
String refreshToken = tokenService.createRefreshToken(code.getUserId());
TokenResponse response = new TokenResponse(
accessToken,
refreshToken,
3600,
"Bearer"
);
return ResponseEntity.ok(response);
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new ErrorResponse("invalid_grant", e.getMessage()));
}
}
@PostMapping("/refresh")
public ResponseEntity<TokenResponse> refreshToken(
@RequestBody RefreshTokenRequest request) {
try {
String newAccessToken = tokenService.refreshToken(request.getRefreshToken());
TokenResponse response = new TokenResponse(
newAccessToken,
request.getRefreshToken(),
3600,
"Bearer"
);
return ResponseEntity.ok(response);
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new ErrorResponse("invalid_grant", e.getMessage()));
}
}
@PostMapping("/revoke")
public ResponseEntity<Void> revokeToken(@RequestBody RevokeTokenRequest request) {
// Token auf die Blacklist setzen
tokenBlacklist.addToBlacklist(request.getToken());
return ResponseEntity.ok().build();
}
}
Token Blacklist
@Service
public class TokenBlacklist {
private final RedisTemplate<String, String> redisTemplate;
private static final String BLACKLIST_PREFIX = "blacklist:";
public TokenBlacklist(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void addToBlacklist(String token) {
String jti = extractJti(token);
long expiration = extractExpiration(token);
redisTemplate.opsForValue().set(
BLACKLIST_PREFIX + jti,
"revoked",
Duration.ofSeconds(expiration - System.currentTimeMillis() / 1000)
);
}
public boolean isBlacklisted(String token) {
String jti = extractJti(token);
return Boolean.TRUE.equals(redisTemplate.hasKey(BLACKLIST_PREFIX + jti));
}
private String extractJti(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return claims.getId();
}
private long extractExpiration(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return claims.getExpiration().getTime() / 1000;
}
}
OAuth 2.0 Security Best Practices
1. State Parameter (CSRF Protection)
public class StateManager {
public String generateState() {
return UUID.randomUUID().toString();
}
public void storeState(String state, String sessionId) {
// State in Session oder Redis speichern
redisTemplate.opsForValue().set(
"oauth2_state:" + state,
sessionId,
Duration.ofMinutes(10)
);
}
public boolean validateState(String state, String sessionId) {
String storedSessionId = redisTemplate.opsForValue()
.get("oauth2_state:" + state);
if (storedSessionId == null) {
return false; // State abgelaufen oder nicht gefunden
}
if (!storedSessionId.equals(sessionId)) {
return false; // State stimmt nicht überein
}
// State nach Verwendung löschen
redisTemplate.delete("oauth2_state:" + state);
return true;
}
}
2. PKCE (Proof Key for Code Exchange)
public class PKCEManager {
public PKCEPair generatePKCEPair() {
String codeVerifier = generateCodeVerifier();
String codeChallenge = generateCodeChallenge(codeVerifier);
return new PKCEPair(codeVerifier, codeChallenge);
}
private String generateCodeVerifier() {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[32];
random.nextBytes(bytes);
return Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(bytes);
}
private String generateCodeChallenge(String codeVerifier) {
byte[] bytes = codeVerifier.getBytes(StandardCharsets.US_ASCII);
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(bytes);
return Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(digest);
}
public boolean verifyCodeChallenge(String codeVerifier, String codeChallenge) {
String computedChallenge = generateCodeChallenge(codeVerifier);
return computedChallenge.equals(codeChallenge);
}
}
3. Secure Token Storage
@Component
public class SecureTokenStorage {
@Value("${token.encryption.key}")
private String encryptionKey;
public void storeTokens(String sessionId, TokenResponse tokens) {
// Tokens verschlüsseln speichern
String encryptedAccessToken = encrypt(tokens.getAccessToken());
String encryptedRefreshToken = encrypt(tokens.getRefreshToken());
TokenStorage storage = new TokenStorage(
encryptedAccessToken,
encryptedRefreshToken,
Instant.now().plusSeconds(tokens.getExpiresIn())
);
redisTemplate.opsForValue().set(
"tokens:" + sessionId,
storage,
Duration.ofDays(30)
);
}
public TokenResponse getTokens(String sessionId) {
TokenStorage storage = redisTemplate.opsForValue()
.get("tokens:" + sessionId);
if (storage == null) {
return null;
}
String accessToken = decrypt(storage.getAccessToken());
String refreshToken = decrypt(storage.getRefreshToken());
return new TokenResponse(
accessToken,
refreshToken,
storage.getExpiresIn(),
"Bearer"
);
}
private String encrypt(String data) {
// Implementierung mit AES
return AESTextEncryption.encrypt(data, encryptionKey);
}
private String decrypt(String encryptedData) {
// Implementierung mit AES
return AESTextEncryption.decrypt(encryptedData, encryptionKey);
}
}
OpenID Connect (OIDC)
OIDC Integration
@Configuration
public class OIDCConfig {
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository,
authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
}
// OIDC User Info Service
@Service
public class OIDCUserInfoService {
public OIDCUserInfo getUserInfo(String accessToken) {
// User Info Endpoint aufrufen
String userInfoUrl = "https://auth.example.com/oauth2/userinfo";
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<OIDCUserInfo> response = restTemplate.exchange(
userInfoUrl,
HttpMethod.GET,
entity,
OIDCUserInfo.class
);
return response.getBody();
}
}
// OIDC User Info DTO
public class OIDCUserInfo {
private String sub;
private String name;
private String email;
private String picture;
private List<String> roles;
// Getter und Setter
public String getSubject() { return sub; }
public String getName() { return name; }
public String getEmail() { return email; }
public String getPicture() { return picture; }
public List<String> getRoles() { return roles; }
}
OAuth 2.0 Client Implementierung
JavaScript Client Beispiel
class OAuth2Client {
constructor(config) {
this.clientId = config.clientId;
this.redirectUri = config.redirectUri;
this.authUrl = config.authUrl;
this.tokenUrl = config.tokenUrl;
this.scopes = config.scopes;
}
// Authorization Request initiieren
initiateAuthorization() {
const state = this.generateState();
const codeVerifier = this.generateCodeVerifier();
const codeChallenge = this.generateCodeChallenge(codeVerifier);
// State und Code Verifier speichern
sessionStorage.setItem('oauth2_state', state);
sessionStorage.setItem('oauth2_code_verifier', codeVerifier);
const authUrl = new URL(this.authUrl);
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('client_id', this.clientId);
authUrl.searchParams.set('redirect_uri', this.redirectUri);
authUrl.searchParams.set('scope', this.scopes.join(' '));
authUrl.searchParams.set('state', state);
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
// Weiterleitung zur Authorization Server
window.location.href = authUrl.toString();
}
// Callback verarbeiten
async handleCallback() {
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');
const storedState = sessionStorage.getItem('oauth2_state');
// State validieren
if (state !== storedState) {
throw new Error('Invalid state parameter');
}
// Code gegen Tokens eintauschen
const codeVerifier = sessionStorage.getItem('oauth2_code_verifier');
const tokenResponse = await this.exchangeCodeForTokens(code, codeVerifier);
// Tokens speichern
localStorage.setItem('access_token', tokenResponse.access_token);
localStorage.setItem('refresh_token', tokenResponse.refresh_token);
// Cleanup
sessionStorage.removeItem('oauth2_state');
sessionStorage.removeItem('oauth2_code_verifier');
return tokenResponse;
}
// Token Exchange
async exchangeCodeForTokens(code, codeVerifier) {
const response = await fetch(this.tokenUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: this.redirectUri,
client_id: this.clientId,
code_verifier: codeVerifier
})
});
if (!response.ok) {
throw new Error('Token exchange failed');
}
return await response.json();
}
// Access Token erneuern
async refreshAccessToken() {
const refreshToken = localStorage.getItem('refresh_token');
const response = await fetch(this.tokenUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: this.clientId
})
});
if (!response.ok) {
throw new Error('Token refresh failed');
}
const tokenResponse = await response.json();
localStorage.setItem('access_token', tokenResponse.access_token);
return tokenResponse;
}
// API Request mit Access Token
async makeAuthenticatedRequest(url, options = {}) {
let accessToken = localStorage.getItem('access_token');
// Token prüfen und ggf. erneuern
if (this.isTokenExpired(accessToken)) {
await this.refreshAccessToken();
accessToken = localStorage.getItem('access_token');
}
const headers = {
'Authorization': `Bearer ${accessToken}`,
...options.headers
};
return fetch(url, {
...options,
headers
});
}
// Hilfsmethoden
generateState() {
return Math.random().toString(36).substring(2, 15);
}
generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return btoa(String.fromCharCode.apply(null, array))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
async generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const digest = await crypto.subtle.digest('SHA-256', data);
return btoa(String.fromCharCode.apply(null, new Uint8Array(digest)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
isTokenExpired(token) {
try {
const payload = JSON.parse(atob(token.split('.')[1]));
return Date.now() >= payload.exp * 1000;
} catch {
return true;
}
}
}
// Verwendung
const oauth2Client = new OAuth2Client({
clientId: 'your-client-id',
redirectUri: 'http://localhost:3000/callback',
authUrl: 'https://auth.example.com/oauth2/authorize',
tokenUrl: 'https://auth.example.com/oauth2/token',
scopes: ['read', 'write', 'profile']
});
// Login initiieren
document.getElementById('login-btn').addEventListener('click', () => {
oauth2Client.initiateAuthorization();
});
// API Request
document.getElementById('fetch-data-btn').addEventListener('click', async () => {
try {
const response = await oauth2Client.makeAuthenticatedRequest(
'https://api.example.com/user/profile'
);
const data = await response.json();
console.log('User data:', data);
} catch (error) {
console.error('API request failed:', error);
}
});
Prüfungsrelevante Konzepte
Wichtige OAuth 2.0 Flows
- Authorization Code Flow: Sicherste Methode für Web-Apps
- Implicit Flow: Veraltet, nicht mehr empfohlen
- Client Credentials Flow: Für Machine-to-Machine Kommunikation
- Resource Owner Password Credentials: Nur für vertrauenswürdige Anwendungen
Sicherheitsaspekte
- State Parameter: CSRF Protection
- PKCE: Schutz gegen Code Interception
- Token Storage: Sichere Speicherung von Tokens
- HTTPS: Verschlüsselte Kommunikation erforderlich
- Scope Limitation: Minimale Berechtigungen anfordern
Typische Prüfungsaufgaben
- Erklären Sie den Authorization Code Flow
- Vergleichen Sie OAuth 2.0 mit OpenID Connect
- Implementieren Sie einen einfachen OAuth 2.0 Client
- Beschreiben Sie Sicherheitsrisiken und Gegenmaßnahmen
Zusammenfassung
OAuth 2.0 ist ein mächtiges Framework für die Delegierung von Autorisierung:
- Sicher: Token-basierte Authentifizierung statt Passwörter
- Flexibel: Verschiedene Flows für unterschiedliche Anwendungsfälle
- Standardisiert: Weit verbreitet und gut unterstützt
- Erweiterbar: OpenID Connect für Identitätsmanagement
Die richtige Implementierung erfordert sorgfältige Beachtung von Sicherheitsaspekten wie State Parameters, PKCE und sicherer Token Storage.