Express - Authenticating URI endpoints using facebook & Google - authentication

I've got URI endpoint authentication working for both facebook & google in my express app through separate middlewares. Facebook uses passport facebook-token strategy, whereas for google I wrote my own middleware using nodejs client lib for google API. What I want is to authenticate a user on a URI endpoint using both these middleware.
/*
//google controller file
module.exports = function(req,res,next){
}
*/
googlectrl = require('google controller file');
//this works fine
app.get('someurl',googlectrl,function(req,res){
//google user authenticated
}
//this works fine too
app.get('someurl',passport.authenticate('facebook-token',{session=false}),function(req,res){
//google user authenticated
}
But how do I combine the two for the same uri. Otherwise I need to use seperate URI for google & fb. Pls advice. Pls note I've tried implementing google strategy and it has not worked.

You can use one array field for your user object named as providers as shown below:
{
"name": "asdasd",
"providers": [ 'google']
}
And at server side check user is using which provider and call the method accordingly. For example:
If a user requests with google service provider then call
function handleGoogleAuthentication(){
// Logic
}
And if a user requests with facebook service provider then call
function handleFacebookAuthentication(){
//Logic
}

Related

How to configure Azure AD authentication in Hybrid ASP.NET Core MVC (backend) and Vuejs SPA (frontend)?

My application is a hybrid approach where use ASP.NET Core MVC as my backend. I have various controllers which my front end uses to pull data from our database and also to do API calls on MS Graph. I am using the following program.cs file to get the authentication initiated when a user first logs on to the site:
//authentication pipline
builder.Services.AddHttpContextAccessor();
var initialScopes = builder.Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(options =>
{
builder.Configuration.Bind("AzureAd", options);
options.Events = new OpenIdConnectEvents
{
//Tap into this event to add a UserID Claim to a new HttpContext identity
OnTokenValidated = context =>
{
//This query returns the UserID from the DB by sending the email address in the claim from Azure AD
string query = "select dbo.A2F_0013_ReturnUserIDForEmail(#Email) as UserID";
string connectionString = builder.Configuration.GetValue<string>("ConnectionStrings:DBContext");
string signInEmailAddress = context.Principal.FindFirstValue("preferred_username");
using (var connection = new SqlConnection(connectionString))
{
var queryResult = connection.QueryFirst(query, new { Email = signInEmailAddress });
var claims = new List<Claim>
{
new Claim("UserID", queryResult.UserID.ToString())
};
var appIdentity = new ClaimsIdentity(claims);
context.Principal.AddIdentity(appIdentity);
}
return Task.CompletedTask;
},
};
}).EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
//Add Transient Services
builder.Services.AddTransient<IOneDrive, OneDrive>();
builder.Services.AddControllers(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();
builder.Services.AddRazorPages().AddRazorPagesOptions(options =>
{
options.Conventions.AllowAnonymousToFolder("/Login");
options.Conventions.AuthorizeFolder("/");
options.Conventions.AuthorizeFolder("/files");
}).AddMicrosoftIdentityUI();
// Add the UI support to handle claims challenges
builder.Services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
builder.Services.AddRequiredScopeAuthorization();
In the Azure AD portal my application is registered as a web app. So when a user initially goes to the site they are redirected to https://login.microsoftonline.com/blahblah to get the login process started. This is automated by the Azure AD identity platform. Then once the login occurs they are redirected to localhost where the VueJS spa is loaded (localhost:43862). My spa uses various axios requests to the controllers and they pull data and vue router loads components. However, my issue is say the user needs to relog in because the cookie is expired or they logged out in another tab. The next axios request made by the expired session does not redirect the user to Azure login screen but instead results in an CORS error. So I need to get my axios requests to force the page redirect to Azure AD login screen (which probably is the worst idea since CORS policy is resulting in error) or have it return a redirect to localhost/login which is my own custom login screen with a button to Azure AD login and shouldnt impact CORS. So how do I intercept this Azure AD redirect to Azure AD login and replace with my own?
I have also tried to return a 401 error code so I could check for that in my axios request but to no avail it does nothing. If I put a breakpoint there it does hit this code but it does not change the status code of the response and I still get 302. My code for that was to try and add to the event :
OnRedirectToIdentityProvider = context =>
{
context.Response.StatusCode = 401;
return Task.CompletedTask;
}
My other ideas was maybe I should set my CORS policy to allow redirects from login.microsoft.com? Or would this be bad practice?
I can answer part of your question... First, for our API application which is protected by Azure AD, what the API should do is validating the request whether it contained a correct access token in the request header, if yes, give the response, if no, then give error like 401 or 403. A normal API application shouldn't have a UI to let users sign in. Anyway, if you want to expose an API in an MVC project, it's OK, but for API itself, it shouldn't have a UI.
Let's see sample below, I had a .net 6 web api project, and here's my program.cs:
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration);
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
And it requires configurations in appsetting.json.
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "azure_ad_client_id",
"ClientSecret": "client_secret",
"Domain": "tenant_id",
"TenantId": "tenant_id",
//"Audience": "api://azure_ad_client_id_which exposed_api" // here I used the same azure ad app to expose API, so I can comment this property
},
And this is the Controller:
[ApiController]
[Route("[controller]")]
[Authorize]
public class WeatherForecastController : ControllerBase
{
[RequiredScope("Tiny.Read")]
[HttpGet]
public string Get()
{
return "world";
}
}
I had an Azure AD app, and I exposed an API like this:
I also add this API for the same Azure AD app.
Then let's do a test. When I call this API directly, I will get 401 error:
If I used an expired token within the request, I will also get 401 error:
But if I used a correct token(go to https://jwt.io to decode the token, we should see it containing correct scope, for me its "scp": "Tiny.Read",), I will get response:
And till now, the API part had finished. Let's see the client SPA. For SPA, you should integrate MSAL so that you can make your users to sign in via Azure AD, and generate the access token for calling MS graph API or your own API. The code for generating access token should be the same but you should set different scope for different API. In my scenario, my API required a scope Tiny.Read, then I should set in my client App.
Here's an screenshot for generating access token in react. You need to set the scope in your code.
Now you have the method to generate access token, you already know the API url. Then you can send request to call api, using AJAX, using fetch, or something else, sending an http request is ok. And in the calling api part, you also need to handle the response. If the response code is 401, then you need to do some logic, maybe redirect to the sign in page. And you said you had trouble here, you met CORS issue. I can't answer this part. I think it depends on how you redirect to Azure AD sign in page. I'm afraid you can take a look at this sample to learn how to sign in users and call graph api.

Angularfire2 custom authentication

I creating a website which has register link multiple auth providers and custom token as well. I also using AngularFire2 to communicate between Angular2 and Firebase but seem it doesn't have method similar with Firebase, e.g:
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
ref.authWithCustomToken(AUTH_TOKEN, function(error, authData) {
Anyone can show up to me how can deal with issue?
To authenticate using a custom token, you can call AngularFire2's login method with the following configuration options:
angularFire.auth.login(AUTH_TOKEN, {
provider: AuthProviders.Custom,
method: AuthMethods.CustomToken
});
Internally, this will call Firebase's signInWithCustomToken method.

Aurelia Security - Authentication & Authorization

These are a couple general questions to really see different implementations of detecting authentication and authorization using Aurelia. All of this is speaking within the context of a secured back-end service.
Say you are using cookie authentication with the server. How are you acknowledging that cookie in Aurelia to display to the user that they are logged in?
In the Aurelia documentation(seen here), we can see the following:
class AuthorizeStep {
run(navigationInstruction, next) {
if (navigationInstruction.getAllInstructions().some(i =>
i.config.settings.roles.indexOf('admin') !== -1)) {
var isAdmin = /* insert magic here */false;
if (!isAdmin) {
return next.cancel(new Redirect('welcome'));
}
}
return next();
}
}
What does /* insert magic here */ look like for you? What should it look like?
The app I am currently working on requests a token from the server at the 'login' route using XHR. If this request is successful, and a token was received from the backend, then the token is stored in a cookie and we route away from the login page to the main content of the app. We could then set a global variable 'loggedIn' to display that the user is logged in, etc. Each time we make further requests to the backend via XHR, we send the token with the request.
The 'magic' in the authorize step is just some logic that checks to see if the user is logged in, or in the example above, an admin.

Accessing Shoeboxed API with Google Apps Script (OAuth v2)

I'm trying to initiate a session with the Shoeboxed API via Google Apps Script. I hoped I could use Apps Script internal library to access it but I'm having issues. Here is my attempt:
function testAPI() {
var consumerKey = '';
var consumerSecret = '';
var oauthConfig = UrlFetchApp.addOAuthService('shoeboxed');
oauthConfig.setAccessTokenUrl(
'https://id.shoeboxed.com/oauth/token');
oauthConfig.setRequestTokenUrl(
'https://id.shoeboxed.com/oauth/token');
oauthConfig.setAuthorizationUrl(
'https://id.shoeboxed.com/oauth/authorize');
oauthConfig.setConsumerKey(consumerKey);
oauthConfig.setConsumerSecret(consumerSecret);
var options = {
'oAuthServiceName' : 'shoeboxed',
'oAuthUseToken' : 'always'
};
var url = 'https://api.shoeboxed.com/v2/user';
var response = UrlFetchApp.fetch(url, options);
Logger.log("Response: " + response.getContentText());
}
It's failing at the point where it attempts to fetch user data via the API url with an authorization failed message. I'm not sure what I'm doing wrong. Information about the API and OAuth can be found here: https://github.com/Shoeboxed/api/blob/master/sections/authentication.md
New method:
It looks like that API requires OAuth2, but the UrlFetchApp.addOAuthService method only works with the older version of OAuth.
There's a new method ScriptApp.newStateToken() which can be used in combination with OAuth2, but it requires more manual/explicit control over the OAuth2 steps. It generates a state token.
A minor detail on that method:
Note that when you construct URLs, the state token should passed as a URL parameter on the .../authorize URL, not embedded as a URL parameter within the .../usercallback URL.
For example:
You would want to redirect the user to:
https://id.shoeboxed.com/oauth/authorize?client_id=<your client id>&response_type=code&scope=all&redirect_uri=<your site>&state=<CSRF token>
where redirect_uri is:
https://script.google.com/macros/d/1234567890abcdefghijklmonpqrstuvwxyz/usercallback
When the user clicked authorize, Shoeboxed should redirect them to:
https://script.google.com/macros/d/1234567890abcdefghijklmonpqrstuvwxyz/usercallback?state=<CSRF token>
oauth2 support for the shoeboxd API has just been added to the cEzyOauth2 Google Apps Script library.
You can copy the pattern to your app and include the library as described here
It uses the statetoken as described by Steve Lieberman, and takes care of the oauth2 conversation, token handling and refreshing automatically.

Facebook C# SDK: OAuth 2 in Silverlight 4 browser app

I'm completely newbie at authentication proccess with OAuth (I'm trying to make use of OAuth 2, exactly), and the example I am following to authenticate by using Facebook SDK latest release says that this code snippet should work for C# .NET environments (http://blog.prabir.me/post/Facebook-CSharp-SDK-Writing-your-first-Facebook-Application.aspx):
webBrowser.Navigate(loginUrl);
private void webBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
FacebookOAuthResult result;
if (FacebookOAuthResult.TryParse(e.Url, out result))
{
if (result.IsSuccess)
{
var accesstoken = result.AccessToken;
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
}
Since I am programming a browser SL app, the WebBrowser control displays nothing, so I am not either able to catch the response, how could I do something equivalent to that in my app? Or how could I manage to complete the authentication proccess if there is no equivalent way? Thanks!
A suggestion: Why don't you try to parse the WebResponse when you receive it as opposed to listening for the event?
I use Facebook OAuth in my web app. It is nothing but a series of URL posts with the correct parameters.
Take a look at this post: Login using Facebook Problem after logging out (All the details are in the answer and comments)
Here are the brief steps:
Call the Facebook OAuth Dialog URL with your AppId, redirect url, and permissions. Request_type should be "code"
When the user logs in and authorizes you application, they will be redirected to the redirect url with a "code" querystring parameter.
Take the value of the code parameter and make another call to Facebook to get the token.
Use this token to make calls on the user's behalf.