// TODO The secret should be a char[].
  private Client validate(String clientId, String clientSecret) {
    Client client = clients.findById(clientId);
    getLogger().fine("Client = " + client);

    if (client == null) {
      sendError(
          OAuthError.invalid_client,
          "Could not find the correct client with id : " + clientId,
          null);
      setStatus(Status.CLIENT_ERROR_NOT_FOUND);
      return null;
    }

    if ((clientSecret == null) || !clientSecret.equals(client.getClientSecret())) {
      sendError(OAuthError.invalid_grant, "Client secret did not match", null);
      setStatus(Status.CLIENT_ERROR_UNAUTHORIZED);
      getLogger()
          .warning(
              "Could not find or match client secret "
                  + clientSecret
                  + " : "
                  + client.getClientSecret());
      return null;
    }

    return client;
  }
  // TODO The secret should be a char[].
  private Representation doAuthCodeFlow(
      String clientId, String clientSecret, Series<Parameter> params)
      throws IllegalArgumentException {
    String redirUri = params.getFirstValue(REDIR_URI);

    if ((redirUri == null) || (redirUri.length() == 0)) {
      setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
      return sendError(
          OAuthError.invalid_request, "Mandatory parameter redirect_uri is missing", null);
    }

    String code = params.getFirstValue(CODE);
    if ((code == null) || (code.length() == 0)) {
      setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
      return sendError(OAuthError.invalid_request, "Mandatory parameter code is missing", null);
    }

    Client client = validate(clientId, clientSecret);
    // null check on failed
    if (client == null) {
      setStatus(Status.CLIENT_ERROR_FORBIDDEN);
      return sendError(OAuthError.invalid_request, "Client id verification failed.", null);
    }

    // check the client secret
    if (!clientSecret.equals(client.getClientSecret())) {
      setStatus(Status.CLIENT_ERROR_UNAUTHORIZED);
      return sendError(OAuthError.invalid_request, "Client secret did not match", null);
    }

    // TODO could add a cookie match on the owner but could fail if code is
    // sent to other entity
    // unauthorized_client, right now this is only performed if
    // ScopedResource getOwner returns the user

    // 5 min timeout on tokens, 0 for unlimited
    Token token = generator.exchangeForToken(code, tokenTimeSec);

    // TODO send back scopes if limited

    JSONObject body = createJsonToken(token, null);

    // Sets the no-store Cache-Control header
    getResponse().setCacheDirectives(noStore);
    return new JsonStringRepresentation(body);
  }