Java webservice client with ADFS SAML authentication - java

We are trying to connect to webservice (from Java) that has ADFS SAML authentication.
All the examples I have seen, use Basic Authentication over HTTPS. (I am just using HttpsURLConnection to make a request for now, not using anything like Axis or JAX-WS)
I am not sure how to approach ADFS SAML authentication. Here's what I understand so far (don't know much about SAML):
I make one request, pass username/password and get the
authentication token back
Save the authentication token
Pass the token as some SOAP attribute in my calls where I invoke an
actual operation on the webservice
No idea under which attribute would I put this authentication token though
Is my above approach correct? If so, is there some library that I can use that does all this?
If not how can I go about doing this manually?
Please let me know if there are other or better ways of going about this.

If you are trying to build native app then can use below code. i has tried to use power bi rest apis. once you gets token you can use that in api calls.
public class PublicClient {
private final static String AUTHORITY = "https://login.microsoftonline.com/common";
private final static String CLIENT_ID = "XXXX-xxxx-xxx-xxx-xxxX";
private final static String RESOURCE = "https://analysis.windows.net/powerbi/api";
public static void main(String args[]) throws Exception {
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
System.out.print("Enter username: ");
String username = br.readLine();
System.out.print("Enter password: ");
String password = br.readLine();
AuthenticationResult result = getAccessTokenFromUserCredentials(
username, password);
System.out.println("Access Token - " + result.getAccessToken());
System.out.println("Refresh Token - " + result.getRefreshToken());
System.out.println("ID Token Expires on - " + result.getExpiresOn());
}
}
private static AuthenticationResult getAccessTokenFromUserCredentials(
String username, String password) throws Exception {
AuthenticationContext context = null;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(AUTHORITY, false, service);
Future<AuthenticationResult> future = context.acquireToken(
RESOURCE, CLIENT_ID, username, password, null);
result = future.get();
} finally {
service.shutdown();
}
if (result == null) {
throw new ServiceUnavailableException(
"authentication result was null");
}
return result;
}
}

Related

How to access and create lifecycle rules/lifecycle management policy for azure storage account through java code

I want to create a lifecycle rule or lifecycle management policy for a specific azure storage account through java code (not through terraform or azure portal). Any appropriate code snippet or reference will be helpful. Thanks in advance.
If you would like to manage the Azure Blob storage lifecycle, you could create it with these following methods.
Azure portal, Azure PowerShell, Azure CLI, REST APIs
So you could call this REST API to create lifecycle with Java code. You need to get access token, then call the API. See the sample code, note to change the HTTP request:
public class PublicClient {
/*tenant_id can be found from your azure portal. Login into azure portal and browse to active directory and choose the directory you want to use. Then click on Applications tab and at the bottom you should see "View EndPoints". In the endpoints, the tenant_id will show up like this in the endpoint url's: https://login.microsoftonline.com/{tenant_id} */
private final static String AUTHORITY = "https://login.microsoftonline.com/{tenant_id}";
public static void main(String args[]) throws Exception {
AuthenticationResult result = getAccessTokenFromUserCredentials();
System.out.println("Access Token - " + result.getAccessToken());
HttpClient client = new DefaultHttpClient();
/* replace {subscription_id} with your subscription id and {resourcegroupname} with the resource group name for which you want to list the VM's. */
HttpGet request = new HttpGet("https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resourcegroupname}/providers/Microsoft.ClassicCompute/virtualMachines?api-version=2014-06-01");
request.addHeader("Authorization","Bearer " + result.getAccessToken());
HttpResponse response = client.execute(request);
BufferedReader rd = new BufferedReader (new InputStreamReader(response.getEntity().getContent()));
String line = "";
while ((line = rd.readLine()) != null)
{
System.out.println(line);
}
}
private static AuthenticationResult getAccessTokenFromUserCredentials() throws Exception {
AuthenticationContext context = null;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(AUTHORITY, false, service);
/* Replace {client_id} with ApplicationID and {password} with password that were used to create Service Principal above. */
ClientCredential credential = new ClientCredential("{client_id}","{password}");
Future<AuthenticationResult> future = context.acquireToken("https://management.azure.com/", credential, null);
result = future.get();
} finally {
service.shutdown();
}
if (result == null) {
throw new ServiceUnavailableException("authentication result was null");
}
return result;
}
}

Unable to have concurrent log-ins with Google auth

I have a web app where users have to authenticate using Google sign-in. I do this because I need to grab their email address. When they fill out the fields on the page, all that data is stored in a google sheet alongside their email address (for auditing purposes incase something is askew with the data). Unfortunately what's happening is that if user A signs in, and does some work and at the same time user B logs in, when user A submits data, they will be submitting user B's email address (as does user B). In short, the latest person to log in, that email address is used. There is no database and I'm not storing any cookies. When they refresh the page, they have to re-authenticate. I am using Angular 7 and Java. Here is the code that I used:
ngOnInit() {
gapi.load('auth2', () => {
this.auth2 = gapi.auth2.init({
client_id: 'CLIENT_ID_HERE',
// Scopes to request in addition to 'profile' and 'email'
scope: 'https://www.googleapis.com/auth/spreadsheets'
});
});
}
signInWithGoogle(): void {
this.auth2.grantOfflineAccess().then((authResult) => {
this.authCode = authResult['code'];
this.fetchData();
});
}
authCode is bound to the child component so it can be passed as query param to the java code for google auth.
this.seriesService.submitSeriesData(matchList, this.authToken).subscribe(res => {.....);
The google auth java code is so:
private static final String APPLICATION_NAME = "Google Sheets API Java";
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static final List<String> SCOPES = Collections.singletonList(SheetsScopes.SPREADSHEETS);
private static final String CLIENT_SECRET_DIR = "/client_secret.json";
private static GoogleTokenResponse tokenResponse = null;
public static String getEmailAddress() throws IOException {
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
String email = payload.getEmail();
return email;
}
public static Sheets getSheetsService1(String token, String redirectUri) throws IOException, GeneralSecurityException {
// Exchange auth code for access token
InputStream in = GoogleAuthUtil.class.getResourceAsStream(CLIENT_SECRET_DIR);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
"https://www.googleapis.com/oauth2/v4/token",
clientSecrets.getDetails().getClientId(),
clientSecrets.getDetails().getClientSecret(),
token,
redirectUri)
.execute();
String accessToken = tokenResponse.getAccessToken();
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Sheets service = new Sheets.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
.setApplicationName("MY APP HERE")
.build();
return service;
}
And the endpoint:
#RequestMapping(value="series/data", method = RequestMethod.POST, consumes="application/json")
public boolean submitSeriesMatchData(#RequestBody(required=true) SubmitStatsDto request) throws IOException, GeneralSecurityException, Exception {
if (service == null) {
service = GoogleAuthUtil.getSheetsService1(request.getToken(), this.redirectUri);
}
......
}
1) User clicks on the google sign in button
2) They select email and auth with google
3) I receive an auth code back and store it in ng.
4) Every REST call is passed said token to auth with google, and every endpoint calls getSheetsService1 which authenticates w/ token. (multiple endpoints, I only showed one above)
5) I get email from that tokenResponse.
Any ideas? This site will not have a database/users/local logins. Thank you.

How to show browser login form for basic authentication using RESTEasy

I´m currently messing around with JAX-RS specifically Resteasy, because it "just works" with Wildfly and I don´t have to configure anything. That´s really the only reason I use that.
I did already implement Basic Authentication, looking forward to replacing it with OAuth2 later, just did this now for simplicity reasons.
The ContainerRequestFilter looks like this
#Provider
public class SecurityFilter implements ContainerRequestFilter {
private static final String AUTHORIZATION_HEADER_KEY = "Authorization";
private static final String AUTHORIZATION_HEADER_PREFIX = "Basic ";
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
if(isAuthenticated(containerRequestContext) == false)
containerRequestContext.abortWith(createUnauthorizedResponse("Access denied."));
}
private boolean isAuthenticated(ContainerRequestContext containerRequestContext) {
List<String> authHeader = containerRequestContext.getHeaders().get(AUTHORIZATION_HEADER_KEY);
ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) containerRequestContext.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
Method method = methodInvoker.getMethod();
RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
if (authHeader != null && authHeader.size() > 0) {
String authToken = authHeader.get(0).replaceFirst(AUTHORIZATION_HEADER_PREFIX, "");
byte[] decoded = null;
try {
decoded = Base64.getDecoder().decode(authToken);
} catch (IllegalArgumentException ex) {
return false;
}
String decodedString = new String(decoded);
StringTokenizer tokenizer = new StringTokenizer(decodedString, ":");
String username = null, password = null;
if(tokenizer.countTokens() < 2)
return false;
username = tokenizer.nextToken();
password = tokenizer.nextToken();
if (DbController.isValid(username, password, rolesAnnotation.value()))
return true;
}
return false;
}
private Response createUnauthorizedResponse(String msg) {
return Response.status(Response.Status.UNAUTHORIZED)
.entity("{ \"Unauthorized\" : \"" + msg + "\" }")
.type(MediaType.APPLICATION_JSON)
.build();
}
}
It works fine with postman. And I do realize that the main usage of such apis is in well other programs.
But it would be nice, if opened in a browser it would ask you to enter your credentials, instead of just telling you that you are not authorized, with no way to really enter your credentials. Unless you do some trickery to manually put it in the header, but then you might as well just use postman.
If I put a security constraint with auth-constraint role admin it does give a login dialog, but then the authorization does not work and it just keeps asking for authorization.
Is there anything else that I can do instead of containerRequestContext.abortWith? Or do I need to use a completely different approach and it just won´t work with ContainerRequestFilter?
You need to add the WWW-Authenticate header to the response that you abort with. This header tells the browser that it should present the default browser login form.
private static final String CHALLENGE_FORMAT = "%s realm=\"%s\"";
private Response createUnauthorizedResponse() {
return Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.WWW_AUTHENTICATE, String.format(CHALLENGE_FORMAT, "Basic", "Access"))
.type(MediaType.TEXT_PLAIN_TYPE)
.entity("Credentials are required to access this resource.")
.build();
And here's what the login should look like on Chrome

Web app and request authentication

I currently have a working web app, but I need to provide means for friend website to consume my data.
There is currently JSON response in place which retrieves some data from my website to caller. It's without authentication currently and I'd like to implement some kind of per request authentication.
My web app has users which are logged in and there is a authentication in place for that. But
I have 3 requests in total for which callers can get data off of my website, what would be the simplest way to add some kind of authentication just for those 3 requests?
I'm using play framework + java
Imo the best options for this would be in the order of simplicity:
Basic authentication (since it's possible to choose either to auth once and then do session-base user recognition or authorize on every request)
2-way SSL
Combination of both
What toolkit do you use for authentication part?
I personally stuck with play-authenticate. So I might be able to answer you question in regard to this toolkit, please apply it to your particular toolkit as needed.
I will provide Basic authentication example as the easiest one. The benefit is: you could start with it and add on top it later (e.g. add Client certificate authentication via Apache later on).
So, my controller code snippet
#Restrict(value = #Group({"ROLE_WEB_SERVICE1"}), handler = BasicAuthHandler.class)
public static Result ws1() {
return TODO;
}
And the authentification handler itself
public class BasicAuthHandler extends AbstractDeadboltHandler {
public static final String HEADER_PREFIX = "Basic ";
private static final String AUTHORIZATION = "authorization";
private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
#Override
public Result beforeAuthCheck(final Http.Context context) {
return basicAuthenticate(context);
}
private Result basicAuthenticate(Http.Context context) {
if (PlayAuthenticate.isLoggedIn(context.session())) {
// user is logged in
return null;
}
final String authHeader = context.request().getHeader(AUTHORIZATION);
if (authHeader == null || !authHeader.toLowerCase().startsWith(HEADER_PREFIX.toLowerCase())) {
return onAuthFailure(context, "Basic authentication header is missing");
}
final String auth = authHeader.substring(HEADER_PREFIX.length());
final byte[] decodedAuth;
final String[] credentials;
try {
decodedAuth = Base64.base64ToByteArray(auth);
credentials = new String(decodedAuth, "UTF-8").split(":");
} catch (final IOException e) {
Logger.error("basicAuthenticate", e);
return Results.internalServerError();
}
if (credentials.length != 2) {
return onAuthFailure(context, "Could not authenticate with absent password");
}
final String username = credentials[0];
final String password = credentials[1];
final AuthUser authUser = new AuthUser(password, username);
final Enum result = AuthProvider.getProvider().loginUser(authUser);
if ("USER_LOGGED_IN".equals(result.name())) {
PlayAuthenticate.storeUser(context.session(), authUser);
return null;
}
return onAuthFailure(context, "Authenticate failure");
}
#Override
public Subject getSubject(final Http.Context context) {
// your implementation
}
#Override
public Result onAuthFailure(final Http.Context context,
final String content) {
// your error hangling logic
return super.onAuthFailure(context, content);
}
}
Hopefully it fills in some blanks

RestFB: Using a facebook app to get the users Access Token

This is what i have:
static AccessToken accessToken = new DefaultFacebookClient().obtainExtendedAccessToken("<my app id>", "<my app secret>");
static FacebookClient client = new DefaultFacebookClient();
public static void main(String args[]) {
System.out.print("Enter Your Status: ");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String status= null;
try {
userName = br.readLine();
System.out.println("..........");
} catch (IOException ioe) {
System.out.println("!");
System.exit(1);
}
FacebookType publishMessageResponse =
client.publish("me/feed", FacebookType.class,
Parameter.with("message", status));
So first line gets the token and stores it as type AccessToken but what good does that do to me because next line i need to provide the access token as a string and i can't convert it. Any Help?
First of all don't confuse app token with user token (more info)
To get user token you have to
Provide a way for user to authenticate against Facebook (more info) and receive "code" - an encrypted string unique to each login request.
Get the user token using this code.
You can get user token with pure RestFB the following way:
private FacebookClient.AccessToken getFacebookUserToken(String code, String redirectUrl) throws IOException {
String appId = "YOUR_APP_ID";
String secretKey = "YOUR_SECRET_KEY";
WebRequestor wr = new DefaultWebRequestor();
WebRequestor.Response accessTokenResponse = wr.executeGet(
"https://graph.facebook.com/oauth/access_token?client_id=" + appId + "&redirect_uri=" + redirectUrl
+ "&client_secret=" + secretKey + "&code=" + code);
return DefaultFacebookClient.AccessToken.fromQueryString(accessTokenResponse.getBody());
}
The call is simple:
FacebookClient.AccessToken token = getFacebookUserToken(code, redirectUrl);
String accessToken = token.getAccessToken();
Date expires = token.getExpires();
In addition to what Jack said about AccessToken.getAccessToken() returning the string value of accessToken, you can avoid instantiating DefaultFacebookClient twice by extending DefaultFacebookClient like this:
import com.restfb.DefaultFacebookClient;
public class LoggedInFacebookClient extends DefaultFacebookClient {
public LoggedInFacebookClient(String appId, String appSecret) {
AccessToken accessToken = this.obtainAppAccessToken(appId, appSecret);
this.accessToken = accessToken.getAccessToken();
}
}
Try the following code:
AccessToken accessToken = new DefaultFacebookClient().obtainAppAccessToken(appid,appsecret);
String token=accessToken.getAccessToken();
Per restfb.FacebookClient.AccessToken, you should be able to call accessToken.getAccessToken() -- that should return the String you are looking for.
This will work
AccessToken accessToken = new DefaultFacebookClient().obtainAppAccessToken("XXXX", "XXXX");
String token=accessToken.getAccessToken();
DefaultFacebookClient facebookClient = new DefaultFacebookClient(token);

Categories