I have a spring rest api that is secured using base64 authentication through a database. Is it possible to then take another rest api and somehow authenticate through the first api?
-
btw, the backend uses the UserDetailsServiceWill Harrison– Will Harrison2015-02-02 21:57:58 +00:00Commented Feb 2, 2015 at 21:57
-
why not use OAuth? It's a more secure way than HTTP basic authentication. Here's a blog post that might help. stormpath.com/blog/secure-your-rest-api-right-wayAlex– Alex2015-02-04 20:34:06 +00:00Commented Feb 4, 2015 at 20:34
3 Answers
Have you considered securing your APIs with OAuth-based authentication and API key management. HTTP Basic Authentication isn't really consider ideal from a security perspective and username and password have another set of security issues for APIs
Either way, you might consider using Stormpath to make this REALLY easy for you. Take a look at this guide, it supports both HTTP basic and OAuth.
This sample code will give you a good idea of how easy this is.
Let's suppose you want to expose an operation called startEngines() and you want to secure it. You will also need to expose a new operation to get access tokens, in this example String getAccessToken(ApiKey).
Your users will run something like this:
@Test
public void executeSomeOauth2AuthenticatedOperation() {
String userApiKeyPath = System.getProperty("user.home") + "/.stormpath/apiKey_4Yrc0TJ5sBFldwtu6nfzf5.properties";
ApiKey userApiKey = ApiKeys.builder().setFileLocation(userApiKeyPath).build();
//Developer requests access token
String accessToken = getAccessToken(userApiKey);
//Developer executes an authenticated operation (e.g startEngines()) with the provided accessToken
if (startEngines(accessToken)) {
System.out.print("Client-side message: Execution allowed");
} else {
System.out.print("Client-side message: Execution denied");
}
}
Your code will look like this:
String path = System.getProperty("user.home") + "/.stormpath/apiKey.properties";
String applicationUrl = "https://api.stormpath.com/v1/applications/2TqboZ1qo73eDM4gTo2H94";
Client client = Clients.builder().setApiKey(ApiKeys.builder().setFileLocation(path).build()).build();
Application application = client.getResource(applicationUrl, Application.class);
public String getAccessToken(ApiKey apiKey) {
HttpRequest request = createOauthAuthenticationRequest(apiKey);
AccessTokenResult accessTokenResult = (AccessTokenResult) application.authenticateApiRequest(request);
System.out.println(accessTokenResult.getScope());
return accessTokenResult.getTokenResponse().getAccessToken();
}
public boolean startEngines(String accessToken) {
HttpRequest request = createRequestForOauth2AuthenticatedOperation(accessToken);
try {
OauthAuthenticationResult result = application.authenticateOauthRequest(request).execute();
System.out.println(result.getAccount().getEmail() + " is about to start the engines!");
doStartEngines(); //Here you will actually call your internal doStartEngines() operation
return true;
} catch (AccessTokenOauthException e) {
//This accessToken is not allowed to start the engines
System.out.print("AccessToken: " + accessToken + " just tried to start the engines. He is not allowed to do so.");
return false;
}
}
private HttpRequest createOauthAuthenticationRequest(ApiKey apiKey) {
try {
String credentials = apiKey.getId() + ":" + apiKey.getSecret();
Map<String, String[]> headers = new LinkedHashMap<String, String[]>();
headers.put("Accept", new String[]{"application/json"});
headers.put("Content-Type", new String[]{"application/x-www-form-urlencoded"});
headers.put("Authorization", new String[]{"Basic " + Base64.encodeBase64String(credentials.getBytes("UTF-8"))});
Map<String, String[]> parameters = new LinkedHashMap<String, String[]>();
parameters.put("grant_type", new String[]{"client_credentials"});
HttpRequest request = HttpRequests.method(HttpMethod.POST)
.headers(headers)
.parameters(parameters)
.build();
return request;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private HttpRequest createRequestForOauth2AuthenticatedOperation(String token) {
try {
Map<String, String[]> headers = new LinkedHashMap<String, String[]>();
headers.put("Accept", new String[]{"application/json"});
headers.put("Authorization", new String[]{"Bearer " + token});
HttpRequest request = HttpRequests.method(HttpMethod.GET)
.headers(headers)
.build();
return request;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private void doStartEngines() {
System.out.println("Server-side message: Engines started!!!");
}
For the sake of simplicity I made all this code run in the same machine (no network communication between the client- and server-side code). You will actually need to expose startEngines() and String getAccessToken(ApiKey) via Rest API using Spring and have your end-users access them via the network.
Give it a try, it should a pretty easy and quick solution. :)
Full disclosure- I work at Stormpath
Comments
Base64 is an encoding mechanism, not an authentication scheme. I assume you mean you are using basic authentication, which is the username and password base64 encoded in the Authorization HTTP header.
If the other service also uses basic authentication and check username and password against the same credential database, then you will be able to use the same base64 encoded string to authenticate against the other REST service.