Dropwizard authentication header token - authentication

I am trying to implement OAuth2 authentication in a Dropwizard web-application. I have created the required Authenticator and Authorizer classes and added the code supplied in the Dropwizard manual in my Application's run-method as follows:
environment.jersey().register(new AuthDynamicFeature(
new OAuthCredentialAuthFilter.Builder<User>()
.setAuthenticator(new TokenAuthenticator(service))
.setAuthorizer(new TokenAuthorizer())
.setPrefix("Bearer")
.buildAuthFilter()));
environment.jersey().register(RolesAllowedDynamicFeature.class);
//If you want to use #Auth to inject a custom Principal type into your resource
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
My required behavior is that after my client has logged in by providing his/her credentials on my login page, I want to redirect the client to a greeting page I have created using Dropwizard Views and is under the path: "/me" as follows:
//After succesfull login and token generation
return Response.seeOther(new URI("/me")).build(); // redirect to greeting page
And my greeting resource looks as follows:
#Path("/me")
#Produces(MediaType.TEXT_HTML)
public class UserResource {
#GET
public UserView getView(#Auth User user) {
return new UserView(user);
}
}
Currently I am getting a "Credentials are required to access this resource." response after logging in. After some reading on token authentication (nice explanation here) I picked up that the token must be sent from the client in the header of each request. So my question is how do I tell the user's browser (client) to include the token in the header of future requests?

I managed to solve this by doing the following:
In order to verify the user, a token must be sent in the header of the request in the form of Authorization: Bearer <token-value>. This token is sent by the server upon authentication and must be stored by the client / user to be sent in future requests. I managed to store the token by using an ajax request when my login form is submitted as follows:
<#-- Handle form submission response to save the token on the client side-->
<script>
$('#loginForm').submit(function(event){
event.preventDefault();
$.ajax({
url: $(this).attr('action'),
type: 'POST',
data : $(this).serialize(),
dataType: 'json',
success: function(data){
//alert("The server says success!!: " +data);
console.log(data);
window.sessionStorage.accessToken = data.token;
window.location = data.url;
},
error: function(data){
alert("The server says error! : ");
console.log(data);
}
});
});
</script>
The login resource then produces JSON which is received in the data-variable in the code above. The required token resides in data.token - which is then stored. I added a second entry in the JSON named "url" to indicate the path to redirect to after successful authentication.
Now the token is stored on the client side when needed. In order to send this token in the request header, I needed to alter my approach to using the Views provided by Dropwizard. Instead of directly requiring authentication, I split the View's resource and the authenticated data resources. To clarify consider the following example. A user logs in, gets the token and then goes to a page that displays his/her username. For the page, a View resource is created with a .ftl file to serve as a template. Something like:
#Path("/me")
#Produces(MediaType.TEXT_HTML)
public class UserResource {
#GET
public UserView getView() {
return new UserView();
}
}
and...
public class UserView extends View {
public UserView() {
super("user.ftl");
}
}
And user.ftl:
<#include "include/head.html">
<#include "include/header.html">
<!-- Header -->
<div id ="headerWrapper">
</div>
<div class="container-fluid">
<div id="name">
<p>Hello user</p>
</div>
</div>
<#include "include/footer.html">
Now to retrieve the username I create a new resource which produces JSON on a new path. For example:
#Path("/getdetails")
#Produces(MediaType.APPLICATION_JSON)
public class UserDetailsResource {
#GET
#Timed
#UnitOfWork
public User getDetails(#Auth User user) {
return user;
}
}
This resource requires authentication and provides JSON from which the username can be retrieved. Now to get the username and place it inside the view, simply add a script to the users.ftl with an ajax request to the getdetails resource, providing the token in the header and using the result to place the username in the view. See script below.
<script>
$.ajax({
url: '/getdetails',
type: 'GET',
headers: {"Authorization": window.sessionStorage.accessToken},
dataType: 'json',
success: function(data){
//alert("The server says success!!: " +data);
console.log(data);
$("#name").text('Hello '+data.username);
},
error: function(data){
alert("The server says error! : ");
console.log(data);
}
});
</script>

Related

Shopware 6 - Admin API : The resource owner or authorization server denied the request

I want to create a new own route API using admin API. I tried this code :
<?php declare(strict_types=1);
namespace TestApi\Controller\Api;
use Shopware\Core\Framework\Context;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
/**
* #RouteScope(scopes={"api"})
*/
class ApiController extends AbstractController
{
protected EntityRepositoryInterface $productRepository;
public function __construct(EntityRepositoryInterface $productRepository)
{
$this->productRepository = $productRepository;
}
/**
* #Route("/api/product", name="api.product.search", methods={"GET"})
*/
public function getProducts(Context $context): JsonResponse
{
$criteria = new Criteria();
return new JsonResponse($this->productRepository->search($criteria, $context));
}
}
When I try this request {baseUrl}/api/product in storefront I get this error:
{"errors":[{"code":"9","status":"401","title":"The resource owner or authorization server denied the request.","detail":"Missing \u0022Authorization\u0022 header","meta":{"trace":[{"file":"\/var\/www\/html\/vendor\/league\/oauth2-server\/src\/AuthorizationValidators\/BearerTokenValidator.php","line":93,"function":"accessDenied","class":"League\\OAuth2\\Server\\Exception\\OAuthServerException","type":"::","args":["Missing \u0022Authorization\u0022 header"]}
Could you help me please ?
You'll have to provide the Authorization header in the request to your admin-api endpoint. The header should include a valid token. To retrieve the token you must first request the corresponding endpoint:
// POST /api/oauth/token
{
"grant_type": "client_credentials",
"client_id": "...",
"client_secret": "..."
}
You'll get a client_id and client_secret by creating an integration in the administration of your shop.
This endpoint will then return a temporarily valid token:
{
"token_type": "Bearer",
"expires_in": 600,
"access_token": "xxxxxxxxxxxxxx"
}
You then take the access_token and in all your following requests to the api you set it for Authorization in the request header, prepended by Bearer:
Authorization: Bearer xxxxxxxxxxxxxx
If you're using a javascript client like axios the object for the headers then would look like this for example:
{
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
There are two API's, one for Admin use (starts with /api/), one for Storefront (/store-api/). As you can see, you are not using the storefront API, and therefore expected to provide the appropriate Bearer token.
Moreover, please check that /api/product route already exists in the admin API.
Another thing, I think there is some confusion between a controller & an API route implementation. Maybe you could follow the official documentation on it or look it up online?

Redirect_URI error when using GoogleAuth.grantOfflineAccess to authenticate on server

I'm trying to use the authorization flow outlined at https://developers.google.com/identity/sign-in/web/server-side-flow.
I've created the credentials as indicated... with no Authorized redirect URIs specified as the doc indicates: "The Authorized redirect URI field does not require a value. Redirect URIs are not used with JavaScript APIs."
The code initiating the authorization is:
Client button and callback:
<script>
$('#signinButton').click(function() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.grantOfflineAccess().then(signInCallback);
});
function signInCallback(authResult) {
console.log('sending to server');
if (authResult['code']) {
// Send the code to the server
$.ajax({
type: 'POST',
url: 'CheckAuth',
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
contentType: 'application/octet-stream; charset=utf-8',
success: function(result) {
// Handle or verify the server response.
},
processData: false,
data: authResult['code']
});
} else {
// There was an error.
}
}
</script>
Server side (CheckAuth method to create credentials from auth code, which it receives correctly via the javascript callback):
private Credential authorize() throws Exception {
// load client secrets
InputStream is = new FileInputStream(clientSecretsPath_);
InputStreamReader isr = new InputStreamReader(is);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, isr);
String redirect_URI = "";
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
httpTransport, JSON_FACTORY,
"https://www.googleapis.com/oauth2/v4/token",
clientSecrets.getDetails().getClientId(),
clientSecrets.getDetails().getClientSecret(),
token_,
redirect_URI)
.execute();
String accessToken = tokenResponse.getAccessToken();
// Use access token to call API
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
return credential;
}
The flow works correctly, up until the point my server attempts to exchange the authorization code for the token response (GoogleAuthorizationCodeTokenRequest.execute() )... the auth server returns:
400 Bad Request
{
"error" : "invalid_request",
"error_description" : "Missing parameter: redirect_uri"
}
Given the error, I looked in debug at the auth instance in javascript and noted what it indicated was the redirect_uri. I then updated my google credentials and specified that URI in the Authorized redirect URIs (it's the URL that accessed the javascript, as the auth server correctly returns to the specified javascript callback). With the updated credentials and the URI specified in the instantiation of GoogleAuthorizationCodeTokenRequest (String redirect_URI = "http://example.com:8080/JavascriptLocation";), the error then becomes:
400 Bad Request
{
"error" : "redirect_uri_mismatch",
"error_description" : "Bad Request"
}
I've tracked all the way through to the actual HttpRequest to the auth server (www.googleapis.com/oauth2/v4/token) and cannot tell what redirect_uri it is looking for.
Does anyone know what the value of redirect_uri should be in this case (when using grantOfflineAccess())? I'm happy to post more of the code, if that is at all helpful... just didn't want to flood the page. Thanks.
Found a reference to "postmessage" right after posting the question... using it as the redirect_URI on the server side seems to generate a successful response from the auth server. So... setting redirect_URI="postmessage" in the code below appears to work in this situation.
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
httpTransport, JSON_FACTORY,
"https://www.googleapis.com/oauth2/v4/token",
clientSecrets.getDetails().getClientId(),
clientSecrets.getDetails().getClientSecret(),
token_,
redirect_URI)
.execute();

Not able to redirect to client side with token from server(express) side route

I am using 'googleapis' npm package to do token based google authentication.
I am redirected to '/api/auth/success/google' route inside express after google provided authentication and redirects us to the uri stated in google app credentials.
The problem I am facing is that ,I have retrieved the tokens on server side,but I am unable to send those tokens to client side for them to be saved in cookies.
The problem I am facing is because,'/api/auth/success/google' is redirected from google side and not an ajax call from client side.So if I send the tokens back in res,where will it redirect.Also please suggest a way to redirect from server side to client side,along with access_token.
server side code.
//Route reached after google successful login/authentication
app.get('/api/auth/success/google',function(req,res){
console.log("inside redirect");
var code = req.query.code;
oauth2Client.getToken(code, function(err, tokens) {
// Now tokens contains an access_token and an optional refresh_token. Save them.
if(!err) {
oauth2Client.setCredentials(tokens);
}
res.sendFile('./index.html');
});
})
Client side call
//Google login requested from this function
googleLogin(){
event.preventDefault();
$.ajax({
type : 'POST',
url : baseURL + 'api/authenticate/google',
success: (function(data) {
if (data.redirect) {
document.location.href = data.redirect;
}
}).bind(this)
});
}
//Route handling request of google access
app.post('/api/authenticate/google',function(req,res){
// generate a url that asks permissions for Google+ and Google Calendar scopes
var scopes = [
googlecredentials.SCOPE[0],
googlecredentials.SCOPE[1]
];
var url = oauth2Client.generateAuthUrl({
access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)
scope: scopes // If you only need one scope you can pass it as string
});
res.send({ redirect: url });
})
//Google App Credentials
var OAuth2 = google.auth.OAuth2;
var oauth2Client = new OAuth2(googlecredentials.CLIENT_ID, googlecredentials.CLIENT_SECRET, googlecredentials.REDIRECT_URL);
googlecredentials.CLIENT_ID - 858093863410-j9ma1i7lgapupip1ckegc61plrlledtq.apps.googleusercontent.com
REDIRECT_URL - http://localhost:3000/api/auth/success/google where localhost:3000 runs server side
If you send the redirect URL back in the res, the client-side should be able to check for the presence of the redirect URL in the response, and if it exists, push your user to that URL.

Access to Calendar API return 401 Unauthorized

I'm new to office 365 and having problem with accessing rest api.
I'm trying to test the rest api of Calendar and Mail API, so I decided to use Postman. However, to test those APIs, I need an access token in Authorization header. To figure out how to get a token, I decided to get the sample project here , configure, run and sign in on this local site to get the token cached in local storage and use that token for further requests in Postman. However, all requests I tested returned '401 unauthorized request'.
What I did:
Register a new app on Azure ADD associated with O365 account
Add full app permissions and delegated permissions.
Update 'oauth2AllowImplicitFlow' to true in manifest file.
Clone sample project
In app.js, I change the alter the content of config function as following
function config($routeProvider, $httpProvider, adalAuthenticationServiceProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/home.html',
controller: 'HomeController',
controllerAs: 'home',
requireADLogin: true
})
.otherwise({
redirectTo: '/'
});
// The endpoints here are resources for ADAL to get tokens for.
var endpoints = {
'https://outlook.office365.com': 'https://outlook.office365.com'
};
// Initialize the ADAL provider with your tenant name and clientID (found in the Azure Management Portal).
adalAuthenticationServiceProvider.init(
{
tenant: 'mytenantname.onmicrosoft.com',
clientId: '<my cliend Id>',
endpoints: endpoints,
cacheLocation: 'localStorage'
},
$httpProvider
);
};
Then I ran the app, it sign me in just fine and I can also get the token, but that token is also unauthorized to request.
I decoded the token and saw the value of 'aud', it didn't return "https://outlook.office365.com/". In this url, the author said that "This should be "https://outlook.office365.com/" for the Mail, Calendar, or Contacts APIs"
So what did I miss ?
How you call the Office 365 API in AngularJS?
When signing the user in, you will only get the id_token to authenticate the user.
The aud of id_token is the tenant id (GUID).
To call the Office 365 API, you need to use AugularJS http request.
Here is a sample of sending email using Microsoft Graph API in AngularJS:
// Build the HTTP request to send an email.
var request = {
method: 'POST',
url: 'https://graph.microsoft.com/v1.0/me/microsoft.graph.sendmail',
data: email
};
// Execute the HTTP request.
$http(request)
.then(function (response) {
$log.debug('HTTP request to Microsoft Graph API returned successfully.', response);
response.status === 202 ? vm.requestSuccess = true : vm.requestSuccess = false;
vm.requestFinished = true;
}, function (error) {
$log.error('HTTP request to Microsoft Graph API failed.');
vm.requestSuccess= false;
vm.requestFinished = true;
});
Before calling the API, ADAL.js will acquire another token - access token which you can used to send the email.
UPDATE#1
I also downloaded the sample you mentioned. To run this sample, please ensure you have the Exchange Online > Read and writer user mail Permission assigned in your application.

Backbone.js and user authentication

I have been wondering for quite a while how I would go about authenticating users using Backbone because I have been reading a few articles about it and a lot of them are talking about tokens and keys.. But I just want to be able to sign in a user and register a user like you would normally.
I was thinking that on the web app start up there would be a request to the route '/me' and then the server gives the user back appropriate information if he/she is logged in.
Like if the route came back with {loggedIn: false} the backbone router would send the user to the login/register pages only. But if it came back with a users profile information then it would obviously mean he had a session.
Is this an okay way of going back user authentication when using Backbone?
Short answer: wire up $.ajax to respond to 401 (Unauthorized) status codes.
Long answer: We're consuming a RESTful api from a single page website. when the server detects an unauthorized request, it just returns a 401. The client will redirect to /login?#requested/resource.
/login will prompt for authorization (redirect to google's oath server in our case) then add an authorization cookie and redirect to the originally requested #requested/resource
we're also sending the auth cookie on every $.ajax request.
Hopefully this is helpful.
define(
[
'jquery',
'jquery.cookie'
],
function ($) {
var redirectToLogin = function () {
var locationhref = "/login";
if (location.hash && location.hash.length > 0) {
locationhref += "?hash=" + location.hash.substring(1);
}
location.href = locationhref;
};
var $doc = $(document);
$doc.ajaxSend(function (event, xhr) {
var authToken = $.cookie('access_token');
if (authToken) {
xhr.setRequestHeader("Authorization", "Bearer " + authToken);
}
});
$doc.ajaxError(function (event, xhr) {
if (xhr.status == 401)
redirectToLogin();
});
});