use of com.yahoo.athenz.auth.token.IdToken in project athenz by yahoo.
the class ZTSImpl method getOIDCResponse.
@Override
public Response getOIDCResponse(ResourceContext ctx, String responseType, String clientId, String redirectUri, String scope, String state, String nonce, String keyType) {
final String caller = ctx.getApiName();
final String principalDomain = logPrincipalAndGetDomain(ctx);
validateRequest(ctx.request(), principalDomain, caller);
validate(nonce, TYPE_ENTITY_NAME, principalDomain, caller);
validate(clientId, TYPE_SERVICE_NAME, principalDomain, caller);
if (!StringUtil.isEmpty(state)) {
validate(state, TYPE_ENTITY_NAME, principalDomain, caller);
}
if (!StringUtil.isEmpty(keyType)) {
validate(keyType, TYPE_SIMPLE_NAME, principalDomain, caller);
}
clientId = clientId.toLowerCase();
// get our principal's name
final Principal principal = ((RsrcCtxWrapper) ctx).principal();
String principalName = principal.getFullName();
// verify we have a valid client id
final String domainName = AthenzUtils.extractPrincipalDomainName(clientId);
if (domainName == null) {
throw requestError("Invalid client id", caller, principal.getDomain(), principalDomain);
}
setRequestDomain(ctx, domainName);
// first retrieve our domain data object from the cache
DataCache data = dataStore.getDataCache(domainName);
if (data == null) {
setRequestDomain(ctx, ZTSConsts.ZTS_UNKNOWN_DOMAIN);
throw notFoundError("No such domain: " + domainName, caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain);
}
final String serviceEndpoint = extractServiceEndpoint(data.getDomainData(), clientId);
if (StringUtil.isEmpty(serviceEndpoint)) {
throw requestError("Invalid Service or empty service endpoint", caller, principal.getDomain(), principalDomain);
}
if (!serviceEndpoint.equalsIgnoreCase(redirectUri)) {
throw requestError("Unregistered redirect uri", caller, principal.getDomain(), principalDomain);
}
if (!ZTSConsts.ZTS_OPENID_RESPONSE_IT_ONLY.equals(responseType)) {
return oidcError("invalid response type", redirectUri, state);
}
if (StringUtil.isEmpty(scope)) {
return oidcError("no scope provided", redirectUri, state);
}
// our scopes are space separated list of values. Any groups
// scopes are preferred over any role scopes
IdTokenRequest tokenRequest = new IdTokenRequest(scope);
if (tokenRequest.getDomainName() != null && !domainName.equalsIgnoreCase(tokenRequest.getDomainName())) {
return oidcError("domain name mismatch", redirectUri, state);
}
// check if the authorized service domain matches to the
// requested domain name
checkRoleTokenAuthorizedServiceRequest(principal, domainName, caller);
// validate principal object to make sure we're not
// processing a role identity, and instead we require
// a service identity
validatePrincipalNotRoleIdentity(principal, caller);
// now let's process our requests and see if we need to extract
// either groups or roles for our response
List<String> groups = null;
if (tokenRequest.isGroupsScope()) {
Set<String> groupNames = tokenRequest.getGroupNames();
if (groupNames != null) {
for (String groupName : groupNames) {
validate(groupName, TYPE_ENTITY_NAME, principalDomain, caller);
}
}
// process our request and retrieve the groups for the principal
groups = dataStore.getPrincipalGroups(principalName, domainName, groupNames);
if (groupNames != null && groups == null) {
return oidcError("principal not included in requested groups", redirectUri, state);
}
} else if (tokenRequest.isRolesScope()) {
String[] requestedRoles = tokenRequest.getRoleNames();
if (requestedRoles != null) {
for (String requestedRole : requestedRoles) {
validate(requestedRole, TYPE_ENTITY_NAME, principalDomain, caller);
}
}
// process our request and retrieve the roles for the principal
Set<String> roles = new HashSet<>();
dataStore.getAccessibleRoles(data, domainName, principalName, requestedRoles, roles, false);
if (requestedRoles != null && roles.isEmpty()) {
return oidcError("principal not included in requested roles", redirectUri, state);
}
groups = new ArrayList<>(roles);
}
long iat = System.currentTimeMillis() / 1000;
IdToken idToken = new IdToken();
idToken.setVersion(1);
idToken.setAudience(clientId);
idToken.setSubject(principalName);
idToken.setIssuer(ztsOpenIDIssuer);
idToken.setNonce(nonce);
idToken.setGroups(groups);
idToken.setIssueTime(iat);
idToken.setAuthTime(iat);
idToken.setExpiryTime(iat + idTokenDefaultTimeout);
ServerPrivateKey signPrivateKey = getSignPrivateKey(keyType);
String location = redirectUri + "#id_token=" + idToken.getSignedToken(signPrivateKey.getKey(), signPrivateKey.getId(), signPrivateKey.getAlgorithm());
if (!StringUtil.isEmpty(state)) {
location += "&state=" + state;
}
return Response.status(ResourceException.FOUND).header("Location", location).build();
}
use of com.yahoo.athenz.auth.token.IdToken in project athenz by yahoo.
the class ZTSClientTokenCacher method extractIdTokenServiceName.
private static String extractIdTokenServiceName(final String token) {
if (token == null) {
return null;
}
final String tokenWithoutSignature = removeSignature(token);
IdToken idToken;
try {
idToken = new IdToken(tokenWithoutSignature, (JwtsSigningKeyResolver) null);
} catch (Exception ex) {
LOG.error("ZTSTokenCache: unable to parse id token", ex);
return null;
}
final String fullServiceName = idToken.getAudience();
if (fullServiceName == null) {
LOG.error("ZTSTokenCache: token has no audience");
return null;
}
int index = fullServiceName.lastIndexOf('.');
if (index == -1) {
LOG.error("ZTSTokenCache: invalid id token audience - {}", fullServiceName);
return null;
}
return fullServiceName.substring(index + 1);
}
use of com.yahoo.athenz.auth.token.IdToken in project athenz by yahoo.
the class ZTSImpl method postAccessTokenRequest.
@Override
public AccessTokenResponse postAccessTokenRequest(ResourceContext ctx, String request) {
final String caller = ctx.getApiName();
final String principalDomain = logPrincipalAndGetDomain(ctx);
validateRequest(ctx.request(), principalDomain, caller);
// get our principal's name
final Principal principal = ((RsrcCtxWrapper) ctx).principal();
String principalName = principal.getFullName();
if (StringUtil.isEmpty(request)) {
throw requestError("Empty request body", caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain);
}
// we want to log the request body in our access log so
// we know what is the client asking for but we'll just
// limit the request up to 1K
ctx.request().setAttribute(ACCESS_LOG_ADDL_QUERY, getQueryLogData(request));
// decode and store the attributes that could exist in our
// request body
String grantType = null;
String scope = null;
String proxyForPrincipal = null;
String authzDetails = null;
List<String> proxyPrincipalsSpiffeUris = null;
int expiryTime = 0;
boolean useOpenIDIssuer = false;
String[] comps = request.split("&");
for (String comp : comps) {
int idx = comp.indexOf('=');
if (idx == -1) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("postAccessTokenRequest: skipping invalid component: {}", comp);
}
continue;
}
final String key = decodeString(comp.substring(0, idx));
if (key == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("postAccessTokenRequest: skipping invalid component: {}", comp);
}
continue;
}
final String value = decodeString(comp.substring(idx + 1));
if (value == null) {
continue;
}
switch(key) {
case KEY_GRANT_TYPE:
grantType = value.toLowerCase();
break;
case KEY_SCOPE:
scope = value.toLowerCase();
break;
case KEY_EXPIRES_IN:
expiryTime = ZTSUtils.parseInt(value, 0);
break;
case KEY_PROXY_FOR_PRINCIPAL:
proxyForPrincipal = getProxyForPrincipalValue(value.toLowerCase(), principalName, principalDomain, caller);
break;
case KEY_AUTHORIZATION_DETAILS:
authzDetails = value;
break;
case KEY_PROXY_PRINCIPAL_SPIFFE_URIS:
proxyPrincipalsSpiffeUris = getProxyPrincipalSpiffeUris(value.toLowerCase(), principalDomain, caller);
break;
case KEY_OPENID_ISSUER:
useOpenIDIssuer = Boolean.parseBoolean(value);
break;
}
}
if (!OAUTH_GRANT_CREDENTIALS.equals(grantType)) {
throw requestError("Invalid grant request: " + grantType, caller, principal.getDomain(), principalDomain);
}
if (scope == null || scope.isEmpty()) {
throw requestError("Invalid request: no scope provided", caller, principal.getDomain(), principalDomain);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("postAccessTokenRequest(principal: {}, grant-type: {}, scope: {}, expires-in: {}, proxy-for-principal: {})", principalName, grantType, scope, expiryTime, proxyForPrincipal);
}
// our scopes are space separated list of values
AccessTokenRequest tokenRequest = new AccessTokenRequest(scope);
// before using any of our values let's validate that they
// match our schema
final String domainName = tokenRequest.getDomainName();
setRequestDomain(ctx, domainName);
validate(domainName, TYPE_DOMAIN_NAME, principalDomain, caller);
String[] requestedRoles = tokenRequest.getRoleNames();
if (requestedRoles != null) {
for (String requestedRole : requestedRoles) {
validate(requestedRole, TYPE_ENTITY_NAME, principalDomain, caller);
}
}
// first retrieve our domain data object from the cache
DataCache data = dataStore.getDataCache(domainName);
if (data == null) {
setRequestDomain(ctx, ZTSConsts.ZTS_UNKNOWN_DOMAIN);
throw notFoundError("No such domain: " + domainName, caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain);
}
// if we're given authorization details to be included in the
// token then we must have only role requested and we need
// to make sure the requested fields are valid according
// to our configured authorization details entity for the role
validateAuthorizationDetails(authzDetails, requestedRoles, data, caller, domainName, principalDomain);
// check if the authorized service domain matches to the
// requested domain name
checkRoleTokenAuthorizedServiceRequest(principal, domainName, caller);
// process our request and retrieve the roles for the principal
Set<String> roles = new HashSet<>();
dataStore.getAccessibleRoles(data, domainName, principalName, requestedRoles, roles, false);
if (roles.isEmpty()) {
throw forbiddenError(tokenErrorMessage(caller, principalName, domainName, requestedRoles), caller, domainName, principalDomain);
}
// if this is proxy for operation then we want to make sure that
// both principals have access to the same set of roles so we'll
// remove any roles that are authorized by only one of the principals
String proxyUser = null;
if (proxyForPrincipal != null) {
if (tokenRequest.isOpenIdScope()) {
throw requestError("Proxy Principal cannot request id tokens", caller, domainName, principalDomain);
}
// process the role lookup for the proxy principal
Set<String> rolesForProxy = new HashSet<>();
dataStore.getAccessibleRoles(data, domainName, proxyForPrincipal, requestedRoles, rolesForProxy, false);
roles.retainAll(rolesForProxy);
if (roles.isEmpty()) {
throw forbiddenError(tokenErrorMessage(caller, proxyForPrincipal, domainName, requestedRoles), caller, domainName, principalDomain);
}
// we need to switch our principal and proxy for user
proxyUser = principalName;
principalName = proxyForPrincipal;
}
if (!isPrincipalRoleCertificateAccessValid(principal, domainName, roles)) {
throw forbiddenError("Role based Principal does not include all roles", caller, domainName, principalDomain);
}
long tokenTimeout = determineTokenTimeout(data, roles, null, expiryTime);
long iat = System.currentTimeMillis() / 1000;
AccessToken accessToken = new AccessToken();
accessToken.setVersion(1);
accessToken.setJwtId(UUID.randomUUID().toString());
accessToken.setAudience(domainName);
accessToken.setClientId(principalName);
accessToken.setIssueTime(iat);
accessToken.setAuthTime(iat);
accessToken.setExpiryTime(iat + tokenTimeout);
accessToken.setUserId(principalName);
accessToken.setSubject(principalName);
accessToken.setIssuer(useOpenIDIssuer ? ztsOpenIDIssuer : ztsOAuthIssuer);
accessToken.setProxyPrincipal(proxyUser);
accessToken.setScope(new ArrayList<>(roles));
accessToken.setAuthorizationDetails(authzDetails);
// if we have a certificate used for mTLS authentication then
// we're going to bind the certificate to the access token
// and the optional proxy principals if specified
X509Certificate cert = principal.getX509Certificate();
if (cert != null) {
accessToken.setConfirmX509CertHash(cert);
if (proxyPrincipalsSpiffeUris != null) {
accessToken.setConfirmProxyPrincipalSpiffeUris(proxyPrincipalsSpiffeUris);
}
}
String accessJwts = accessToken.getSignedToken(privateKey.getKey(), privateKey.getId(), privateKey.getAlgorithm());
// now let's check to see if we need to create openid token
String idJwts = null;
if (tokenRequest.isOpenIdScope()) {
final String serviceName = tokenRequest.getServiceName();
validate(serviceName, TYPE_SIMPLE_NAME, principalDomain, caller);
IdToken idToken = new IdToken();
idToken.setVersion(1);
idToken.setAudience(tokenRequest.getDomainName() + "." + serviceName);
idToken.setSubject(principalName);
idToken.setIssuer(useOpenIDIssuer ? ztsOpenIDIssuer : ztsOAuthIssuer);
// id tokens are only valid for up to 12 hours max
// (value configured as a system property).
// we'll use the user specified timeout unless it's
// over the configured max
idToken.setIssueTime(iat);
idToken.setAuthTime(iat);
idToken.setExpiryTime(iat + determineIdTokenTimeout(tokenTimeout));
idJwts = idToken.getSignedToken(privateKey.getKey(), privateKey.getId(), privateKey.getAlgorithm());
}
AccessTokenResponse response = new AccessTokenResponse().setAccess_token(accessJwts).setToken_type(OAUTH_BEARER_TOKEN).setExpires_in((int) tokenTimeout).setId_token(idJwts);
if (tokenRequest.sendScopeResponse() || requestedRoles != null && requestedRoles.length != roles.size()) {
List<String> domainRoles = new ArrayList<>();
for (String role : roles) {
domainRoles.add(domainName + AccessTokenRequest.OBJECT_ROLE + role);
}
if (tokenRequest.isOpenIdScope()) {
domainRoles.add(AccessTokenRequest.OBJECT_OPENID);
}
response.setScope(String.join(" ", domainRoles));
}
return response;
}
Aggregations