package com.cygnus.cloud.security; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import java.time.Clock; import java.time.Instant; import java.util.Date; import java.util.Set; import java.util.UUID; import org.springframework.stereotype.Component; @Component class AccessTokenIssuer { private final CommunicationSecurityProperties properties; private final Clock clock; AccessTokenIssuer(CommunicationSecurityProperties properties, Clock clock) { this.properties = properties; this.clock = clock; } TokenResponse issue(MachineClientPrincipal principal, Set scopes) { try { Instant issuedAt = clock.instant(); Instant expiresAt = issuedAt.plus(properties.accessTokenTtl()); JWTClaimsSet claims = new JWTClaimsSet.Builder() .issuer(properties.issuerUri()) .subject(principal.clientId()) .audience(properties.audience()) .issueTime(Date.from(issuedAt)) .expirationTime(Date.from(expiresAt)) .jwtID(UUID.randomUUID().toString()) .claim("client_id", principal.clientId()) .claim("installation_id", principal.installationId()) .claim("installation_uuid", principal.internalInstallationId().toString()) .claim("tenant_id", principal.tenantId().toString()) .claim("license_id", principal.licenseId().toString()) .claim("license_type", principal.licenseType()) .claim("package_code", principal.packageCode()) .claim("security_version", principal.securityVersion()) .claim("scope", String.join(" ", scopes)) .build(); SignedJWT jwt = new SignedJWT( new JWSHeader.Builder(JWSAlgorithm.RS256) .keyID("cygnus-access-token") .build(), claims); jwt.sign(new RSASSASigner( PemKeyLoader.privateKey(properties.accessTokenPrivateKey()))); return new TokenResponse( jwt.serialize(), "Bearer", properties.accessTokenTtl().toSeconds(), String.join(" ", scopes)); } catch (Exception exception) { throw new IllegalStateException("Unable to issue access token", exception); } } }