1

I'm just starting to learn oAuth2 authorization to secure my API. and the process is quite complicated. my API is developed using Jersey, and Apache Oltu as the oAuth2 Authorization. now, the token can be generated, but, every time i try to generate a new token, the server create a new instance of @ApplicationScoped bean. this is the bean code i got from internet :

@ApplicationScoped
public class Database {

private Set<String> authCodes = new HashSet<>();
private Set<String> tokens = new HashSet<>();

public void addAuthCode(String authCode) {
    authCodes.add(authCode);
}

public boolean isValidAuthCode(String authCode) {
    return authCodes.contains(authCode);
}

public void addToken(String token) {
    tokens.add(token);
}

public boolean isValidToken(String token) {
    return tokens.contains(token);
}
}

and this is the Authorization code :

@Path("/authz")
public class AuthzEndpoint {

@Inject
Database database;

@GET
public Response authorize(@Context HttpServletRequest request)
        throws URISyntaxException, OAuthSystemException {
    try {
        OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);
        OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());

        //build response according to response_type
        String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);

        OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
                OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);

        if (responseType.equals(ResponseType.CODE.toString())) {
            final String authorizationCode = oauthIssuerImpl.authorizationCode();
            database.addAuthCode(authorizationCode);
            builder.setCode(authorizationCode);
        }
        if (responseType.equals(ResponseType.TOKEN.toString())) {
            final String accessToken = oauthIssuerImpl.accessToken();
            database.addToken(accessToken);

            builder.setAccessToken(accessToken);
            builder.setExpiresIn(3600l);
        }

        String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);
        final OAuthResponse response = builder.location(redirectURI).buildQueryMessage();
        URI url = new URI(response.getLocationUri());
        return Response.status(response.getResponseStatus()).location(url).build();
    } catch (OAuthProblemException e) {
        final Response.ResponseBuilder responseBuilder = Response.status(HttpServletResponse.SC_FOUND);
        String redirectUri = e.getRedirectUri();

        if (OAuthUtils.isEmpty(redirectUri)) {
            throw new WebApplicationException(
                    responseBuilder.entity("OAuth callback url needs to be provided by client!!!").build());
        }
        final OAuthResponse response =
                OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
                        .error(e).location(redirectUri).buildQueryMessage();
        final URI location = new URI(response.getLocationUri());
        return responseBuilder.location(location).build();
    }
}
}

as you can see, there are @Inject annotation of database there, and called addToken() method in some part of the code. and when i try to validate the token from my main web services, the Database bean is empty. this is the code

@Inject
Database database;

@POST
@Path("validateLogin")
@Consumes("application/x-www-form-urlencoded")
@Produces({"application/xml", "application/json", "text/plain", "text/html"})
public Response validateLogin(@HeaderParam("Authorization") String token, @FormParam("username") String username, @FormParam("password") String password) {

    System.out.println(token.substring(7,token.length()));
    System.out.println(database.isValidToken(token.substring(7, token.length())));
    System.out.println(database);

    if (!database.isValidToken(token.substring(7, token.length()))) {
        return Response.status(Response.Status.UNAUTHORIZED).build();
    }
    else {
        String result;
        if (username == null || password == null) {
            return Response.status(Response.Status.BAD_REQUEST).build();
        }
        else {
            STCWebService stcWebService = new STCWebService();
            result = stcWebService.validateLogin(username,password);
            if (result.isEmpty()) {
                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
            }
            else {
                return Response
                        .status(200)
                        .entity(result)
                        .type(MediaType.APPLICATION_JSON)
                        .header("Access-Control-Allow-Origin", "*")
                        .header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
                        .build();
            }
        }
    }
}

i have been thinking there might be some mistake in web.xml file or ResourceConfig.java or even in ApplicationBinder. i have stumbled this problem for hours. any help is appreciated.

EDIT

this is the ApplicationBinder Code

public class MyApplicationBinder extends AbstractBinder {
    @Override
    protected void configure() {
        bind(Database.class).to(Database.class);
    }
}

and is linked to this MyApplication

public class MyApplication extends ResourceConfig {
    public MyApplication() {
        register(new MyApplicationBinder());
        packages("com.package");
    }
}

i created the ApplicationBinder and MyApplication code based on this question Dependency injection with Jersey 2.0

Ardi Sugiarto
  • 85
  • 1
  • 8

1 Answers1

2

The problem is how you are binding the Database.

bind(Database.class).to(Database.class); 

ApplicationScoped will not work. You need to configure the scope with the AbstractBinder. There are a couple ways.

  1. Just instantiate it

    bind(new Database()).to(Database.class);
    

    This will automatically make it a singleton. One problem with this though is that if the Database has it's own dependencies, they will not be injected. Fo this, you can use the next solution

  2. Use the in(Scope) method to add a scope to the service. The default scope, when not provided, is PerLookup, which means that a new one will be created each time is it requested. There are other scopes like Singleton and RequestScope

    bind(Database.class).to(Database.class).in(Singleton.class);
    

    Make sure it's javax.inject.Singleton and not the EJB one.


You should've scrolled down to my post in that question you linked to :-)

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720