I'm developing an "Apache Oltu Spring MVC Github" integration example. In this example I will be sending "App ID" and "Secret" to get the "access_token" in order to access the protected resources like "Gist", "user" etc.
So first step is to create / register the "App" using https://github.com/settings/applications/new.
Once you create a App make sure to Note: AppID and Secret, we need these values to be used in Spring code.
To develop this functionality / code - I search a lot and I did not find any ready made code. So I decided to furnish / explain my code below. So one can find these links useful.
I've taken a reference of following URL's to developed whole code:-
https://developer.github.com/v3/oauth/
https://cwiki.apache.org/confluence/display/OLTU/OAuth+2.0+Client+Quickstart
http://www.jasha.eu/blogposts/2013/09/retrieve-facebook-profile-data-java-apache-oltu.html
Attached is the screen shot to register the "App" on Github. "MyApp" is the App that I created.
Use the same code from http://www.jasha.eu/blogposts/2013/09/retrieve-facebook-profile-data-java-apache-oltu.html, just make sure to change the
AUTHORIZATION_URL = "https://github.com/login/oauth/authorize";
ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token"
To get Protected Resource like User Profile use: https://api.github.com/user
The output I get when run the code:
The user4798111 has mentioned is correct and just adding some more details. Pre-requisite, you need to register App on Github. Once you registered the App, you will get the CLIENT_SECRET,CLIENT_ID to be used to get the Protected resources from the github.
If you're using the OAuthClientRequest API to to make an initial call, you need to have the following details
private static final String AUTHORIZATION_URL = "https://github.com/login/oauth/authorize";
private static final String CLIENT_ID = "8515a1e4XXXXXXX";
private static final String CLIENT_SECRET = "ae3487601d891d257f7193XXXXXXXXX";
private static final String REDIRECT_URL = "http://localhost:8080/apache-oltu/github/redirect";
private static final String ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
#RequestMapping(value = "/auth", method = RequestMethod.GET)
public String authenticate() throws OAuthSystemException {
OAuthClientRequest request = OAuthClientRequest
.authorizationLocation(AUTHORIZATION_URL)
.setClientId(CLIENT_ID)
.setRedirectURI(REDIRECT_URL)
.setResponseType("code")
.setScope("user,gist,user:email,user:follow,public_repo,repo,repo_deployment,repo:status,repo:invite")
.buildQueryMessage();
System.out.println("REDIRECT TO: "+request.getLocationUri());
return "redirect:" + request.getLocationUri();
}
The same something simllar you would need to use like below
request= new OAuthBearerClientRequest("https://api.github.com/user").
setAccessToken(oAuthResponse.getAccessToken()).
buildQueryMessage();
The information about the scopes and other details can be found here:
https://developer.github.com/apps/building-integrations/setting-up-and-registering-oauth-apps/about-authorization-options-for-oauth-apps/
https://developer.github.com/apps/building-integrations/setting-up-and-registering-oauth-apps/about-scopes-for-oauth-apps/
The result which could see is below for reference:
{
"login":"JavaHelper",
"id":8208031,
"avatar_url":"https://avatars0.githubusercontent.com/u/8208031?v=4",
"gravatar_id":"",
"url":"https://api.github.com/users/JavaHelper",
"html_url":"https://github.com/JavaHelper",
"followers_url":"https://api.github.com/users/JavaHelper/followers",
"following_url":"https://api.github.com/users/JavaHelper/following{/other_user}",
"gists_url":"https://api.github.com/users/JavaHelper/gists{/gist_id}",
"starred_url":"https://api.github.com/users/JavaHelper/starred{/owner}{/repo}",
"subscriptions_url":"https://api.github.com/users/JavaHelper/subscriptions",
"organizations_url":"https://api.github.com/users/JavaHelper/orgs",
"repos_url":"https://api.github.com/users/JavaHelper/repos",
"events_url":"https://api.github.com/users/JavaHelper/events{/privacy}",
"received_events_url":"https://api.github.com/users/JavaHelper/received_events",
"type":"User",
"site_admin":false,
"name":"JavaProgramer",
"company":null,
"blog":"",
"location":null,
"email":null,
"hireable":null,
"bio":null,
"public_repos":45,
"public_gists":0,
"followers":4,
"following":60,
"created_at":"2014-07-19T10:03:42Z",
"updated_at":"2017-09-09T12:55:57Z",
"private_gists":0,
"total_private_repos":0,
"owned_private_repos":0,
"disk_usage":142270,
"collaborators":0,
"two_factor_authentication":false,
"plan":{
"name":"free",
"space":976562499,
"collaborators":0,
"private_repos":0
}
}
Related
I am working on a Spring Boot application, which is basically a resource server. As of now, my application has one tenant, which gets authenticated with an authorization server, external to my application.
In order to achieve the same, as of now, I have made the following changes in my application:
config changes are as following:
spring.security.oauth2.client.registration.tenant1.client-id=abcd
spring.security.oauth2.client.registration.tenant1.client-authentication-method=basic
spring.security.oauth2.client.registration.tenant1.authorization-grant-type=authorization_code
myapp.oauth2.path=https://external.authorization.server/services/oauth2/
spring.security.oauth2.client.provider.tenant1.token-uri=${myapp.oauth2.path}token
spring.security.oauth2.client.provider.tenant1.authorization-uri=${myapp.oauth2.path}authorize
spring.security.oauth2.client.provider.tenant1.user-info-uri=${myapp.oauth2.path}userinfo
spring.security.oauth2.client.provider.tenant1.user-name-attribute=name
As of now, I am fetching client secrets from Vault, so I had to define the OAuth2 configuration as follows:
#EnableConfigurationProperties(OAuth2ClientProperties.class)
#Conditional(ClientsConfiguredCondition.class)
#Configuration
public class OAuth2Configuration {
static final String OAUTH2_CLIENT_SECRET_KEY = "oauth2_client_secret";
private static final Logger log = LoggerFactory.getLogger(OAuth2Configuration.class);
private static final String OAUTH2_REGISTRATION_MISSING =
"oAuth2 registration properties are missing";
private final ApplicationSecretProvider applicationSecretProvider;
private final Map<String, ClientAuthenticationMethod> clientAuthenticationMethodMap =
new HashMap<>();
private final String authenticationMethod;
public OAuth2Configuration(
#Value("${spring.security.oauth2.client.registration.tenant1.client-authentication-method}")
final String authenticationMethod,
final ApplicationSecretProvider applicationSecretProvider) {
this.authenticationMethod = authenticationMethod;
this.applicationSecretProvider = applicationSecretProvider;
this.clientAuthenticationMethodMap
.put(ClientAuthenticationMethod.POST.getValue(), ClientAuthenticationMethod.POST);
this.clientAuthenticationMethodMap
.put(ClientAuthenticationMethod.BASIC.getValue(), ClientAuthenticationMethod.BASIC);
this.clientAuthenticationMethodMap
.put(ClientAuthenticationMethod.NONE.getValue(), ClientAuthenticationMethod.NONE);
}
#Bean
public InMemoryClientRegistrationRepository getClientRegistrationRepository(
OAuth2ClientProperties properties) {
List<ClientRegistration> registrations = new ArrayList<>(
OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties).values());
//We will have only one client registered for oAuth
if (CollectionUtils.isEmpty(registrations)) {
log.error(OAUTH2_REGISTRATION_MISSING);
throw new IllegalStateException(OAUTH2_REGISTRATION_MISSING);
}
ClientRegistration registration = registrations.get(0);
ClientRegistration.Builder builder = ClientRegistration.withClientRegistration(registration);
ClientAuthenticationMethod clientAuthenticationMethod =
getClientAuthenticationMethod(authenticationMethod);
ClientRegistration completeRegistration = builder
.clientSecret(applicationSecretProvider.getSecretForKey(OAUTH2_CLIENT_SECRET_KEY))
.clientAuthenticationMethod(clientAuthenticationMethod)
.build();
return new InMemoryClientRegistrationRepository(completeRegistration);
}
protected ClientAuthenticationMethod getClientAuthenticationMethod(String grantType) {
ClientAuthenticationMethod retValue = clientAuthenticationMethodMap.get(grantType);
if (retValue == null) {
return ClientAuthenticationMethod.NONE;
}
return retValue;
}
}
Then I extended DefaultOAuth2UserService in order to save user details in my application as follows:
#Component
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private UserRepository userRepository;
private AuthorityRepository authRepository;
#Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Autowired
public void setAuthorityRepository(AuthorityRepository
authorityRepository) {
this.authorityRepository = authorityRepository;
}
#Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
DefaultOAuth2User oAuth2User = (DefaultOAuth2User) super.loadUser(userRequest);
Collection<GrantedAuthority> authorities = new HashSet<>(oAuth2User.getAuthorities());
Map<String, Object> attributes = oAuth2User.getAttributes();
...
return new DefaultOAuth2User(authorities, oAuth2User.getAttributes(), userNameAttributeName);
}
}
Security configuration is as follows:
#EnableWebSecurity
#Import(SecurityProblemSupport.class)
#ConditionalOnProperty(
value = "myapp.authentication.type",
havingValue = "oauth",
matchIfMissing = true
)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CustomOAuth2UserService customoAuth2UserService;
public SecurityConfiguration(CustomOAuth2UserService customoAuth2UserService) {
this.customoAuth2UserService = customoAuth2UserService;
}
public void configure(HttpSecurity http) throws Exception {
http
.csrf()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers("/api/auth-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.anyRequest().authenticated()
//.and().oauth2ResourceServer().jwt()
.and()
//.and()
.oauth2Login()
.redirectionEndpoint()
.baseUri("/oauth2**")
.and()
.failureUrl("/api/redirectToHome")
.userInfoEndpoint().userService(customoAuth2UserService);
http.cors().disable();
}
}
Now, I would like to onboard multiple tenants using OAuth2 as well. Say I want to onboard another tenant tenant2. In order to achieve this, I think, I need to do the following changes in the existing code base as follows:
adding config entries in the properties file as above:
spring.security.oauth2.client.registration.tenant2.client-id=efgh
spring.security.oauth2.client.registration.tenant2.client-authentication-method=basic
spring.security.oauth2.client.registration.tenant2.authorization-grant-type=authorization_code
spring.security.oauth2.client.provider.tenant2.token-uri=${myapp.oauth2.path}token
spring.security.oauth2.client.provider.tenant2.authorization-uri=${myapp.oauth2.path}authorize
spring.security.oauth2.client.provider.tenant2.user-info-uri=${myapp.oauth2.path}userinfo
spring.security.oauth2.client.provider.tenant2.user-name-attribute=name
I need to do changes in the security configuration class:
SecurityConfiguration and OAuth2 configuration class OAuth2Configuration as well. But I am not able to understand what should I add there in order to make my applications work seamlessly for multiple tenants.
In this context, I found this related post: Dynamically register OIDC client with Spring Security OAuth in a multi-tenant stup, but could not get any concrete idea regarding what changes should I do in the existing code base to make my application work in multi-tenancy set up.
Could anyone please help here?
I think there's a bit of confusion that it might help to clear up.
First, it seems that you are not actually building a resource server, as a resource server would require an access token for authentication. Using .oauth2Login() is for either OAuth 2.0 or OpenID Connect 1.0 login, which is a regular application in most respects except how you log in. You still have a browser session after login is successful, which you would not have in a resource server.
Second, configuring a static number of client registrations isn't really quite the same as building a multi-tenant application. Perhaps you're building up to that later, by demonstrating two clients. When configuring two clients using static configuration properties, nothing is really different from a single configuration, other than that there are two possible registrationIds.
Start by building a simple hello world application, such as the OAuth 2.0 Login Sample. If you add a second client registration to your properties, you'll notice that the auto-generated login page (/login) simply shows two links, one for each client. See docs for more on this.
The default URI for initiating the authorization_code flow is /oauth2/authorization/{registrationId}, which means navigating to /oauth2/authorization/abcd launches the first client's login flow. Navigating to /oauth2/authorization/efgh launches the second client's login flow. There's not really anything else needed to support multiple login clients other than understanding how to initiate login.
If you wish to support a fully multi-tenant login configuration, you would then provide a custom ClientRegistrationRepository, which you have done. The only difference is that you should no longer seek to configure clients through the Spring Boot properties, as that seems to be the point that is confusing in your example. If you want to use properties for some of the configuration, create your own configuration properties for your custom repository implementation. Typically at that point, all of this configuration would come from a database.
I would start with that progression (hello world, two statically configured clients, custom ClientRegistrationRepository) then proceed to add other custom components. It will help illustrate the differences at each point.
I am developing an application using Uber API, I used Retrofit library for retrieving data from API.
I have authorized my application using below endpoint:
https://login.uber.com/oauth/v2/authorize?client_id=<CLIENT_ID>&response_type=code
But when I tried to get estimates of products using below endpoint:
https://api.uber.com/v1.2/requests/estimate?start_latitude=37.7752278&start_longitude=-122.4197513&end_latitude=37.7773228&end_longitude=-122.4272052
Then I got an issue:
This endpoint requires at least one of the following scopes: profile, surge_accept, request.delegate.tos_accept, request, request.delegate","code":"unauthorized
Before implementing this endpoint I have completely done authorization & token procedure by following Uber official doc.
Also I'm showing my source code below please let me know where I'm going wrong:
----> Intializing UBER SDK <----
public void initializeUber() {
if (!UberSdk.isInitialized()) {
configuration = new SessionConfiguration.Builder().setClientId("LWOUTh3AUBkVtaI-cK58-t_pspsvRFfk").setServerToken("J5MNweewRs8vj4-dC0r9OMI4-qjibix0xv6gncGs").setRedirectUri("REDIRECT_URL").setScopes(Arrays.asList(Scope.REQUEST)).setEnvironment(SessionConfiguration.Environment.SANDBOX).build();
UberSdk.initialize(configuration);
accessTokenManager = new AccessTokenManager(context);
System.out.println("Configuration- ---->
"+configuration.toString());
loginManager = new LoginManager(accessTokenManager, new UberLoginCallback(context), configuration, LOGIN_CODE);
} else {
Log.i(TAG, "Uber SDK already initialized");
}
}
public LoginManager getLoginManager() {
PreferenceManager(context).setUberAuthToken(loginManager.getAccessTokenManager().getAccessToken().toString());
System.out.println("Is authenticated-+loginManager.getAccessTokenManager().getAccessToken());
return loginManager;
}
-----> Created API Interface <----
#POST("v1.2/requests/estimate?")
Call<RequestEstimateFare> getRequestEstimateFare(#Query("start_latitude") String start_latitude, #Query("start_longitude") String start_longitude, #Query("end_latitude") String end_latitude, #Query("end_longitude") String end_longitude);
----> Retrofit Library calling <-----
apiInterface = retrofitConfig.createService(ApiInterface.class);
retrofitConfig.changeApiBaseUrl("https://api.uber.com/");
apiInterface.getRequestEstimateFare ("37.7752315", "-122.418075", "37.7752415", "-122.518075").enqueue(new Callback<RequestEstimateFare>() {
#Override
public void onResponse(Call<RequestEstimateFare> call, Response<RequestEstimateFare> response) {
}
#Override
public void onFailure(Call<RequestEstimateFare> call, Throwable t) {
}
});
I'm still getting the same issue.
The problem is you have not requested the 'request' scope during the oauth authorization process.
like:
https://login.uber.com/oauth/v2/authorize?client_id=<CLIENT_ID>&response_type=code&scope=request
or if you want all scope then you need to pass all in link like:
https://login.uber.com/oauth/v2/authorize?client_id=<CLIENT_ID>&response_type=code&scope=request request_receipt history profile
Firstly, are you making this request as one of your 5 approved developer accounts? or have you requested FULL ACCESS?
https://developer.uber.com/docs/riders/references/api/v1.2/requests-estimate-post
Privileged Scope This endpoint requires a privileged scope to be used
in production by all Uber riders. You can use this endpoint
immediately when authenticated as yourself or any of your 5 registered
developers. When you are ready to distribute your application broadly
for use by all Uber riders, you may request FULL ACCESS. For more
information read about scopes.
Secondary to that I'm not sure how your request is working using query params instead of a JSON request body? I can only get your query working using
{"start_latitude": 37.7752278, "start_longitude": -122.4197513, "end_latitude": 37.7773228, "end_longitude":-122.4272052}
Assuming I have a Spring Application whose requests are being authorized with http.authorizeRequests() and #PreAuthorize
Is there a way to get these mappings programmatically?
Is there an api within Spring where I can retrieve which urls are authorized for the currently authenticated user?
I dont know if there are better ways similar to we pass request header like #RequestHeader("Content-Type")... i would love to see that ...
#PreAuthorize("authorizeAccess(#user,#contentType,)")
public Idea somemethod(#RequestBody User user, #RequestHeader("Content-Type") String contentType )
{..}
But below is definitely an option.
Assuming you know how to write overrride or write your own methods to use in spring security (customised class extending SecurityExpressionRoot). If so if you have a method i.e..authorizeAccess
public boolean authorizeAccess(String mapping)
{
// use mapping to authorize
.. do something
}
Then, we can have all urls in a constant file..
package org.somepackage;
public interface URLMappings {
public static String ADMIN = "/admin";
}
Then pass these urls as constants ..
#RequestMapping(value = URLMappings.ADMIN, method = RequestMethod.POST)
#PreAuthorize("authorizeAccess(T(org.somepackage.URLMappings).ADMIN)")
How do I define basic authentication using Swagger 2.0 annotations and have it display in swagger UI.
In the resource I have:
#ApiOperation(value = "Return list of categories", response=Category.class, responseContainer="List", httpMethod="GET", authorizations = {#Authorization(value="basicAuth")})
public Response getCategories();
I looked here:
https://github.com/swagger-api/swagger-core/wiki/Annotations#authorization-authorizationscope
And it says "Once you've declared and configured which authorization schemes you support in your API, you can use these annotation to note which authorization scheme is required on a resource or a specific operation" But I can't find anything that talks about where to declare and configure the authorization schemes.
Update:
I found code on how to declare the schema, but I still do not see any information about the authentication schema in the UI. I'm not sure what I am missing
#SwaggerDefinition
public class MyApiDefinition implements ReaderListener {
public static final String BASIC_AUTH_SCHEME = "basicAuth";
#Override
public void beforeScan(Reader reader, Swagger swagger) {
}
#Override
public void afterScan(Reader reader, Swagger swagger) {
BasicAuthDefinition basicAuthDefinition = new BasicAuthDefinition();
swagger.addSecurityDefinition(BASIC_AUTH_SCHEME, basicAuthDefinition);
}
}
Using Springfox 2.6 annotations, you must first define Basic authentication as one of the security schemes when you set up the Docket in your configuration, like this:
List<SecurityScheme> schemeList = new ArrayList<>();
schemeList.add(new BasicAuth("basicAuth"));
return new
Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo)
.securitySchemes(schemeList)
...
Then you can use the Springfox annotations in your service to set Basic Auth for the operation for which you want to require authentication:
#ApiOperation(value = "Return list of categories", response=Category.class, responseContainer="List", httpMethod="GET", authorizations = {#Authorization(value="basicAuth")})
public Response getCategories();
I struggeled with this as well. In my case i used the swagger-maven-plugin. To solve this i added this within the maven plugin:
<securityDefinitions>
<securityDefinition>
<name>basicAuth</name>
<type>basic</type>
</securityDefinition>
</securityDefinitions>
After that i was able to add it on my resource like this:
#Api(value = "My REST Interface", authorizations = {#Authorization(value="basicAuth")})
The generated json included the security element for each endpoint:
"security":[{
"basicAuth" : []
}]
And the security definition:
"securityDefinitions" : {
"basicAuth" : {
"type" : "basic"
}
}
I hope this helps others as well.
You can use the #SwaggerDefinition
http://swagger.io/customizing-your-auto-generated-swagger-definitions-in-1-5-x/
or you can configure the swagger object directly, here's an example
http://www.programcreek.com/java-api-examples/index.php?source_dir=rakam-master/rakam/src/main/java/org/rakam/WebServiceRecipe.java
I am creating a web app for a project. I want to allow my users to post there blogs onto twitter using the twitter API. they will generate a blog inside my website and if they would like to share their blog via twitter. No so much testing if the Twitter API works, more as if it works inside of my website, as in if my syntax is appropriate, and if how i am incorporating it is correct. Its for a class project.
Assuming you are using C# / ASP.NET, you would be writing a class to make your API calls which can be tested independently. I would suggest downloading the open source Twitterizer DLL and plugging that into your web project, mostly for the OAuth implementation.
So, if you were posting a Tweet, you could write a static method as follows:
public static bool CreateTweet(Twitterizer.OAuthTokens tokens, string tweetText)
{
var response = Twitterizer.TwitterStatus.Update(tokens, text);
return response.Result == Twitterizer.RequestResult.Success;
}
And your test code would look like this:
void Test()
{
var tokens = new Tw.OAuthTokens
{
_accessToken,
_accessTokenSecret,
_consumerKey,
_consumerSecret,
};
var testTweet = "test tweet text";
CreateTweet(tokens, testTweet);
}
You would need to obtain the tokens either via Twitter's login process or have them stored if you don't expect users to log in.
And then basically the test code above would be moved into the appropriate piece of code-behind in your ASP.NET application and it should just work!