Request.HttpContext.AuthenticateAsync result "Not Authenticated" (OAuth2; both Facebook and Google) - xamarin.forms

we are trying to solve the right setup/config to login using facebook and google from one App we created with Xamarin.
We are doing the authentication from the REST Service side using the NET Core 6 framework, and we applied the right configuration on facebook developer site using the developer URL and app_ads.txt file which is located within the root site of the website of our domain and we applied all the configurations required for google as well.
The facebook developer and google api console sites saved the configuration but, when we try to launch the app and it loads the login canvas, it fails in Request.HttpContext.AuthenticateAsync line code with both google and facebook options.
The code involved is the following:
public class AuthController : ControllerBase
{
const string callbackScheme = "signin-google";
[HttpGet("{scheme}")]
public async Task Get([FromRoute] string scheme)
{
var auth = await Request.HttpContext.AuthenticateAsync(GoogleDefaults.AuthenticationScheme);
if (!auth.Succeeded
|| auth?.Principal == null
|| !auth.Principal.Identities.Any(id => id.IsAuthenticated)
|| string.IsNullOrEmpty(auth.Properties.GetTokenValue("access_token")))
{
// Not authenticated, challenge
await Request.HttpContext.ChallengeAsync(scheme);
}
else
{
var claims = auth.Principal.Identities.FirstOrDefault()?.Claims;
var email = string.Empty;
email = claims?.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.Email)?.Value;
// Get parameters to send back to the callback
var qs = new Dictionary<string, string>
{
{ "access_token", auth.Properties.GetTokenValue("access_token") },
{ "refresh_token", auth.Properties.GetTokenValue("refresh_token") ?? string.Empty },
{ "expires", (auth.Properties.ExpiresUtc?.ToUnixTimeSeconds() ?? -1).ToString() },
{ "email", email }
};
// Build the result url
var url = callbackScheme + "://#" + string.Join(
"&",
qs.Where(kvp => !string.IsNullOrEmpty(kvp.Value) && kvp.Value != "-1")
.Select(kvp => $"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value)}"));
// Redirect to final url
Request.HttpContext.Response.Redirect(url);
}
}
}
Do you guys have any idea what can we do to solve this situation?
Thank you so much in advance for your help!
We need some clues or advices for solving this situation.

Related

How to build a custom Email action handler in Flutter for Firebase Authentication

I am very new to web development and i am building a website with Flutter.
I am aware of the Routing / Navigator system.
I would like to know how can i create such page in Flutter to handle Firebase email actions :
https://example.com/usermgmt?mode=resetPassword&oobCode=ABC123&apiKey=AIzaSy...&lang=fr
I am following this documentation.
It is stated here that:
Firebase adds several query parameters to your action handler URL when it generates user management emails.
However i don't understand if it's up to me to build the full url in my Flutter Routes or simply provide the endpoint /usermgmt and Firebase is automatically adding the params ?
I have tried to build a page in my website that addresses : https://example.com/usermgmt and it's working, the page exists hosted in my website.
Now if i add the Firebase params, it returns a 404 not found.
What should i do ?
EDIT : I have made the UI working, it doesn't return a 404 not found anymore.
I have done this for those interrested :
onGenerateRoute: (settings) {
Widget? authPage;
if (settings.name != null) {
var uriData = Uri.parse(settings.name!);
//uriData.path will be the path and uriData.queryParameters will hold query-params values
print("Param in query : " + uriData.queryParameters.toString());
switch (uriData.path) {
case '/auth':
authPage = AuthHandlerPage();
break;
}
}
if (authPage != null) {
return MaterialPageRoute(
builder: (BuildContext context) => authPage!);
}
},
I simply need to handle the links according to the params now.
To complete the email action, you need to fetch the oobCode from URL parameters and use applyActionCode method (checkActionCode can be used before applyActionCode to make sure the oobCode is valid, usually not required as applyActionCode will throw an error on getting an invalid code as well):
var actionCode = "" // Get from URL
try {
await auth.checkActionCode(actionCode);
await auth.applyActionCode(actionCode);
// If successful, reload the user:
auth.currentUser.reload();
} on FirebaseAuthException catch (e) {
if (e.code == 'invalid-action-code') {
print('The code is invalid.');
}
}
If you are using dynamic links, you can get it this way:
//Get actionCode from the dynamicLink
final Uri deepLink = dynamicLink?.link;
var actionCode = deepLink.queryParameters['oobCode'];
You can refer the documentation for more information.
Alternatively, you can also use the Firebase REST API to verify the email links if you need to verify the oobCode from a Cloud function or server.

How to validate a claim in id_token when using OpenIdConnect middleware?

I'm using Oath2 with Google authentication in my ASP.NET Core MVC app. I want to restrict logged in users to a certain G Suite domain which according to the docs is done using the "hd" (hosted domain) claim. I have it working but as it's authentication and I'm not familiar would like input. Am I doing this correctly? Is there a way to instead return a 401 status code instead of calling Fail() which results in a 500 error?
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie()
.AddOpenIdConnect(o =>
{
var hostedDomain = new KeyValuePair<string, string>("hd", "mysite.com");
o.ClientId = "...";
o.ClientSecret = "...";
o.Authority = "https://accounts.google.com";
o.ResponseType = "id_token token";
o.Scope.Add("openid");
o.Scope.Add("email");
o.Scope.Add("profile");
o.GetClaimsFromUserInfoEndpoint = true;
o.SaveTokens = true;
o.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = (context) =>
{
// Add domain limiting using 'hd' or 'hosted domain' parameter
// Docs: https://developers.google.com/identity/protocols/OpenIDConnect#hd-param
//context.ProtocolMessage.SetParameter(hostedDomain.Key, "asdf.com");
// Set up redirect URLs
if (context.Request.Path != "/account/external")
{
context.Response.Redirect("/account/login");
context.HandleResponse();
}
return Task.FromResult(0);
},
OnTokenValidated = (c) =>
{
var hdClaim = c.SecurityToken.Claims.FirstOrDefault(claim => claim.Type == hostedDomain.Key);
if(hdClaim?.Value == null || hdClaim.Value != hostedDomain.Value)
{
// The claim is null or value is not the trusted google domain - do not authenticate!
c.Fail($"Invalid claim for '{hostedDomain.Key}'! User does not belong to trusted G Suite Domain");
}
return Task.FromResult(0);
}
};
});
services.AddMvc();
}
The above works when an incorrect or null hd claim is given which is done by logging in with an account not in the domain name in the hostedDomain.Value. I tried setting the c.Response.StatusCode = 401; but the user still logs in.
Another way to do this would be to use authorization.
You can set up a default authorization policy that requires the presence of the claim you test for. Then any caller that does not have the claim would get redirected to an access denied page. Something like:
services.AddAuthorization(o =>
{
o.AddPolicy("default", policy =>
{
policy.RequireAuthenticatedUser()
.RequireClaim("hd", "mysite.com");
});
});
services.AddMvc(o =>
{
o.Filters.Add(new AuthorizeFilter("default"));
});

Scraping an ASP.NET website with NodeJS

i am trying to login to my supplier website programatically and get the resseller price by code. i have my username and password provided by the supplier to use in the website which is powred by ASP.NET. i tried to use the request module but got no luck with it.
this the code i used so far :
var request = require('request');
var j = request.jar();
var request = request.defaults({ jar : j }) //it will make the session default for every request
//...
request({
url:"https://www.XXXXX.com/login.aspx",
method:"POST",
form:{
ctl00$cpholder$txtUserName:"YYYYYYYY",
ctl00$cpholder$txtPassword:"ZZZZZZZZ"
}
},
function(err,response,body){
console.log(err);
// here i try to access the product page like a reseller
request({
url:"https://www.XXXXXX.com/productdetails.aspx?id=20000028&itemno=90NB0CL1-M08420",
method:"GET",
}, function(err, response, body){
console.log(err);
//Some logic
});
});
}
this is the login form code ( in pastebin because it is very long )
https://pastebin.com/kwuRdLX4
please help

Fullcalendar + Private google calendar

I m using full calendar for a web app project and I sync it with google calendar of my client, but for the moment only public calendar.
Is there any way to sync with a private calendar ?
Note : We use 0auth to identify and sync with Google account.
Thanks
I think it would work with private calendar using the correct authorization.
Authorizing requests with OAuth 2.0
All requests to the Google Calendar API must be authorized by an authenticated user.
Here is a sample create by Alexandre:
<script type="text/javascript">
var clientId = '<your-client-id>';
var apiKey = '<your-api-key>';
var scopes = 'https://www.googleapis.com/auth/calendar';
function handleClientLoad() {
gapi.client.setApiKey(apiKey);
window.setTimeout(checkAuth,1);
}
function checkAuth() {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true}, handleAuthResult);
}
function handleAuthResult(authResult) {
var authorizeButton = document.getElementById('authorize-button');
if (authResult && !authResult.error) {
authorizeButton.style.visibility = 'hidden';
makeApiCall();
} else {
authorizeButton.style.visibility = '';
authorizeButton.onclick = handleAuthClick;
GeneratePublicCalendar();
}
}
function handleAuthClick(event) {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, handleAuthResult);
return false;
}
// Load the API and make an API call. Display the results on the screen.
function makeApiCall() {
// Step 4: Load the Google+ API
gapi.client.load('calendar', 'v3').then(function() {
// Step 5: Assemble the API request
var request = gapi.client.calendar.events.list({
'calendarId': '<your-calendar-id(The #gmail.com>'
});
// Step 6: Execute the API request
request.then(function(resp) {
var eventsList = [];
var successArgs;
var successRes;
if (resp.result.error) {
reportError('Google Calendar API: ' + data.error.message, data.error.errors);
}
else if (resp.result.items) {
$.each(resp.result.items, function(i, entry) {
var url = entry.htmlLink;
// make the URLs for each event show times in the correct timezone
//if (timezoneArg) {
// url = injectQsComponent(url, 'ctz=' + timezoneArg);
//}
eventsList.push({
id: entry.id,
title: entry.summary,
start: entry.start.dateTime || entry.start.date, // try timed. will fall back to all-day
end: entry.end.dateTime || entry.end.date, // same
url: url,
location: entry.location,
description: entry.description
});
});
// call the success handler(s) and allow it to return a new events array
successArgs = [ eventsList ].concat(Array.prototype.slice.call(arguments, 1)); // forward other jq args
successRes = $.fullCalendar.applyAll(true, this, successArgs);
if ($.isArray(successRes)) {
return successRes;
}
}
if(eventsList.length > 0)
{
// Here create your calendar but the events options is :
//fullcalendar.events: eventsList (Still looking for a methode that remove current event and fill with those news event without recreating the calendar.
}
return eventsList;
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
});
}
function GeneratePublicCalendar(){
// You need a normal fullcalendar with googleApi when user isn't logged
$('#calendar').fullCalendar({
googleCalendarApiKey: '<your-key>',
...
});
}
</script>
<script src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script>
Or
Perform Google Apps Domain-Wide Delegation of Authority
In enterprise applications you may want to programmatically access users data without any manual authorization on their part. In Google Apps domains, the domain administrator can grant to third party applications domain-wide access to its users' data — this is referred as domain-wide delegation of authority. To delegate authority this way, domain administrators can use service accounts with OAuth 2.0.
For additional detailed information, see Using OAuth 2.0 for Server to Server Applications
Hope this helps!
I have tried in the backend with php, use the google php client library to get the events and then put it into fullcalendar. This way, it works.

I am using firebase as a Web client for an app to create a login system however when I enter a username & password & press login it say server error

I am using firebase as a Web client for an app to create a login system however when I enter a username & password & press login it say server error.
How do I resolve the server error and what does it mean by server error?
Thank you and help would be appreciated☺😊😃
The code I used to create the Web client
var ROOT_URL = "https://exampleapp.firebaseio.com/"; // Change to your Firebase App
var FIREBASE_CREDENTIAL = "yourAppSecret"; // Change to your Firebase App Secret
var firebase = {
register : function (email, password, callback) {
var emailReplace = email.replace(/\./g, ",");
var beginRegister = function () {
var requestObj = { "email" : email, "password" : password };
var requestJSON = JSON.stringify(requestObj);
var wcRegister = new SMF.Net.WebClient({
URL : ROOT_URL + "Users/" + emailReplace + ".json?auth=" + FIREBASE_CREDENTIAL,
httpMethod : "POST",
requestHeaders : ['Content-Type:application/json', 'X-HTTP-Method-Override:PATCH'],
requestBody : requestJSON,
onSyndicationSuccess : function (e) {
alert("Begin register success");
},
onServerError : function (e) {
alert("Begin register error");
}
});
wcRegister.run(true);
};
var isTaken = new SMF.Net.WebClient({
URL : ROOT_URL + "Users/" + emailReplace + ".json?auth=" + FIREBASE_CREDENTIAL,
httpMethod : "GET",
requestHeaders : ["Content-Type:application/json"],
onSyndicationSuccess : function (e) {
alert("Is taken sucess");
var response = JSON.parse(isTaken.responseText);
if (response !== null) {
//Email is taken, do something
} else {
beginRegister(); //Email is not taken, continue
}
}, onServerError : function (e) {
//Server Error, do something
alert("Is taken error");
}
});
isTaken.run();
}
};
I am using smartface app studio.
Server error means that your request to given URL couldn't successfully made. I have tested your code and it worked for me with my own firebase configurations.
If you are using custom certificate file, which located in Assets folder of your project and named 'cacert.pem', maybe that CA don't accept that domain I don't know. You can check error message in onServerError with
alert(e.message);
This will give more detailed information about error. Also did you test url and body with another HTTP request tool like hurl.it?
Well, I know it's working because I wrote this code :P but probably it's because you didn't change your Firebase rules to access with your app secret or you didn't properly add the variables values. Did you change FIREBASE_CREDENTIAL to your app secret? Did you change ROOT_URL to the root url of your Firebase app?
If you did both, check your firebase rules, it should be something like:
{
"rules": {
".read": "auth == 'YOUR_APP_SECRET'",
".write": "auth == 'YOUR_APP_SECRET'"
}
}
Remember to change YOUR_APP_SECRET with your app secret (pretty logical, huh :P).
If you already done that and still it doesn't work, check if you are calling the function right, and before trying to log in an user, register him. By the way, the code that you showed is incomplete, it doesn't have the login function, this could be the problem too.
And just to you know, if you want to add more info about the user, you can modify the functions to add as many parameters as you want, but this would be another question right? :P

Resources