Xamarin Forms Open URI with token - xamarin.forms

I am building an Xamarin Forms PCL app and getting authenticated successfully with MSAL. My REST calls to graph API are all successful.
What I am trying to do is to open outlook or yammer or calendar in the browser using the token, i.e. without asking the users the re-authenticate.
Device.openUri always sends the users to an auth page, which makes sense since i am not sending the token with it.
Is this possible at all? If so, How can it be done?
thanks in advance !

Please add permission (to Yammer for example) on your App registration in Azure.
then define your array of scope as following
string[] Scopes = { "User.Read", "https://api.yammer.com/user_impersonation" };
then initiate your PublicClientApplication (PCA)
PCA = PublicClientApplicationBuilder.Create(ClientId).WithRedirectUri(ReturnUrl).Build();
after that as per the example from MSAL Team here you will need to add this code in your login button
try
{
IAccount firstAccount = accounts.FirstOrDefault();
authResult = await App.PCA.AcquireTokenSilent(App.Scopes, firstAccount)
.ExecuteAsync();
/* display info*/
}
catch (MsalUiRequiredException ex)
{
try
{
authResult = await App.PCA.AcquireTokenInteractive(App.Scopes, App.ParentWindow)
.ExecuteAsync();
/* display info*/
}
}
Now the Token you will get will work with Yammer as well
Hope this would help

Related

Flutter Firebase Auth Facebook Login change account / require username and password again

I am using Firebase to implement social logins in our application. I have a weird problem with Facebook login where it seems like it is impossibile to change the account. Let me explain:
User clicks on Login with Facebook button
Web view or browser opens up
User logs in with its credentials
Everything works and user can use our app
User logs out within the app
User wants to log in to facebook with a different account, and clicks on login with facebook button
At this time, there's no way to change facebook account. The webview doesn't let you.
I tried to clean the app cache, deleting the app, didn't work.
On Android, I need to clear the Browser cache, or logout from Facebook from the browser, and then I can Login with FB correctly with a new account.
On iOS, even deleting the Safari cache and everything won't work.
The social login has been implemented following the official documentation, and it's like this:
Future<UserCredential?> signInWithFacebook() async {
try {
if ( await FacebookAuth.instance.accessToken != null )
await logoutFacebook();
// Trigger the sign-in flow
LoginResult loginResult;
loginResult = await FacebookAuth.instance.login(
permissions: ['email', 'public_profile'],
loginBehavior: LoginBehavior.webOnly,
);
if (loginResult.status == LoginStatus.success) {
final AccessToken accessToken = loginResult.accessToken!;
final OAuthCredential facebookAuthCredential = FacebookAuthProvider.credential(accessToken.token);
return await FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);
} else
throw loginResult.status.toString() + "\n" + loginResult.message.toString();
} catch (e) {
LoggerService().error("FACEBOOK LOGIN ERROR", e.toString());
FacebookAuth.instance.logOut();
return null;
}
}
At the moment I am limited with Flutter 1.22.6 and so I can only use these firebase packages:
firebase_messaging: ^10.0.7
flutter_local_notifications: ^8.2.0
firebase_crashlytics: ^2.2.1
firebase_core: ^1.6.0
firebase_auth: ^3.1.1
flutter_facebook_auth: ^3.5.2
Do you have any idea on how to solve this problem?
Best regards
EDIT: refactored everything to Flutter 2, but I still have the same issue.
According to the documentation below it is not available to switch users
Note
The Android and iOS SDKs don't currently support re-authentication.
https://developers.facebook.com/docs/facebook-login/reauthentication/

ionic firebase phoneAuth without recaptcha on ios

I have an ionic app where i use firebase phone authentication which uses recaptcha. It works fine on android but throws error on ios saying recaptcha can only be run in an http environment. I would like to know if there's a way to perform firebase phone auth without using recaptcha.
this.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container',{
'size': 'invisible'
});
let appVerifier = this.recaptchaVerifier;
this.appService.sendPhoneVerification(phoneNumber,appVerifier)
.then(confirmationResult => {
//do something
})
Ios throws error 'RECAPTCHA can only be run in HTTP/HTTPS environment'
Well this is how I solved my issue "'RECAPTCHA can only be run in HTTP/HTTPS environment'".
Install the Firebase Plugin :plugin link
Add the it to your app.module.ts.
Make a platform check: to check if its iOS.
if (this.plt.is('ios')) {
//ios code here
} else {
//android here
}
Now add the following code (iOS platform) to send a verification code sms to the user to verify the phone number. Inject the plugin into the constructor. Create a variable to assign the data from the promise. Phone number should be country code + number. example '+19999999999'
public signInUser(phoneNum) {
this.firebase.verifyPhoneNumber(phoneNum).then((vdata) => {
this.refConfirm = vdata;
//you can redirect the person to a verification page or show an alert to
input verification code.
});
}
Now create a token to verify and sign in user with credentials using firebase.
public verifyPhoneNumber(phoneNumber) {
let tokenPhone = firebase.auth.PhoneAuthProvider.credential(this.refConfirm,
phoneNumber);
firebase.auth().signInWithCredential(tokenPhone).then((verifiedData) => {
//whatever you want to do here or redirect the user to home page.
});
}
Generate your GoogleService.plist on Firebase & add to your project root directory
You have to add reversed client id instead of normal one.
This is how I solved it.

Firebase service account to generate authentication token for client-side use with Google Apps Script

I am having difficulty using the FirebaseApp (a 3rd party API) to generate an authentication token that can be passed to a sidebar and used by the client to login and access my Firebase Database client-side.
I'm trying to use this tutorial but cannot get it working without using a database secret (which is being depreciated) in makeToken(). I'd prefer to use a service account as reflected in this tutorial. When I look at the difference between the tokens generated, the first 2 pieces separated by a '.' are identical, the last piece after the final '.' is different. The lengths are the same as well. eg:
//Example Generated by Database Secret: TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBv.ZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=.dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2U=
//Example Generated by Service Account: TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBv.ZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=.IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbml=
I can generate the OAuth access token, pass it to FirebaseApp and generate an authentication token, but when it is passed client-side and I attempt to authenticate I get an error: Login Failed! Error: INVALID_TOKEN: Failed to validate MAC.
It seems like there is a lot of misinformation and conflicting information on how this should be done.
I have a getFirebaseService() function server-side that uses Apps Script OAuth2 Library to get an access token.
function getFirebaseService() {
return OAuth2.createService('Firebase')
// Set the endpoint URL.
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
// Set the private key and issuer.
.setPrivateKey(fb_PRIVATE_KEY) //Service account private key
.setIssuer(fb_SERVICE_EMAIL) //Service account email
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getScriptProperties())
// Set the scopes.
.setScope('https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/firebase.database');
}
I have a makeToken() function server-side that gets an authentication token from Firebase using the OAuth access token. I am able to use the service.getAccessToken() OAuth token server-side to access and store data. So that works, I guess my issue is creating a client auth token that's more restrictive.
function makeToken(){
var service = getFirebaseService();
if (service.hasAccess()) {
return FirebaseApp.getDatabaseByUrl(fb_URL, service.getAccessToken()) //Database Secret Works: "AAslhfi3MYACCESSTOKEN2930hf03ah4th8" but is being depreciated.
.createAuthToken(Session.getActiveUser().getEmail());
} else {
Logger.log("makeToken: " + service.getLastError());
}
}
Then client-side, from the sidebar, I try to authenticate with a custom auth token retrieved server-side from makeToken().
var userAuthToken;
google.script.run.withSuccessHandler(function (requestAuthToken) {
userAuthToken = authenticateClient(requestAuthToken)
}).makeToken();
function authenticateClient(userRequestToken) {
var ref = new Firebase(fb_URL);
ref.authWithCustomToken(userRequestToken, function (error, authData) {
if (error) {
console.log("FB Login Failed!", error); //Error below come from here.
}
else {
console.log("FB Login Succeeded!", authData);
}
});
return ref.authData.auth;
}
This results in Login Failed! Error: INVALID_TOKEN: Failed to validate MAC..
Edit: Is it possible FirebaseApp is incorrectly generating the JWT Authentication Token?
Edit2: I think the above edit is unlikely as I attempted to use the GSApp library and had the same issue. It only seems to want the depreciated database secret, not a service account OAuth.
Alright, so after a very long day I figured it out. I'm going to lay out what I ended up using for libraries and what the issue was (see the third library). The main problem was essentially that the tutorial was outdated and no a lot of people use Firebase in apps script.
OAuth2 (Server-side)
Link
I didn't have to change anything here! It was working fine and never an issue.
FirebaseApp (Server-side)
Link
This is a nice library and I stuck with it because it worked well (once I got it there). I had to make a change to my original code that came from the tutorial I mentioned. My code ended up like this and worked:
if (service.hasAccess()) {
return FirebaseApp.getDatabaseByUrl(fb_URL, service.getAccessToken()) //get OAuth Token
.createAuthToken(Session.getEffectiveUser().getEmail(), null, serviceAccount.client_email, serviceAccount.private_key);
//... Added the null, private key, and service email parameters.
Firebase (Client-side)
Link
Alright, so this is where my main issue was -- The tutorial I followed for client-side setup was old. I had to upgrade the code on my own to use the new 3.x version:
<script src="https://www.gstatic.com/firebasejs/5.8.2/firebase.js"></script>
// Initialize Firebase
var config = {
apiKey: "<Web API Key>",
authDomain: "<Project ID>.firebaseapp.com",
databaseURL: "https://<DB URL>.firebaseio.com/"
};
firebase.initializeApp(config);
With this firebase instance I was able to update my original authenticateClient() method:
function authenticateClient(userRequestToken) {
firebase.auth().signInWithCustomToken(userRequestToken).catch(function(error) {
// Handle Errors here.
console.error("authClient: ", error.code, error.message);
});
return {
uid: firebase.auth().currentUser.uid,
metadata: {
lastSignInTime: firebase.auth().currentUser.lastSignInTime
}
};
}
That's it! I now have a firebase instance with a signed in user via JWT Custom Token! I came across a few people with similar issues an I hope this helps.

Firebase Web Admin

First of all, I am using nodejs for the backend. I use firebase hosting and firebase functions to deploy an express() app.
What I am trying to achieve is to make an admin website, which is connected to Firebase. so I have a route /admin/ like this:
adminApp.get("/", (request, response) => {
return response.redirect("/admin/login");
});
Here I basically want to check if a current user is logged in - or not.
I know firebase supports client side authentication using:
firebase.auth().onAuthStateChanged(user => {
if (user) {
} else {
}
});
And using
function login() {
var userEmail = document.getElementById("email").value;
var userPass = document.getElementById("password").value;
firebase.auth().signInWithEmailAndPassword(userEmail, userPass).catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
if (error) {
document.getElementById('loginError').innerHTML = `Error signing in to firebase`;
}
});
}
However image this case:
Someone (not an admin) is visiting /admin/some_secret_website/ which he obviously does not have access to.
If I rely on client side authentication, it first loads the entire website and the scripts and then notices - hey I am not authenticated, let me redirect to /login. By then however anyone knows the source code of an admin page.
I'd rather have something like:
adminApp.get("/admin/some_secret_website", (request, response) => {
if (request.user) {
// user is authenticated we can check if the user is an admin and give access to the admin page
}
});
I know that you can get the user's token and validate that token using the AdminSDK, but the token must be send by the client code, meaning the website was already loaded.
I came across Authorized HTTPS Endpoint by firebase, but it only allows a middleware when using a bearer token.
Does anybody know how I can maintain a server side user object to not even return admin html to the browser but only allow access to admins?
Like Doug indicated, the way your admin website/webapp would function with Firebase Cloud Functions (which is effectively a Nodejs server) is that you get the request, then use the headers token to authenticate them against Firebase Auth. See this answer for a code snippet on this.
In your case, I'm thinking you would create a custom claim for an "administrator" group and use that to determine whether to send a pug templated page as a response upon authentication. As far as Authorization, your db rules will determine what said user can CRUD.

Issue with jwt-bearer on-behalf-of grant in Azure AD

So I have an Angular app that uses the adal-angular library to authenticate with an ASP.NET Core 2.0 Web API. The API then uses on-behalf-of flow to authenticate with another API using the users token like this MS article https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-on-behalf-of.
The issue I have is this is working fine in the DEV environment but I have now deployed a TST environment with separate App Registrations and I am receiving the following exception when I try and request the token using on-behalf-of
AADSTS240002: Input id_token cannot be used as 'urn:ietf:params:oauth:grant-type:jwt-bearer' grant.
The code I am using to request the token
public async Task<string> AcquireTokenAsync(string resource)
{
try
{
string accessToken = await _httpContextAccessor.HttpContext.GetTokenAsync(AuthenticationConstants.AccessToken);
var credentials = new ClientCredential(_azureOptions.ClientId, _azureOptions.ClientSecret);
var authContext = new AuthenticationContext($"{_azureOptions.Instance}{_azureOptions.TenantId}")
{
ExtendedLifeTimeEnabled = true
};
// On-behalf-of auth token request call
var authResult = await authContext.AcquireTokenAsync(
resource,
credentials,
new UserAssertion(accessToken));
return authResult.AccessToken;
}
catch (AdalServiceException asex)
{
_logger.LogError(asex, $"Instance: {_azureOptions.Instance} Tenant: {_azureOptions.TenantId} ClientId: {_azureOptions.ClientId}");
throw;
}
catch (System.Exception ex)
{
_logger.LogError(ex, ex.Message);
throw;
}
}
And I have used Fiddler and it looks like all the correct parameters are being passed.
Any help would be very much appreciated. I have set knownClientApplications on the second API and I have granted permissions on the Angular backend API to the second API.
For me, I got it to work by changing BOTH of the following to true:
oauth2AllowImplicitFlow
oauth2AllowIdTokenImplicitFlow
See here for more information.
According to your question and the error, it should be caused by that you angular app is not a Native(public) app.
For using this OBO flow with this Grant type, your client must be a public client not credential client.
If you want to register your client as a WebApp/API, you can refer to this Implementation:
Hope this helps!
Update
According to OP's comment, he/she got it working by changing oauth2AllowImplicitFlow from false to true.
We had this problem last week with one Azure Service Registration and not another. A review found that the token didn't return an AIO being returned. It turns out that the registration had redirects with wildcards (e.g., https://*.ngrok.io) and this is incompatible with the AcquireTokenOnBehalfOf function. I'm posting this here so a future person, probably me, will find it.
I was having problems even when oauth2AllowImplicitFlow and oauth2AllowIdTokenImplicitFlow were set to true. One of my Reply URLs had a wildcard in it. When the wildcard was removed, the issue was resolved.

Resources