Use external api having oauth2 in spring boot - spring-boot

I need to call an external API from my spring boot project. The external API is using OAuth 2 security authentication using authorization_code. I have the client id and secret key, any suggestion would be great.
Tried using SDK provided by DocuSign but while getting access token facing issue as 400 with message consent required.

The easiest way to do this is do download a "quickstart" from DocuSign and pick Java for your language. This does a lot more than just give you Java code, it also configures everything you need for you to be able to make API calls.
https://developers.docusign.com/docs/esign-rest-api/quickstart/
The specific Java code that does Auth Code Grant authentication can be found here:
https://github.com/docusign/code-examples-java/blob/master/src/main/java/com/docusign/core/controller/GlobalControllerAdvice.java
OAuth2AuthenticationToken oauth = (OAuth2AuthenticationToken) authentication;
OAuth2User oauthUser = oauth.getPrincipal();
OAuth2AuthorizedClient oauthClient = authorizedClientService.loadAuthorizedClient(
oauth.getAuthorizedClientRegistrationId(),
oauthUser.getName()
);
if (oauth.isAuthenticated()) {
user.setName(oauthUser.getAttribute("name"));
if (oauthClient != null){
user.setAccessToken(oauthClient.getAccessToken().getTokenValue());
} else {
user.setAccessToken(((OAuth.OAuthToken) oauthUser.getAttribute("access_token")).getAccessToken());
}
if (account.isEmpty()) {
account = Optional.ofNullable(getDefaultAccountInfo(getOAuthAccounts(oauthUser)));
}
OAuth.Account oauthAccount = account.orElseThrow(() -> new NoSuchElementException(ERROR_ACCOUNT_NOT_FOUND));
session.setAccountId(oauthAccount.getAccountId());
session.setAccountName(oauthAccount.getAccountName());
// TODO set this more efficiently with more APIs as they're added in
String basePath = this.getBaseUrl(apiIndex, oauthAccount) + apiIndex.getBaseUrlSuffix();
session.setBasePath(basePath);
}

Related

How to add Custom Token Granter to the new Spring Authorization Server

Hello I am currently using and old Authorization Server with th end of life dependency spring-security-oauth2-autoconfigure and now i would like to migrate to the new Spring Authorization Server
My questions is how can i intercept/override the default Token Granter of the new Spring Authorization Service. In the old version i just extended the AbstractTokenGranter SsoTokenGranter extends AbstractTokenGranter.
I would like to call other services during the token generation and add custom claims/authorities to the JWT Token with user information(Roles, Name, etc..).
Any tipps how i can do this?
I think an OAuth2TokenCustomizer can fit nicely in your use case.
#Bean
public OAuth2TokenCustomizer<JwtEncodingContext> tokenCustomizer(
OidcUserInfoService userInfoService) {
return (context) -> {
if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) {
OidcUserInfo userInfo = userInfoService.loadUser( // <2>
context.getPrincipal().getName());
context.getClaims().claims(claims ->
claims.putAll(userInfo.getClaims()));
}
};
}
There is a section on the reference docs and a sample that you can use as reference.

How to Implement SAML Single Logout request from a microservice

I have implemented the multi-tenant SAML SSO in my application. I am using a Single Page Application application with the UI in AngularJS, Web API (for assertion URL), and a microservice for creating and handling the SAML requests. I am using the Itfoxtech library in my microservice.
I have implemented the SAML SSO Login successfully and it is working fine. However, I am facing issues while implementing the SAML Single Logout. On SAML Assertion, I am just extracting few claims and returning these to Web API. On Logout, it seems that I need the ClaimsIdentity and HttpContext. I have persisted ClaimsIdentity during the SAML Assertion and re-using it during the Logout but I don't have access to HttpContext. I have created a custom httpContext from DefaultHttpContext and tried to execute the following line of code,
var saml2LogoutRequest = await new Saml2LogoutRequest(config, User).DeleteSession(HttpContext);
but it gives an error,
No sign-out authentication handlers are registered. Did you forget to call AddAuthentication().AddCookies
My question is that how to perform a single logout without using the HttpContext or if it is required then how to manually create it?
Doing logout SAML 2.0 need NameID, NameID format and session index. To achive this you can polulate the ClaimsIdentity with the claims: Saml2ClaimTypes.NameId, Saml2ClaimTypes.NameIdFormat and Saml2ClaimTypes.SessionIndex.
In the case of single logout you only need to validate the request:
Saml2StatusCodes status;
var requestBinding = new Saml2PostBinding();
var logoutRequest = new Saml2LogoutRequest(config, User);
try
{
requestBinding.Unbind(Request.ToGenericHttpRequest(), logoutRequest);
status = Saml2StatusCodes.Success;
//TODO handle logout
}
catch (Exception exc)
{
// log exception
Debug.WriteLine("SingleLogout error: " + exc.ToString());
status = Saml2StatusCodes.RequestDenied;
}
and respond:
var responsebinding = new Saml2PostBinding();
responsebinding.RelayState = requestBinding.RelayState;
var saml2LogoutResponse = new Saml2LogoutResponse(config)
{
InResponseToAsString = logoutRequest.IdAsString,
Status = status,
};
return responsebinding.Bind(saml2LogoutResponse).ToActionResult();
You do not need to call the DeleteSession(HttpContext) but you need to handle logout somehow.

Xamarin OAuth2Authenticator Microsoft Account returns malformed JWT token for Azure App Service

I am building a Xamarin.Forms app to learn the framework and working on the authentication services while using an Azure App Service for an API (also new to me). And trying to authenticate against a Microsoft account (Outlook.com).
I am using the Xamarin.Auth OAuth2Authenticator class and it is returning a very peculiar, malformed JWT token. This has been driving me nuts for days and finally decided to turn to where the experts are.
I have an IAuthService interface that will be used by the platform apps to build out the authentication service
public interface IAuthService
{
Task SignInAsync(string clientId,
Uri authUrl,
Uri callbackUrl,
Action<string> tokenCallback,
Action<string> errorCallback);
}
Said service (for iOS) is built out as follows (shortened for brevity):
public class AuthService : IAuthService
{
public async Task SignInAsync(string clientId, Uri authUrl, Uri callbackUrl, Action<string> tokenCallback, Action<string> errorCallback)
{
var auth = new OAuth2Authenticator(clientId, "openid", authUrl, callbackUrl);
auth.AllowCancel = true;
var controller = auth.GetUI();
await UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewControllerAsync(controller, true);
auth.Completed += (s, e) =>
{
controller.DismissViewController(true, null);
if (e.Account != null && e.IsAuthenticated)
{
Console.WriteLine(e.Account.Properties["access_token"]);
tokenCallback?.Invoke(e.Account.Properties["access_token"]);
}
else
{
errorCallback?.Invoke("Not authenticated");
}
};
...
}
}
So I instantiate a new instance of the OAuth2Authenticator class, specifying the scope as "openid"; the MS authorize endpoint requires a scope.
When it hits the console writeline I can see a value came back... but it's not like any JWT token I have seen before.
EwB4A8l6BAAURSN/FHlDW5xN74t6GzbtsBBeBUYAAWPAOehpFaoAKb8Kz67ZZzgzBS3KUtHGZri2sbgIJfA5xZYDv5K417HIz2P+ggUeB/gFMxRfXH1Hd1qT90bfo6skGpIc/K2vDgBoRY0VnlA9nnCyct9B2tSaNQn3hZjPOiOchmSCJxrUMILGKdKy4kxxn5qFlTXAy0hWIQjHXcwGeKXDm1w3wY6x8xsmBxNNXor9FluuUXNNTtu4iP6s424JwIiJ7HCyu6ftORXCfIlemRSv5hcHLa1MXS9vUq95lRc08S05Ek7IiUfMiAnYbrqwD7H+vheAtfDc9kYleebyxlFl6gpVKmv43DV2yYgYIqgqswO6ktJ6Gar4zmqUYUIDZgAACGWNlS0Ln+wWSAJC2apVPWWIsKPobIL0uBImdORjOWvFOnLtKhQfCnngoo1Tw1UItqo5FRj1f/KWj3if/DPgWaQx5Bf4tbqCjuuOdEkR9r/Ru1v/ccjrg2oqp0hicWwIoSaQm2JHgnrrCQ1cfcvXhAuVlAo9tKyqW/dCehdz7NRpQbNtmLvba+PjWWYEcDROJJSwTRqNTGkwiwzNhw8p/Zlf7G51F205S4vDZob1MsWythkrUJAjA6MUJy4wZ5B/8ChF7J3WRSTapjr+6mNgvgvhcflGo6GoEID24aSDL6h9QGPylk6zfghksweu9/AmSMO4BKwLDVSr04BJj1n1rfKsadUBqWUQMaXFGu+OfGbOCm6E5zLSJyO2JKbcsI468gb0/vC6FYFJOzp56GXD5brQtKNtu9urtge02kOwaGlHsK2I28BMdCRVFYJI9kEiqhqr342ZDlob7mpBCoNDk1uLLH2MPDAW9NOpq+V0bab+WawINAjl1GY/obL3zRsVNMoAszFSfdbbWS/KDbx6rw5bUPMC37s6LTbECkXHhqeqDlNQs4G9BccfiJNI5CQa+QPmaRNOBKhD2K97Z9fXmAFY155WzTPoIVKupxkPXo0zp/9vOc/HHEtMlkoUUNzxX5Q7T8awfN/7F4IfShXQKEVLaIStdx5istw7rxfuv1v/U+EMj4fmYUW9sNG/5irVyGAAOVvvPNkavLnl+NaKYysvAxYVPlrj+zJIDi5C91MmRhiTfH/Lgyq9Mlr/FaLIa/Ow6rCIjO4oBZSl9dXwLxFI4oQC
It's not encoded/decoded.
The authUrl I am using is
https://login.microsoftonline.com/common/oauth2/v2.0/authorize
If I attempted to use the v1.0 of the authorize endpoint, the phone OS appears to throw an error to me stating Authentication Error - Invalid state from server. Possible forgery!
I have set up an Azure App Service to act as my API and if I hit my app login url directly it gives me back a well formed token which I then could use to hit the api with no problem. So that tells me that my cliend_id are correct and everything should work.
So I am doing it wrong somewhere. I don't know if I trust the OAuth2Authenticator class from Xamarin when using MS accounts. But documentation is old and/or lacking. I feel like it's going to be an easy answer but I am going cross-eyed trying to interpret MS/Xamarin/Googled documentation.

Oauth2 Spring Security Resource Server and Independent Auth Server

everyone!
I'm new to Oauth2 and I've had different approaches with it.
I have a doubt. I'm actually building a Provider Server with Spring Security and I have an external access token provider (Google and AWS Cognito).
I know the process to get the access token following the code grant flow (Which is the one I want to implement). I built an Android app that gets the code and changes it for the access token.
My question is:
How do I validate that the token I'm sending to the Provider Server is a valid one using Spring Security to also access the protected resources that the server has?
Thank you in advance.
I think there are two alternatives either u get the public key and verify the token urself or maybe they have an endpoint where you can send the token and know if its a valid one or not.
Something like this
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
// Specify the CLIENT_ID of the app that accesses the backend:
.setAudience(Collections.singletonList(CLIENT_ID))
// Or, if multiple clients access the backend:
//.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
.build();
// (Receive idTokenString by HTTPS POST)
GoogleIdToken idToken = verifier.verify(idTokenString);
if (idToken != null) {
Payload payload = idToken.getPayload();
// Print user identifier
String userId = payload.getSubject();
System.out.println("User ID: " + userId);
// Get profile information from payload
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
// Use or store profile information
// ...
} else {
System.out.println("Invalid ID token.");
}
Link: https://developers.google.com/identity/sign-in/web/backend-auth

what protocol to use with ADFS when security webapi for non-browser clients

Our webapi endpoints are used for both browser based clients (angular) and non-browser based clients (restsharp) and the webapi are currently secured using passive WS-Federation as the protocol and ADFS as the STS. We currently use a rather convoluted workaround for the restsharp clients since passive WS-Federation really isn't optimal for non-browser clients so we would like to find a better way to secure our webapi endpoints for these types of clients without having to replace ADFS or add extra infrastructure.
My understanding is that OAuth2 "Resource Owner Password Credentials Grant" (grant_type=password) would support this scenario nicely but unfortunately it is currently not supported by ADFS.
So, my question is this, is there a nice way to use the one OAuth2 flow that ADFS supports, namely "Authorization Code Grant Flow" (grant_type=authorization_code) to support non-browser based clients?
If this is not possible, can I secure WebApi endpoints using WS-Trust and bearer tokens without resorting to using WCF?
It turns out it was possible to use WS-Trust to get a saml 2.0 token and a WebApi to consume it with a little help from Thinktecture IdentityModel. The following does not include claims transformation so if you need to add claims to the Principal, then a little more work is needed.
The owin startup for the webapi service needs to use the following from Thinktecture.IdentityModel.Owin:
app.UseSaml2BearerAuthentication(
audience: new Uri(ConfigurationManager.AppSettings["FederatedSecurity.Realm"]),
issuerThumbprint: ConfigurationManager.AppSettings["FederatedSecurity.Thumbprint"],
issuerName: ConfigurationManager.AppSettings["FederatedSecurity.Authority"]);
For the client to request the saml 2.0 token from ADFS
private static SecurityToken RequestSecurityToken()
{
var trustChannelFactory = new WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), new EndpointAddress(new Uri("https://yourAdfsServer/adfs/services/trust/13/usernamemixed"), new AddressHeader[0]))
{
TrustVersion = TrustVersion.WSTrust13,
Credentials = { UserName = { UserName = #"u$ern#me", Password = "p#ssw0rd" } }
};
var requestSecurityToken = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Bearer,
TokenType = TokenTypes.Saml2TokenProfile11,
AppliesTo = new EndpointReference(_audience)
};
RequestSecurityTokenResponse response;
var securityToken = trustChannelFactory.CreateChannel().Issue(requestSecurityToken, out response);
return securityToken;
}
And for the client to call the service (using HttpClient but RestSharp will also work)
private static void CallService(SecurityToken token)
{
using (HttpClient client = new HttpClient())
{
client.SetBearerToken(Convert.ToBase64String(Encoding.UTF8.GetBytes(token.ToTokenXmlString())));
var httpMessage = client.GetAsync(new Uri(_restEndpoint)).Result;
}
}

Resources