I'm trying to pull a timeline from my own twitter account in to a website using Scribe. I tried the provided Twitter example, but I'm always getting the following response from twitter:
{"errors":[{"message":"Could not authenticate you","code":32}]}
I tried this by using my own access token as well as a dynamically created one.
Code for using my own access token:
import org.scribe.builder.ServiceBuilder;
import org.scribe.builder.api.TwitterApi;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verb;
import org.scribe.oauth.OAuthService;
public class TwitterTest {
public static void main(String[] args) {
OAuthService service = new ServiceBuilder()
.provider(TwitterApi.SSL.class)
.apiKey("myApiKey")
.apiSecret("myApiSecret")
.build();
Token accessToken = new Token("myAccessToken", "myAccessTokenSecret");
OAuthRequest request = new OAuthRequest(Verb.GET, "https://api.twitter.com/1.1/statuses/user_timeline.json");
service.signRequest(accessToken, request);
Response response = request.send();
System.out.println(response.getBody());
}
}
Related
Using ADAL library in Java , I created token (using client credential provider) ,the resulted token is TokenCredentialAuthProvider which is of IAuthenticationProvider type. this token i was able to use to create a graph client.
But as i want to migrate from ADAL to MSAL ,I tried creating a token using MSAL4j (java) (using client credential provider) and the resulted token was of the form IAuthenticationResult , which i cant use to create Graph client as it requires an instance of IAuthenticationProvider.
how to create graph client using token of the form IAuthenticationResult (msal4j)
MS Graph clients creation require an instance of IAuthenticationProvider. Several built-in providers and samples are available in Choose a Microsoft Graph authentication provider based on scenario. If you already posses an token you can create your own class/implementation and return the token returned by IAuthenticationResult.getAccessToken() from within the getAuthorizationTokenAsync method.
E.g.
// MyAuthenticationProvider.java
package com.example;
import java.net.URL;
import java.util.concurrent.CompletableFuture;
import com.microsoft.graph.authentication.IAuthenticationProvider;
public class MyAuthenticationProvider implements IAuthenticationProvider {
private CompletableFuture<String> accessTokenFuture;
public MyAuthenticationProvider(String accessToken) {
this.accessTokenFuture = new CompletableFuture<>();
this.accessTokenFuture.complete(accessToken);
}
#Override
public CompletableFuture<String> getAuthorizationTokenAsync(URL requestUrl) {
return this.accessTokenFuture;
}
}
// App.java
package com.example;
import com.google.gson.Gson;
import com.microsoft.graph.authentication.IAuthenticationProvider;
import com.microsoft.graph.models.User;
import com.microsoft.graph.requests.GraphServiceClient;
public class App {
public static void main(String[] args) {
String msalAccessToken = args[0];
final IAuthenticationProvider auth = new MyAuthenticationProvider(msalAccessToken);
final GraphServiceClient graphClient = GraphServiceClient
.builder()
.authenticationProvider(auth)
.buildClient();
final User me = graphClient.me().buildRequest().get();
System.out.println(new Gson().toJson(me));
}
}
package com.example.GraphMS_API.service;
import java.util.Arrays;
import com.azure.identity.UsernamePasswordCredential; import
com.azure.identity.UsernamePasswordCredentialBuilder; import
com.microsoft.graph.authentication.TokenCredentialAuthProvider;
import com.microsoft.graph.models.User; import
com.microsoft.graph.requests.GraphServiceClient;
public class App {
public static void main(final String args[]) {
final UsernamePasswordCredential usernamePasswordCredential = new
UsernamePasswordCredentialBuilder()
.clientId("dfdfjhf").username("abc#xyz.com")
.password("1234").build();
final TokenCredentialAuthProvider tokenCredentialAuthProvider =
new TokenCredentialAuthProvider(
Arrays.asList("Mail.ReadWrite.Shared"), usernamePasswordCredential); final GraphServiceClient graphClient
= GraphServiceClient.builder()
.authenticationProvider(tokenCredentialAuthProvider).buildClient();
final User me = graphClient.me().buildRequest().get();
System.out.println(me.companyName);
} }
Have you tried isolating the issue from code and leveraging Postman/Graph Explorer to make same API call. Observe the behavior and see if you are able to obtain token.
Also make sure SDK you are trying to use is latest and updated.
Thanks!
I use Spring security to authenticate my spring websocket server. It works fine with the Basic authentication, but it went wrong when I changed to Digest authentication. I don't know what to put into the headers. Does someone know any solutions?
This is the websocket client code snippet:
SockJsClient sockJsClient;
WebSocketStompClient stompClient;
List<Transport> transports = new ArrayList<>();
final WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
headers.add("Authorization", "Basic YWRtaW46YWRtaW4=");
transports.add(new WebSocketTransport(new StandardWebSocketClient()));
sockJsClient = new SockJsClient(transports);
stompClient = new WebSocketStompClient(sockJsClient);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
......
Update:
It works well with digest for rest, the following codes can configure RestTempalte to use digest:
import java.net.URI;
import org.apache.http.HttpHost;
import org.apache.http.client.AuthCache;
import org.apache.http.client.HttpClient;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
public class HttpComponentsClientHttpRequestFactoryDigestAuth extends HttpComponentsClientHttpRequestFactory {
public HttpComponentsClientHttpRequestFactoryDigestAuth(HttpClient client) {
super(client);
}
#Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
return createHttpContext(uri);
}
private HttpContext createHttpContext(URI uri) {
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate DIGEST scheme object, initialize it and add it to the local auth cache
DigestScheme digestAuth = new DigestScheme();
// If we already know the realm name
digestAuth.overrideParamter("realm", "myrealm");
HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort());
authCache.put(targetHost, digestAuth);
// Add AuthCache to the execution context
BasicHttpContext localcontext = new BasicHttpContext();
localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache);
return localcontext;
}
}
Get a rest template:
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
public class RestTempleteConfig {
public RestTemplate getRestTemplate() {
CloseableHttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(provider())
.useSystemProperties().build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactoryDigestAuth(
client);
return new RestTemplate(requestFactory);
}
private CredentialsProvider provider() {
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("admin", "admin");
provider.setCredentials(AuthScope.ANY, credentials);
return provider;
}
}
Use rest template:
RestTemplate restTemplate = new RestTempleteConfig().getRestTemplate();
String uri = "http://localhost:8080/login";
ResponseEntity<String> entity = restTemplate.exchange(uri, HttpMethod.GET, null, String.class);
System.out.println(entity.getBody());
I have figured out the solution. Configure digest authentication with spring security at the sever side, then change client implementation to this:
RestTemplate restTemplate = new RestTempleteConfig().getRestTemplate();
SockJsClient sockJsClient;
WebSocketStompClient stompClient;
List<Transport> transports = new ArrayList<>();
final WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
StandardWebSocketClient websocketClient = new StandardWebSocketClient();
// add restTemplate first
transports.add(new RestTemplateXhrTransport(restTemplate));
transports.add(new WebSocketTransport(websocketClient));
sockJsClient = new SockJsClient(transports);
stompClient = new WebSocketStompClient(sockJsClient);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
The digest is configured in the rest template, what we need to do is to add it to Transport list. And you should add rest template first, then websocket, for it matters when creating the sockJs url. More details refer to this link.
I have a Keycloak protected backend that I would like to access via swagger-ui. Keycloak provides the oauth2 implicit and access code flow, but I was not able to make it work. Currently, Keycloak's documentation is lacking regarding which url should be used for authorizationUrl and tokenUrl within swagger.json.
Each realm within Keycloak offers a huge list of configuration urls by accessing http://keycloak.local/auth/realms/REALM/.well-known/openid-configuration
Furthermore I've tried to directly integrate the keycloak js-client within swagger-ui index.html by adding the following lines:
<script src="keycloak/keycloak.js"></script>
<script>
var keycloak = Keycloak('keycloak.json');
keycloak.init({ onLoad: 'login-required' })
.success(function (authenticated) {
console.log('Login Successful');
window.authorizations.add("oauth2", new ApiKeyAuthorization("Authorization", "Bearer " + keycloak.token, "header"));
}).error(function () {
console.error('Login Failed');
window.location.reload();
}
);
</script>
I also tried something like this after 'Login Successful'
swaggerUi.api.clientAuthorizations.add("key", new SwaggerClient.ApiKeyAuthorization("Authorization", "Bearer " + keycloak.token, "header"));
But it also doesn't work.
Any suggestions how I can integrate keycloak auth within swagger?
Swagger-ui can integrate with keycloak using the implicit authentication mode.
You can setup oauth2 on swagger-ui so that it will ask you to authenticate instead of giving swagger-ui the access token directly.
1st thing, your swagger need to reference a Security definition like:
"securityDefinitions": {
"oauth2": {
"type":"oauth2",
"authorizationUrl":"http://172.17.0.2:8080/auth/realms/master/protocol/openid-connect/auth",
"flow":"implicit",
"scopes": {
"openid":"openid",
"profile":"profile"
}
}
}
Then, you swagger-ui need to reference some other parameter: With the pure js, you can use in the index.html
const ui = SwaggerUIBundle({ ...} );
ui.initOAuth({
clientId: "test-uid",
realm: "Master",
appName: "swagger-ui",
scopeSeparator: " ",
additionalQueryStringParams: {"nonce": "132456"}
})
In this code,
authorizationUrl is the authorization endpoint on your keycloak realm
Scopes are something you can set to your needs
clientId is a client parametrized with implicit mode on keycloak realm
the additional parameter nonce should be random, but swagger-ui don't use it yet.
I add here an example if you want to do all this on Spring-boot:
On this framework, you will mainly use swagger and swagger-ui web-jar from Springfox. This is done by adding the dependencies:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
Swagger is enable by adding the annotation swagger2 on your main class:
#SpringBootApplication
#EnableSwagger2
public class TestSpringApplication {
...
then you can setup a Configuration class like this:
#Configuration
public class SwaggerConfigurer {
#Bean
public SecurityConfiguration securityConfiguration() {
Map<String, Object> additionalQueryStringParams=new HashMap<>();
additionalQueryStringParams.put("nonce","123456");
return SecurityConfigurationBuilder.builder()
.clientId("test-uid").realm("Master").appName("swagger-ui")
.additionalQueryStringParams(additionalQueryStringParams)
.build();
}
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.testspring"))
.paths(PathSelectors.any())
.build().securitySchemes(buildSecurityScheme()).securityContexts(buildSecurityContext());
}
private List<SecurityContext> buildSecurityContext() {
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(SecurityReference.builder().reference("oauth2").scopes(scopes().toArray(new AuthorizationScope[]{})).build());
SecurityContext context = SecurityContext.builder().forPaths(Predicates.alwaysTrue()).securityReferences(securityReferences).build();
List<SecurityContext> ret = new ArrayList<>();
ret.add(context);
return ret;
}
private List<? extends SecurityScheme> buildSecurityScheme() {
List<SecurityScheme> lst = new ArrayList<>();
// lst.add(new ApiKey("api_key", "X-API-KEY", "header"));
LoginEndpoint login = new LoginEndpointBuilder().url("http://172.17.0.2:8080/auth/realms/master/protocol/openid-connect/auth").build();
List<GrantType> gTypes = new ArrayList<>();
gTypes.add(new ImplicitGrant(login, "acces_token"));
lst.add(new OAuth("oauth2", scopes(), gTypes));
return lst;
}
private List<AuthorizationScope> scopes() {
List<AuthorizationScope> scopes = new ArrayList<>();
for (String scopeItem : new String[]{"openid=openid", "profile=profile"}) {
String scope[] = scopeItem.split("=");
if (scope.length == 2) {
scopes.add(new AuthorizationScopeBuilder().scope(scope[0]).description(scope[1]).build());
} else {
log.warn("Scope '{}' is not valid (format is scope=description)", scopeItem);
}
}
return scopes;
}
}
There is a lot of thing you can update in this code. This is mainly the same as before:
nonce which should be a random thing (swagger-ui don't use it yet)
clientId which you need to setup accordingly to the client you setup in keycloak
basePackage: You need to set the package in which all your controller are
If you need an api-key, you can enable it and add it on the security scheme list
LoginEndpoint: that need to be the authorization endpoint of you keycloak realm
scopeItems: the scopes you want for this authentication.
It will generate the same thing as before: Updating the swagger to add the securityDefinition and make swagger-UI take the parameter for clientId, nonce, ...
Was struggling with this setup for the past 2 days. Finally got a working solution for those who cannot resolve.
pom.xml
...
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-security-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
...
Enable Swagger on main class
...
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#SpringBootApplication
#EnableSwagger2
#EnableAsync
#EnableCaching
public class MainApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MainApplication.class);
app.run(args);
}
}
SwaggerConfig.java
package com.XXX.XXXXXXXX.app.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.AuthorizationCodeGrantBuilder;
import springfox.documentation.builders.OAuthBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.Arrays;
import static springfox.documentation.builders.PathSelectors.regex;
/*
* Setting up Swagger for spring boot
* https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api
*/
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Value("${keycloak.auth-server-url}")
private String AUTH_SERVER;
#Value("${keycloak.credentials.secret}")
private String CLIENT_SECRET;
#Value("${keycloak.resource}")
private String CLIENT_ID;
#Value("${keycloak.realm}")
private String REALM;
private static final String OAUTH_NAME = "spring_oauth";
private static final String ALLOWED_PATHS = "/directory_to_controllers/.*";
private static final String GROUP_NAME = "XXXXXXX-api";
private static final String TITLE = "API Documentation for XXXXXXX Application";
private static final String DESCRIPTION = "Description here";
private static final String VERSION = "1.0";
#Bean
public Docket taskApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName(GROUP_NAME)
.useDefaultResponseMessages(true)
.apiInfo(apiInfo())
.select()
.paths(regex(ALLOWED_PATHS))
.build()
.securitySchemes(Arrays.asList(securityScheme()))
.securityContexts(Arrays.asList(securityContext()));
}
private ApiInfo apiInfo() {
return new
ApiInfoBuilder().title(TITLE).description(DESCRIPTION).version(VERSION).build();
}
#Bean
public SecurityConfiguration security() {
return SecurityConfigurationBuilder.builder()
.realm(REALM)
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.appName(GROUP_NAME)
.scopeSeparator(" ")
.build();
}
private SecurityScheme securityScheme() {
GrantType grantType =
new AuthorizationCodeGrantBuilder()
.tokenEndpoint(new TokenEndpoint(AUTH_SERVER + "/realms/" + REALM + "/protocol/openid-connect/token", GROUP_NAME))
.tokenRequestEndpoint(
new TokenRequestEndpoint(AUTH_SERVER + "/realms/" + REALM + "/protocol/openid-connect/auth", CLIENT_ID, CLIENT_SECRET))
.build();
SecurityScheme oauth =
new OAuthBuilder()
.name(OAUTH_NAME)
.grantTypes(Arrays.asList(grantType))
.scopes(Arrays.asList(scopes()))
.build();
return oauth;
}
private AuthorizationScope[] scopes() {
AuthorizationScope[] scopes = {
new AuthorizationScope("user", "for CRUD operations"),
new AuthorizationScope("read", "for read operations"),
new AuthorizationScope("write", "for write operations")
};
return scopes;
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(Arrays.asList(new SecurityReference(OAUTH_NAME, scopes())))
.forPaths(PathSelectors.regex(ALLOWED_PATHS))
.build();
}
}
From terminal, run "mvnw spring-boot:run"
Open browser and hit http://localhost:[port]/[app_name]/swagger-ui.html.
Click the Authorize button:
Swagger Authorize Button
This should present a modal to confirm your keycloak settings.
Click Authorize button once again. You should be redirected to a login screen.
Once credentials are entered and confirmed, you will be redirected back to Swagger-UI fully authenticated.
Swagger-ui + Keycloak (or any other OAuth2 provider) using implicit flow, OpenAPI 3.0 template:
components:
...
securitySchemes:
my_auth_whatever:
type: oauth2
flows:
implicit:
authorizationUrl: https://MY-KEYCLOAK-HOST/auth/realms/MY-REALM-ID/protocol/openid-connect/auth
scopes: {}
...
security:
- my_auth_whatever: []
Make sure the implicit flow is enabled in Keycloak settings for the client that you use.
One downside is that the user is still asked for client_id in the modal when clicks on "Authorize" button in Swagger UI.
The value that user enters may be overwritten by adding query param ?client_id=YOUR-CLIENT-ID to the authorizationUrl but it's kinda the dirty hack and the modal is still showed to the user.
When running swagger-ui in docker - the OAUTH_CLIENT_ID env var may be provided to container to set the default client_id value for the modal.
For non-docker deployment refer to #wargre's approach with changing the index.html (not sure if there's a better way).
For SwaggerAPI (OpenAPI 2.0) example refer to first code snippet in #wargre's answer and this doc: https://swagger.io/docs/specification/2-0/authentication/
I had been trying to get my timeline messages through some Java code explained in the http://www.dreamincode.net/forums/blog/114/entry-4459-demo-of-twitter-application-only-oauth-authentication-using-java blog and wrote some code in Java.
I am using application-only authentication and using consumer key and consumer secret. Here is a portion of my code where I am requesting the bearer token:
bearerToken=requestBearerToken("https://api.twitter.com/1.1/statuses/besra_u_timeline.json");
Is this the right way? I ask because in the requestBearerToken method
private static String requestBearerToken(String endPointUrl) throws IOException {
HttpsURLConnection connection = null;
URL url = new URL(endPointUrl);
connection = (HttpsURLConnection) url.openConnection();
try {
connection.getInputStream();
} catch(IOException e){
System.out.println("IOException");
}
I am constantly getting an IOException which means that I am not getting the input stream from the connection object. Please help me to get the tweets.
Ya i have got a way to do it using the twitter4j api.Consumer key , consumer secret, access token and access secret u can get once u register ur app in dev.twitter.com/apps
import twitter4j.ResponseList;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.Status;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.conf.ConfigurationBuilder;
public class SimpleCrawler {
static ResponseList<Status> statuses;
public static void main(String[] args) throws TwitterException {
ConfigurationBuilder cb=new ConfigurationBuilder();
cb.setDebugEnabled(true)
.setOAuthConsumerKey("your consumer key")
.setOAuthConsumerSecret("your con secret")
.setOAuthAccessToken("your access token")
.setOAuthAccessTokenSecret("access token secret");
TwitterFactory tf=new TwitterFactory(cb.build());
Twitter tw = tf.getInstance();
try {
statuses=tw.getHomeTimeline();
System.out.println("Connected to twitter");
} catch (TwitterException e) {
System.out.println("Cannot connect to Twitter");
System.exit(0);
}
System.out.println("Reading TimeLine...");
for(Status st1:statuses){
String data[]={st1.getUser().getName(),String.valueOf(st1.getCreatedAt()),twtmsg};
System.out.println(data);
}
}
}
Through twitter api also its possible but don't know . One problem with this method is that it fetches 20 tweets only at a time ,any help would be appreciated.suggestions are most welcome plz comment.
You need to use Paging:
Paging paging = new Paging();
paging.setPage(1);
ResponseList<Status> statuses = twitter.getHomeTimeline(paging);
System.out.println(statuses.size());
paging.setPage(2);
statuses.addAll(twitter.getHomeTimeline(paging));
System.out.println(statuses.size());
The above simplified code will give you two pages worth of statuses