@Post
  public Representation token(final Representation entity) throws OAuth2RestletException {

    final OAuth2Request request = requestFactory.create(getRequest());

    try {
      final AccessToken accessToken = accessTokenService.requestAccessToken(request);
      return new JacksonRepresentation<Map<String, Object>>(accessToken.toMap());
    } catch (InvalidGrantException e) {
      throw new OAuth2RestletException(
          e.getStatusCode(),
          e.getError(),
          "Assertion is invalid.",
          request.<String>getParameter("redirect_uri"),
          request.<String>getParameter("state"));
    } catch (ClientAuthenticationFailedException e) {
      getResponse()
          .setChallengeRequests(
              singletonList(
                  new ChallengeRequest(
                      ChallengeScheme.valueOf(
                          SUPPORTED_RESTLET_CHALLENGE_SCHEMES.get(e.getChallengeScheme())),
                      e.getChallengeRealm())));
      throw new OAuth2RestletException(
          e.getStatusCode(), e.getError(), e.getMessage(), request.<String>getParameter("state"));
    } catch (OAuth2Exception e) {
      throw new OAuth2RestletException(
          e.getStatusCode(),
          e.getError(),
          e.getMessage(),
          request.<String>getParameter("redirect_uri"),
          request.<String>getParameter("state"));
    }
  }
  /** For at_hash values, used when token and id_token exist in scope. */
  private String generateAtHash(
      String algorithm, OAuth2Request request, OAuth2ProviderSettings providerSettings)
      throws ServerException {

    final AccessToken accessToken = request.getToken(AccessToken.class);

    if (accessToken == null) {
      logger.message("at_hash generation requires an existing access_token.");
      return null;
    }

    final String accessTokenValue =
        ((String) accessToken.getTokenInfo().get(OAuth2Constants.Params.ACCESS_TOKEN));

    return generateHash(algorithm, accessTokenValue, providerSettings);
  }
  private boolean isEntitled(
      UmaProviderSettings umaProviderSettings,
      PermissionTicket permissionTicket,
      AccessToken authorisationApiToken)
      throws EntitlementException, ServerException {
    String realm = permissionTicket.getRealm();
    String resourceSetId = permissionTicket.getResourceSetId();
    String resourceName = UmaConstants.UMA_POLICY_SCHEME;
    Subject resourceOwnerSubject;
    try {
      ResourceSetStore store =
          oauth2ProviderSettingsFactory
              .get(requestFactory.create(getRequest()))
              .getResourceSetStore();
      Set<ResourceSetDescription> results =
          store.query(
              org.forgerock.util.query.QueryFilter.equalTo(
                  ResourceSetTokenField.RESOURCE_SET_ID, resourceSetId));
      if (results.size() != 1) {
        throw new NotFoundException("Could not find Resource Set, " + resourceSetId);
      }
      resourceName += results.iterator().next().getId();
      resourceOwnerSubject =
          UmaUtils.createSubject(
              createIdentity(results.iterator().next().getResourceOwnerId(), realm));
    } catch (NotFoundException e) {
      debug.message("Couldn't find resource that permission ticket is registered for", e);
      throw new ServerException("Couldn't find resource that permission ticket is registered for");
    }
    Subject requestingPartySubject =
        UmaUtils.createSubject(createIdentity(authorisationApiToken.getResourceOwnerId(), realm));

    // Implicitly grant access to the resource owner
    if (isRequestingPartyResourceOwner(requestingPartySubject, resourceOwnerSubject)) {
      return true;
    }

    List<Entitlement> entitlements =
        umaProviderSettings
            .getPolicyEvaluator(
                requestingPartySubject, permissionTicket.getClientId().toLowerCase())
            .evaluate(realm, requestingPartySubject, resourceName, null, false);

    Set<String> requestedScopes = permissionTicket.getScopes();
    Set<String> requiredScopes = new HashSet<String>(requestedScopes);
    for (Entitlement entitlement : entitlements) {
      for (String requestedScope : requestedScopes) {
        final Boolean actionValue = entitlement.getActionValue(requestedScope);
        if (actionValue != null && actionValue) {
          requiredScopes.remove(requestedScope);
        }
      }
    }

    return requiredScopes.isEmpty();
  }
    /** {@inheritDoc} */
    public void additionalDataToReturnFromTokenEndpoint(
        AccessToken accessToken, OAuth2Request request)
        throws ServerException, InvalidClientException, NotFoundException {

      final Map<String, String> data = new HashMap<String, String>();
      data.put("nonce", accessToken.getNonce());
      data.put(
          OAuth2Constants.Custom.SSO_TOKEN_ID,
          getSsoToken(ServletUtils.getRequest(request.<Request>getRequest())));

      final Map<String, Object> tokenEntries =
          scopeValidator.extraDataToReturnForTokenEndpoint(
              data, new LegacyAccessTokenAdapter(accessToken));

      if (tokenEntries != null) {
        for (final Map.Entry<String, Object> tokenEntry : tokenEntries.entrySet()) {
          accessToken.addExtraData(tokenEntry.getKey(), (String) tokenEntry.getValue());
        }
      }
    }
  // See spec section 5.5. - add claims to id_token based on 'claims' parameter in the access token
  private void appendRequestedIdTokenClaims(
      OAuth2Request request,
      OAuth2ProviderSettings providerSettings,
      OpenAMOpenIdConnectToken oidcToken)
      throws ServerException, NotFoundException, InvalidClientException {

    AccessToken accessToken = request.getToken(AccessToken.class);
    String claims;
    if (accessToken != null) {
      claims = (String) accessToken.toMap().get(OAuth2Constants.Custom.CLAIMS);
    } else {
      claims = request.getParameter(OAuth2Constants.Custom.CLAIMS);
    }

    if (claims != null) {
      try {
        JSONObject claimsObject = new JSONObject(claims);
        JSONObject idTokenClaimsRequest =
            claimsObject.getJSONObject(OAuth2Constants.JWTTokenParams.ID_TOKEN);
        Map<String, Object> userInfo =
            providerSettings.getUserInfo(accessToken, request).getValues();

        Iterator<String> it = idTokenClaimsRequest.keys();
        while (it.hasNext()) {
          String keyName = it.next();

          if (userInfo.containsKey(keyName)) {
            oidcToken.put(keyName, userInfo.get(keyName));
          }
        }
      } catch (UnauthorizedClientException e) {
        throw new InvalidClientException(e.getMessage());
      } catch (JSONException e) {
        // if claims object not found, fall through
      }
    }
  }
 public void updateAccessToken(AccessToken accessToken) {
   try {
     deleteAccessToken(accessToken.getTokenId());
     tokenStore.create(accessToken);
   } catch (ServerException e) {
     logger.error(
         "DefaultOAuthTokenStoreImpl::Unable to delete access token " + accessToken.getTokenId(),
         e);
     throw new OAuthProblemException(
         Status.SERVER_ERROR_INTERNAL.getCode(),
         "Internal error",
         "Could not delete token in CTS",
         null);
   } catch (CoreTokenException e) {
     logger.error(
         "DefaultOAuthTokenStoreImpl::Unable to create access token " + accessToken.getTokenId(),
         e);
     throw new OAuthProblemException(
         Status.SERVER_ERROR_INTERNAL.getCode(),
         "Internal error",
         "Could not create token in CTS",
         null);
   }
 }
  /** {@inheritDoc} */
  public AccessToken createAccessToken(
      String grantType,
      String accessTokenType,
      String authorizationCode,
      String resourceOwnerId,
      String clientId,
      String redirectUri,
      Set<String> scope,
      RefreshToken refreshToken,
      String nonce,
      String claims,
      OAuth2Request request)
      throws ServerException, NotFoundException {

    OpenIdConnectClientRegistration clientRegistration = getClientRegistration(clientId, request);

    final OAuth2ProviderSettings providerSettings = providerSettingsFactory.get(request);
    final String id = UUID.randomUUID().toString();
    final String auditId = UUID.randomUUID().toString();

    String realm = realmNormaliser.normalise(request.<String>getParameter(REALM));

    long expiryTime = 0;
    if (clientRegistration == null) {
      expiryTime = providerSettings.getAccessTokenLifetime() + System.currentTimeMillis();
    } else {
      expiryTime =
          clientRegistration.getAccessTokenLifeTime(providerSettings) + System.currentTimeMillis();
    }

    final AccessToken accessToken;
    if (refreshToken == null) {
      accessToken =
          new OpenAMAccessToken(
              id,
              authorizationCode,
              resourceOwnerId,
              clientId,
              redirectUri,
              scope,
              expiryTime,
              null,
              OAuth2Constants.Token.OAUTH_ACCESS_TOKEN,
              grantType,
              nonce,
              realm,
              claims,
              auditId);
    } else {
      accessToken =
          new OpenAMAccessToken(
              id,
              authorizationCode,
              resourceOwnerId,
              clientId,
              redirectUri,
              scope,
              expiryTime,
              refreshToken.getTokenId(),
              OAuth2Constants.Token.OAUTH_ACCESS_TOKEN,
              grantType,
              nonce,
              realm,
              claims,
              auditId);
    }
    try {
      tokenStore.create(accessToken);
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"CREATED_TOKEN", accessToken.toString()};
        auditLogger.logAccessMessage("CREATED_TOKEN", obs, null);
      }
    } catch (CoreTokenException e) {
      logger.error("Could not create token in CTS: " + e.getMessage());
      if (auditLogger.isAuditLogEnabled()) {
        String[] obs = {"FAILED_CREATE_TOKEN", accessToken.toString()};
        auditLogger.logErrorMessage("FAILED_CREATE_TOKEN", obs, null);
      }
      throw new ServerException("Could not create token in CTS: " + e.getMessage());
    }
    request.setToken(AccessToken.class, accessToken);
    return accessToken;
  }
  @Post
  public Representation requestAuthorization(JsonRepresentation entity)
      throws BadRequestException, UmaException, EntitlementException, ServerException,
          NotFoundException {
    UmaProviderSettings umaProviderSettings = umaProviderSettingsFactory.get(this.getRequest());
    JsonValue requestBody = json(toMap(entity));
    PermissionTicket permissionTicket =
        getPermissionTicket(umaProviderSettings.getUmaTokenStore(), requestBody);

    final AccessToken authorisationApiToken = getAuthorisationApiToken();

    if (hasExpired(permissionTicket)) {
      throw new UmaException(
          400, UmaConstants.EXPIRED_TICKET_ERROR_CODE, "The permission ticket has expired");
    }

    // Remove permission ticket so it cannot be re-used
    umaProviderSettings.getUmaTokenStore().deletePermissionTicket(permissionTicket.getId());

    final String requestingUserId = authorisationApiToken.getResourceOwnerId();
    final String resourceSetId = permissionTicket.getResourceSetId();
    final Request request = getRequest();
    final String resourceOwnerId = getResourceOwnerId(resourceSetId);

    auditLogger.log(
        resourceSetId, resourceOwnerId, UmaAuditType.REQUEST, request, requestingUserId);

    if (isEntitled(umaProviderSettings, permissionTicket, authorisationApiToken)) {
      getResponse().setStatus(new Status(200));
      auditLogger.log(
          resourceSetId, resourceOwnerId, UmaAuditType.GRANTED, request, requestingUserId);
      return createJsonRpt(
          umaProviderSettings.getUmaTokenStore(), permissionTicket, authorisationApiToken);
    } else {
      try {
        if (verifyPendingRequestDoesNotAlreadyExist(
            resourceSetId,
            resourceOwnerId,
            permissionTicket.getRealm(),
            requestingUserId,
            permissionTicket.getScopes())) {
          auditLogger.log(
              resourceSetId, resourceOwnerId, UmaAuditType.DENIED, request, requestingUserId);
          throw new UmaException(
              403,
              UmaConstants.NOT_AUTHORISED_ERROR_CODE,
              "The client is not authorised to access the requested resource set");
        } else {
          pendingRequestsService.createPendingRequest(
              ServletUtils.getRequest(getRequest()),
              resourceSetId,
              auditLogger.getResourceName(resourceSetId, request),
              resourceOwnerId,
              requestingUserId,
              permissionTicket.getRealm(),
              permissionTicket.getScopes());
          auditLogger.log(
              resourceSetId,
              resourceOwnerId,
              UmaAuditType.REQUEST_SUBMITTED,
              request,
              requestingUserId);
        }
      } catch (org.forgerock.openam.sm.datalayer.store.ServerException e) {
        logger.error("Failed to create pending request", e);
        throw new UmaException(
            403, UmaConstants.NOT_AUTHORISED_ERROR_CODE, "Failed to create pending request");
      }
      throw newRequestSubmittedException();
    }

    // TODO not sure where "need_info" error fits in....
  }