Secure web api with AAD -Authorization has been denied for this request - asp.net-web-api

I use an angularJS web application to login to azure => this part is working.
But when I try to access an authorized controller in my web app, I receive the "Authorization has been denied". While the authorization bearer token has been sent to the web API
my Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
ApiController
[Authorize]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
Error:
<Error>
<script/>
<Message>Authorization has been denied for this request.</Message>
</Error>
Response Header:
VzViQXBwbGljYXRp
b245XGFwaVx2YWx1ZXM=?=

AFAIK, we would leverage adal.js and adal-angular.js in the Angular JS application to authenticate users and get tokens in the client side. Details you could follow the tutorials Azure AD AngularJS getting started and Integrating Azure AD into an AngularJS single page app to narrow this issue.
But when I try to access an authorized controller in my web app, I receive the "Authorization has been denied". While the authorization bearer token has been sent to the web API.
If you manually enable the middleware to validate the token, you need to make sure that you have correctly configured the WindowsAzureActiveDirectoryBearerAuthenticationOptions.Audience or TokenValidationParameters.AllowedAudience(s) which would be compared with the aud property from the incoming JWT token. You could press F12 when browsing your app and trace the Network or use Fiddler to capture your bearer token, then use https://jwt.io/ to decode your token.
Moreover, if you use the built-in Authentication and authorization in Azure App Service for your backend web app, you need to correctly configure the Client ID or ALLOWED TOKEN AUDIENCES for AD authentication under the Authentication / Authorization blade of your app service app.

Related

Google Social Login with Auth0 Tokens for [Authorized] API Calls

Anyone here implemented social login through Google for Auth0? I have an issue with the tokens (access and id) being returned after validating with Google.
Here's my code:
var waGoogle = new auth0.WebAuth({
domain: 'testApplication.auth0.com',
clientID: '************',
redirectUri: 'http://localhost:8080/'
})
waGoogle.authorize({
connection: 'google-oauth2',
responseType: 'id_token token'
}, function(err, authResult){
if(err){
console.log('Google Login Error')
console.log(err)
}
});
Google screen shows up, I log in and I am redirected back to my application. From the application, I parse the URL so that I can get the access and id tokens.
let getParameterByName = (name) => {
var match = RegExp('[#&]' + name + '=([^&]*)').exec(window.location.hash);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
var access_token = getParameterByName('access_token')
var id_token = getParameterByName('id_token')
Issue I am having is that none of the tokens allow me to call my APIs (asp.net web api) which are decorated with the [Authorize] attribute. It returns a:
401 (Unauthorized)
I know that my API is working, as using the normal
Username-Password-Authentication
method where I also obtain an access token, my api calls are just pulling through.
Are there any next steps which I need to do after obtaining the access and id_token from Google? Do I need to make an additional call to Auth0 to obtain the proper access token to be able to call my web api?
Thanks.
The token you are looking for is called an IdP (Identity Provider) Token. This is different from the one issued to you after logging in. There are pretty good instructions on the Auth0 site that walk you through the process of getting that token.
Here is the overview of IdP tokens
Here is a step-by-step guide to calling the Identity Provider
The tl;dr:
To access the IdP token you need to call the Auth0 management API from your server. For that, your server will need a management token. Then use that token to access the endpoint /api/v2/users/[USER_ID]. In the object sent back from Auth0, look for the google identity and extract that token.
Also note, you should probably keep that token on your server if you can. If you can keep those power tokens away from your client your users will be happy.

Return JWT token to javascript SPA from oauth login

I'm developing a javascript spa in vue.js which is going to eventually be a cordova application. As well as a backend api built with lumen.
I'm trying to provide login with facebook and google functionality. I've added the laravel socialite package and created a redirect and callback route. Ive created a page in my spa with the login buttons that direct to my api redirect. The user is redirected to facebook's oauth mechanism I login, the callback routes handle function looks something like this
public function handleProviderCallback($provider)
{
$oauthUser = $this->socialiteManager->with($provider)->stateless()->user();
$userEntity = $this->repository->findOrCreateOauthUser($oauthUser);
$providerEntity = app()
->make('em')
->getRepository('Entities\Social\Provider')
->findOneBy(['name' => $provider]);
if(is_null($providerEntity))
{
throw new \Exception('Oauth Provider not found');
}
$socialAccountEntity = app()
->make('em')
->getRepository('Entities\Social\SocialAccount')
->findOrCreateSocialAccount($providerEntity, $oauthUser, $userEntity);
$token = $this->auth->fromUser($userEntity);
$resource = $this->item($token)
->transformWith($this->transformer)
->serializeWith(new ArraySerialization())
->toArray();
return $this->showResponse($resource);
}
It basically gets a the oauth user, finds or stores them in the database, finds or stores their social account info,
$token = $this->auth->fromUser($userEntity);
Then authenticates them with JWT issuing a token. Which is then serialised and returned as a json response.
The problem is that the response is given while on the backend application, im never returned back to the javascript SPA.
Instead of returning json I could do some redirect like
return redirect()->to('/some-spa-callback-route');
But should the api be aware of the SPA location? and how will this work when the SPA is ported into cordova, as the mobile application wont have a url?
My thoughts are that A
The social provider should redirect directly to the SPA at which point it should make another request exchanging the authorisation code for a JWT token.
B it redirects to the SPA with a query string containing the token, which doesn't sound secure.
or c sends some type of cookie back.
And I am still confused as to how to actually redirect from my api to a mobile application.
In the end I ended up with the following login flow
User is directed to Oauth provider
Oauth provider returns an access token to the client
the client sends the access token to my api
my api sends a renew request to the Oauth provider
the Oauth provider validates the token and returns a new one to my api
my api exchanges the access token for a jwt token
my api returns the jwt token to the client
In my opinion it is the only correct way to authenticate SPA applications, and it is important to renew the Oauth token the client provides rather than blindly exchanging for a jwt as you can't trust the client, and is better than issuing redirects from the api that isn't very restfull
Instead of dealing with 2 services, your spa should talk to a single auth service in your backend. You register your service as the oauth callback and you handle oauth/jwt as you described. Your auth service can also be the decision point for user (re-)authentication. Since your frontend calls your backend directly, you can now return the json payload back to your web/mobile caller.

Spring boot webapp as client of Google Oauth2

I'm writing a webapp using Spring Boot. For my project, I need to connect to Google for retrieving calendar information about users.
So, I integrate Spring security Oauth2 for user connection and after I want to use the Google calendar API.
The connection is ok and after it, Google redirects to a page of my webapp defined by this controller:
#Controller
class ConnectedController {
private String token
#RequestMapping("/welcome")
String welcome(Principal principal,
Model model) {
OAuth2Authentication auth = principal
model.addAttribute("user", auth.userAuthentication.details)
// credential
def api = new CalendarApi()
token = auth.details.tokenValue
println "Token: [$token]"
api.fetch(token)
return 'welcome'
}
}
For the Calendar API, I need the access token value and the refresh token value. I find the access token value in the principal object but where is the refresh token?
For information, I configured the authorization like below to have the resfresh token (normally):
userAuthorizationUri: https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent
As I interpret the google Oauth2 doc you get a authorization code when consent is given by the user. And this code should be used to get an access token and a refresh token from the API.
The result is an authorization code, which the application can
exchange for an access token and a refresh token.
Have you tried this?

Sending Bearer Tokens to Web API via Postman

Update
I have been able to get a Bearer token using instructions from this thread
Here are the instructions in Postman:
Url: https://login.windows.net/[tenantname].onmicrosoft.com/oauth2/token
Type: POST
Headers: none
Body: form-data
grant_type: client_credentials
client_id: [client-id]
client_secret: [client-secret]
However, if I send the same token in my call to a Web API endpoint, I still get back "Authorization has been denied for this request"
Why is it still not authorizing ?
End Update
I have created an ASP.Net Web API project which is protected using an organizational Azure AD instance. I have set up the tenant id, client id and secret correctly.
The Azure AD instance is the same one backing our Office 365/SharePoint instance and the idea is to create SharePoint Add-Ins which can call the services using the logged in user's context.
I am stuck at testing the API. I can call unauthorized endpoints without any issue. However, when I add the [Authorize] attribute, I always get back this response: "Authorization has been denied for this request."
As I understand it, I need to generate a bearer token and add it to my Postman request in the header (see image). After much Googling, I still have not been able to make this work.
My question is: How do I generate a bearer token for a Web API instance protected by Azure AD.
My configuration code is as below:
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters {
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
First, you can use POSTMAN to test web api protected by the Bearer token. Here's my postman screenshot:
POSTMAN sending bearer token to web api
Basically: in the request header, specify the key as "Authorization", and value as "Bearer [your token". IF you run into errors, look the headers of the response and you'll see more detailed error info.
Note, most tokens have an expiration period, you can try to verify if your token is valid. e.g. https://jwt.io/

How to exchange Google one-time authorization code for a refresh token without callback (intranet)?

I'm working on a intranet-based application and I want to use Google services. Currently I have successfully implemented Google Authentication with "Sign-In for Websites" using JavaScript client-side authentication. My users can now sign in or sign up with their Google accounts.
Now I want to use Google API to create and share Google Sheets with my users. These documents will be created with a specific Google account and then shared with my users.
This is why I want to use this server-slide flow to get a one-time authorization code and exchange it for a refresh token:
https://developers.google.com/identity/sign-in/web/server-side-flow
This refresh token will be stored in my database allowing me to user Google services on behalf of this offline user.
Using JavaScript library, I was able to get the one-time authorization code that I send to my server with a AJAX request.
auth2.grantOfflineAccess({'redirect_uri': 'postmessage'}).then(grantOfflineAccessCallback);
var grantOfflineAccessCallback = function(authResult) {
var auth_code = authResult.code;
// Exchange the one-time authorization code for tokens
$.post(...);
}
On server-side I use Google API PHP Client (v2.0.0-RC6) to acquire an access and refresh token.
$this->client = new Google_Client();
$this->client->setClientId($this->clientId);
$this->client->setClientSecret($this->clientSecret);
$this->client->setAccessType('offline');
$this->client->setApprovalPrompt('force');
$response = $this->client->fetchAccessTokenWithAuthCode($oneTimeCode);
I wasn't able to exchange the authorization code.
Client error: `POST https://www.googleapis.com/oauth2/v4/token` resulted in a `400 Bad Request` response:
{
"error": "invalid_request",
"error_description": "Missing parameter: redirect_uri"
}
On this page we can read:
On the server, exchange the auth code for access and refresh tokens.
Use the access token to call Google APIs on behalf of the user.
On the JAVA example code:
REDIRECT_URI: // Specify the same redirect URI that you use with your web
// app. If you don't have a web version of your app, you can
// specify an empty string.
Because the application I working on is an intranet application, I tried to specify an empty string for this redirect_uri parameter before calling fetchAccessTokenWithAuthCode() method:
$this->client->setRedirectUri('');
... result in Redirect URI must be absolute.
Can we use this hybrid server-slide flow without callback URL?
Is there any solution to my problem?
Thanks,
Edit:
redirect_uri is where the user will be redirected to after he signed in. This URL must be registered in the Google Project (developers console). So redirect_uri is NOT the callback...!
Problem is now solved with:
$this->client->setRedirectUri('http://same.url.as.in.developers.console/');

Resources