I'm trying to create a group room using Twilio REST API, but i am facing a crash:
Process: com.example.twilioroom, PID: 25401
java.lang.NoSuchFieldError: No static field INSTANCE of type Lorg/apache/http/conn/ssl/AllowAllHostnameVerifier; in class Lorg/apache/http/conn/ssl/AllowAllHostnameVerifier; or its superclasses (declaration of 'org.apache.http.conn.ssl.AllowAllHostnameVerifier' appears in /system/framework/framework.jar!classes2.dex)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.<clinit>(SSLConnectionSocketFactory.java:151)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.getSystemSocketFactory(SSLConnectionSocketFactory)
Here is my code where i'm trying to verify hostname:
Twilio.init(multiAccountSID,multiAccountAuthToken)
val httpClientBuilder = HttpClientBuilder.create()
httpClientBuilder.setSSLHostnameVerifier(object : HostnameVerifier{
override fun verify(hostname: String?, session: SSLSession?): Boolean {
certs = try {
session!!.peerCertificates
} catch (e: SSLException) {
return false
}
val x509: X509Certificate = certs[0] as X509Certificate
val hostName = hostname!!.trim().toLowerCase(Locale.ENGLISH)
val firstCn: String = getFirstCn(x509)
if (Pattern.matches(hostName, firstCn)) {
return true
}
for (cn in getDNSSubjectAlts(x509)) {
if (Pattern.matches(hostName, cn!!)) {
return true
}
}
return true
}
})
val verifier = SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
val sslSocketFactory = SSLConnectionSocketFactory.getSocketFactory()
httpClientBuilder.setSSLSocketFactory(sslSocketFactory)
httpClientBuilder.build()
val networkHttpClient = NetworkHttpClient(httpClientBuilder)
val twilioRestClient = TwilioRestClient.Builder(multiAccountSID,multiAccountAuthToken).httpClient(networkHttpClient).build()
Log.d("networkHttpClient", "getAccessToken: "+networkHttpClient.lastResponse.statusCode)
but i'm getting error on:
val sslSocketFactory = SSLConnectionSocketFactory.getSocketFactory()
Can someone help me what I'm doing wrong?
The Twilio Java library is not built to be used in an Android application. This is because the Twilio library requires your account credentials in order to make requests to the API and if your application is handling those credentials a malicious user could decompile the application, extract the credentials and use them to abuse your account.
Instead, you should make the requests to the Twilio API from a server side application, where you can keep the API credentials safe, and trigger that request from your application.
Here is more about why you should not make API requests from your Android application and an example how to build a server side application that can make these requests for your application (the example is to send an SMS, but you can switch that out for using the Verify API).
Related
We have problems with obtaining ID token using auth0 SDK.
We have API Gateway based on Spring Cloud Gateway (version 3.1.4) where we try to use your auth0 platform to authenticate the users and then route the exchange to our micro services. To do it we would like to use ID Token and get email from it and pass this email to our micro services.
We log in by hitting oauth2/authorization/auth0 endpoint, we are being redirected to auth0 login page, where we provide credentials, then we get redirect back to our app.
When we configuire endpoints directly in API Gateway and mark them with #AuthenticationPrincipal OidcUser user it works and we have full user details as well as ID token.
When we proxy the exchange to different service we have Authorisation header in the request, which contains only header & signature part without payload in the ID token.
We would need the payload in ID Token in order to fetch user email for mapping the user with our internal DB in our micro services.
What do you think would be the proper workflow in this case and how can we solve this issue?
We tried to use Rules & Actions, which I paste below, but it didn’t helped us.
Our configuration looks like this:
#Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) throws Exception {
return http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/test").authenticated()
.anyExchange().authenticated()
.and().oauth2Login()
.and().logout().logoutSuccessHandler(logoutSuccessHandler())
.and().build();
}
In the RouteLocator in GatewayConfiguration we have filter for TokenRelay.
Our Action looks like this:
exports.onExecutePostLogin = async (event, api) => {
const namespace = 'http://test.{our_local_development_route}:8888';
if (event.authorization) {
api.idToken.setCustomClaim(`${namespace}/claims/email`, event.user.email);
api.accessToken.setCustomClaim(`${namespace}/email`, event.user.email);
}
};
And Rule:
function addEmailToAccessToken(user, context, callback) {
// This rule adds the authenticated user's email address to the access token.
const namespace = 'http://test.{our_local_development_route}:8888';
context.idToken[namespace + 'email'] = user.upn;
context.accessToken[namespace + 'email'] = user.email;
return callback(null, user, context);
}
I recently created a microservices architecture with Spring Cloud Gateway and Auth0. I wrote about how I created it on the Auth0 blog. That's not the interesting part. The interesting part is JHipster generates a ReactiveJwtDecoder that calls a /userinfo endpoint if some claims aren't available in the access token. This way, the access token is enriched with identity information before it's relayed to downstream microservices.
#Bean
ReactiveJwtDecoder jwtDecoder(ReactiveClientRegistrationRepository registrations) {
Mono<ClientRegistration> clientRegistration = registrations.findByRegistrationId("oidc");
return clientRegistration
.map(oidc ->
createJwtDecoder(
oidc.getProviderDetails().getIssuerUri(),
oidc.getProviderDetails().getJwkSetUri(),
oidc.getProviderDetails().getUserInfoEndpoint().getUri()
)
)
.block();
}
private ReactiveJwtDecoder createJwtDecoder(String issuerUri, String jwkSetUri, String userInfoUri) {
NimbusReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(jwkSetUri);
OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(jHipsterProperties.getSecurity().getOauth2().getAudience());
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
jwtDecoder.setJwtValidator(withAudience);
return new ReactiveJwtDecoder() {
#Override
public Mono<Jwt> decode(String token) throws JwtException {
return jwtDecoder.decode(token).flatMap(jwt -> enrich(token, jwt));
}
private Mono<Jwt> enrich(String token, Jwt jwt) {
// Only look up user information if identity claims are missing
if (jwt.hasClaim("given_name") && jwt.hasClaim("family_name")) {
return Mono.just(jwt);
}
// Retrieve user info from OAuth provider if not already loaded
return users.get(
jwt.getSubject(),
s -> {
WebClient webClient = WebClient.create();
return webClient
.get()
.uri(userInfoUri)
.headers(headers -> headers.setBearerAuth(token))
.retrieve()
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})
.map(userInfo ->
Jwt
.withTokenValue(jwt.getTokenValue())
.subject(jwt.getSubject())
.audience(jwt.getAudience())
.headers(headers -> headers.putAll(jwt.getHeaders()))
.claims(claims -> {
String username = userInfo.get("preferred_username").toString();
// special handling for Auth0
if (userInfo.get("sub").toString().contains("|") && username.contains("#")) {
userInfo.put("email", username);
}
// Allow full name in a name claim - happens with Auth0
if (userInfo.get("name") != null) {
String[] name = userInfo.get("name").toString().split("\\s+");
if (name.length > 0) {
userInfo.put("given_name", name[0]);
userInfo.put("family_name", String.join(" ", Arrays.copyOfRange(name, 1, name.length)));
}
}
claims.putAll(userInfo);
})
.claims(claims -> claims.putAll(jwt.getClaims()))
.build()
);
}
);
}
};
}
i'm currently trying to connect via UNO-Plattform sample to the Spotify API.
https://github.com/unoplatform/Uno.Samples/blob/master/UI/Authentication.OidcDemo/Authentication.OidcDemo/Authentication.OidcDemo.Shared/MainPage.xaml.cs
Therefore I have updated the PrepareClient method.
private async void PrepareClient()
{
var redirectUri = WebAuthenticationBroker.GetCurrentApplicationCallbackUri().OriginalString;
// Create options for endpoint discovery
var options = new OidcClientOptions
{
Authority = "https://accounts.spotify.com", //"https://demo.duendesoftware.com/",
ClientId = "7c1....a45",
ClientSecret = "4b..a",
Scope = "playlist-read-private",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect,
Flow = OidcClientOptions.AuthenticationFlow.AuthorizationCode
};
// Create the client. In production application, this is often created and stored
// directly in the Application class.
_oidcClient = new OidcClient(options);
var extra_parameters = new Dictionary<string, string>();
//extra_parameters.Add("response_type", "token"); // if i add this line i get an error
_loginState = await _oidcClient.PrepareLoginAsync(extra_parameters);
btnSignin.IsEnabled = true;
// Same for logout url.
//If i add this line a get an error
//_logoutUrl = new Uri(await _oidcClient.PrepareLogoutAsync(new LogoutRequest()));
btnSignout.IsEnabled = true;
}
private async void SignIn_Clicked(object sender, RoutedEventArgs e)
{
var startUri = new Uri(_loginState.StartUrl);
// Important: there should be NO await before calling .AuthenticateAsync() - at least
// on WebAssembly, in order to prevent triggering the popup blocker mechanisms.
var userResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, startUri);
if (userResult.ResponseStatus != WebAuthenticationStatus.Success)
{
txtAuthResult.Text = "Canceled";
// Error or user cancellation
return;
}
// User authentication process completed successfully.
// Now we need to get authorization tokens from the response
var authenticationResult = await _oidcClient.ProcessResponseAsync(userResult.ResponseData, _loginState);
if (authenticationResult.IsError)
{
var errorMessage = authenticationResult.Error;
// TODO: do something with error message
txtAuthResult.Text = $"Error {errorMessage}";
return;
}
// That's completed. Here you have to token, ready to do something
var token = authenticationResult.AccessToken;
var refreshToken = authenticationResult.RefreshToken;
// TODO: make something useful with the tokens
txtAuthResult.Text = $"Success, token is {token}";
}
If i use Postman for authentication, i can use the URL
curl --location --request GET 'https://accounts.spotify.com/authorize?response_type=token&client_id=7c...45&scope=playlist-read-private&redirect_uri=http://localhost:8080&state=test'
and everything works fine and i get the token in the callback url as parameter.
If i add as "extra_parameters" the "response_type" : "token" i get the message, that this parameter is not supported...
I'm a little bit stucked here and don't know how to proceed.
I'm happy about any help in every direction to get this autentication done with uno-plattform.
OIDC can be described as a superset of OAuth2. It is a way for an identity provider to issue tokens and supply info about a user via additional APIs. Read more here.
The Oidc code that you use (probably IdentityModel.OidcClient?) requires a the service you’re calling to implement a few extra endpoints which Spotify has not implemented for their API. This is discussed in this forum topic. Because of the missing Oidc support, your code will try making calls that do not work.
The SpotifyAPI-NET library might also help you authenticate and make API calls instead.
I'm trying to read in a Google sheet my Twitter timeline.
I've copied the following code reported in the GAS documentation about twitter authentication (omitting step 2 since I'm not using the code inside a UI):
function getTwitterService() {
// Create a new service with the given name. The name will be used when
// persisting the authorized token, so ensure it is unique within the
// scope of the property store.
return OAuth1.createService('twitter')
// Set the endpoint URLs.
.setAccessTokenUrl('https://api.twitter.com/oauth/access_token')
.setRequestTokenUrl('https://api.twitter.com/oauth/request_token')
.setAuthorizationUrl('https://api.twitter.com/oauth/authorize')
// Set the consumer key and secret.
.setConsumerKey('mykey')
.setConsumerSecret('mysecret')
// Set the name of the callback function in the script referenced
// above that should be invoked to complete the OAuth flow.
.setCallbackFunction('authCallback')
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getUserProperties());
}
function authCallback(request) {
var twitterService = getTwitterService();
var isAuthorized = twitterService.handleCallback(request);
if (isAuthorized) {
return Logger.log('Success! You can close this tab.');
} else {
return Logger.log('Denied. You can close this tab');
}
}
function makeRequest() {
var twitterService = getTwitterService();
var response = twitterService.fetch('https://api.twitter.com/1.1/statuses/user_timeline.json');
Logger.log(response);
}
but I obtain the message error: Service not authorized. (row 292, file "Service", project "OAuth1").
What's wrong?
I needed to add the following line the first time I execute makeRequest:
var authorizationUrl = twitterService.authorize();
Logger.log(authorizationUrl);
Then, open the url read from the log and authorize the app.
After that, all works fine.
I have been looking for some documentation around how to do OAuth in the Play framework (version 2.2.2) and I can't really find anything. I read in one place that it has been deprecated but I haven't been able to find anything about this either. Does anyone know? I want to connect to the Twitter API and make some requests for data in my application.
You can find examples of OAuth with Play Framework on these open source projects:
securesocial
play-silhouette
play-authenticate
It's supported and actually pretty straight forward.
Here's an OAuth authorization example directly from the Play Docs:
object Twitter extends Controller {
val KEY = ConsumerKey("xxxxx", "xxxxx")
val TWITTER = OAuth(ServiceInfo(
"https://api.twitter.com/oauth/request_token",
"https://api.twitter.com/oauth/access_token",
"https://api.twitter.com/oauth/authorize", KEY),
false)
def authenticate = Action { request =>
request.queryString.get("oauth_verifier").flatMap(_.headOption).map { verifier =>
val tokenPair = sessionTokenPair(request).get
// We got the verifier; now get the access token, store it and back to index
TWITTER.retrieveAccessToken(tokenPair, verifier) match {
case Right(t) => {
// We received the authorized tokens in the OAuth object - store it before we proceed
Redirect(routes.Application.index).withSession("token" -> t.token, "secret" -> t.secret)
}
case Left(e) => throw e
}
}.getOrElse(
TWITTER.retrieveRequestToken("http://localhost:9000/auth") match {
case Right(t) => {
// We received the unauthorized tokens in the OAuth object - store it before we proceed
Redirect(TWITTER.redirectUrl(t.token)).withSession("token" -> t.token, "secret" -> t.secret)
}
case Left(e) => throw e
})
}
def sessionTokenPair(implicit request: RequestHeader): Option[RequestToken] = {
for {
token <- request.session.get("token")
secret <- request.session.get("secret")
} yield {
RequestToken(token, secret)
}
}
}
If you want to sign request, you can do it like this:
WS.url(s"https://api.twitter.com/1.1/account/verify_credentials.json")
.sign(OAuthCalculator(Key, RequestToken(token, tokenSecret)))
.get
Please note that the above is for OAuth 1.0. OAuth2 is very easy to implement without a dedicated library that why the Play folks left it out.
i am trying to write a tool that creates entries in the google calendar.
after following the google docs and creating an client-identifier/secret in the api console, i managed to put together a client that authenticates correctly and shows my registered google calendars. right now for me it looks like my google-account is somehow tied to my client-identifier/secret. what i want to know is: how can i change the auth process so that it is possible for an other user of this tool to enter his google-id and get access to his calendars?
EDIT: in other words (used in the RFC): I want make the resource owner-part editable while leaving the client-part unchanged. but my example, although working, ties together client and resource owner.
here is my app that works fine so far:
public void Connect()
{
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description);
provider.ClientIdentifier = "123456123456.apps.googleusercontent.com";
provider.ClientSecret = "nASdjKlhnaxEkasDhhdfLklr";
var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
var service = new CalendarService(auth);
//Events instances = service.Events.Instances("primary", "recurringEventId").Fetch();
var list = service.CalendarList.List().Fetch();
foreach (var itm in list.Items)
Console.WriteLine(itm.Summary);
}
private static readonly byte[] AditionalEntropy = { 1, 2, 3, 4, 5 };
private static IAuthorizationState GetAuthorization(NativeApplicationClient arg)
{
var state = new AuthorizationState(new[] { CalendarService.Scopes.Calendar.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
var refreshToken = LoadRefreshToken();
if (!String.IsNullOrWhiteSpace(refreshToken))
{
state.RefreshToken = refreshToken;
if (arg.RefreshToken(state))
return state;
}
var authUri = arg.RequestUserAuthorization(state);
// Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString());
var frm = new FormAuthCodeInput();
frm.ShowDialog();
// Retrieve the access token by using the authorization code:
var auth = arg.ProcessUserAuthorization(frm.txtAuthCode.Text, state);
StoreRefreshToken(state);
return auth;
}
private static string LoadRefreshToken()
{
try
{
return Encoding.Unicode.GetString(ProtectedData.Unprotect(Convert.FromBase64String(Properties.Settings.Default.RefreshToken), AditionalEntropy, DataProtectionScope.CurrentUser));
}
catch
{
return null;
}
}
private static void StoreRefreshToken(IAuthorizationState state)
{
Properties.Settings.Default.RefreshToken = Convert.ToBase64String(ProtectedData.Protect(Encoding.Unicode.GetBytes(state.RefreshToken), AditionalEntropy, DataProtectionScope.CurrentUser));
Properties.Settings.Default.Save();
}
Prompt the user to enter their ClientIdentifier and ClientSecret, then pass these values to your Connect method.
i solved the problem myself.
the problem was, that i'm usually always connected to google and because i did't log out from google before my app redirected to google to get the access-token, google automatically generated the access-token for my account - skipping the part where an input-form appears where anyone could enter his/her user-credentials to let google generate an access-token for his/her account.