Firebase oAuth: scope - firebase-authentication

How can I access or get the permission of the extended scopes provided by the third party identity authorizers like Google or Facebook?
Like, accessing the contacts from Facebook or Google+.

For Facebook you should ask for the permissions that you want to get from the user. For example read_custom_friendlists permission will grant you access to the user Facebook friend's list. You can request the permissions using the Facebook SDK and integrating it in your app:
#Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.splash, container, false);
loginButton = (LoginButton) view.findViewById(R.id.login_button);
loginButton.setReadPermissions("email");
// If using in a fragment
loginButton.setFragment(this);
// Other app specific specialization
// Callback registration
loginButton.registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
#Override
public void onSuccess(LoginResult loginResult) {
// App code
}
#Override
public void onCancel() {
// App code
}
#Override
public void onError(FacebookException exception) {
// App code
}
});
}
With loginButton.setReadPermissions("email") you are getting the permission to access to the email of the user. You can check all the permissions that you can get from Facebook developers website.
About Google+, if I'm not wrong you should just use the android sdk to pick contacts from gmail.
hope that it will help you!

Related

How display Firebase In-App Messaging on TWA?

I tried to display In-App Messaging but it didn't show up with TWA.
In-App Messaging works without any problems with normal Activity.
I use https://github.com/GoogleChrome/android-browser-helper/tree/main/demos/twa-basic to test TWA.
My application is correctly configured with Firebase.
I created a campaign.
My logs after publishing the campaign:
I closed my application and then launched it and I didn't see In-App Messaging.
I tested another application with a standard Activity and there was no problem displaying In-App Messaging.
I took a lot of time today to answer this question for myself.
In my opinion TWA can't work with In-App Messaging.
I am not an Android programmer and I could be wrong.
This is my test Activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FirebaseInAppMessaging.getInstance().setMessagesSuppressed(true);
}
public void onClick(View view) {
CampaignMetadata campaignMetadata = new CampaignMetadata("test_campaign", "name", true);
Text title = Text.builder().setText("test").setHexColor("#000000").build();
Text body = Text.builder().setText("test").setHexColor("#000000").build();
ModalMessage message = ModalMessage.builder()
.setBackgroundHexColor("#ffffff")
.setTitle(title)
.setBody(body)
.build(campaignMetadata, null);
FirebaseInAppMessagingDisplay.getInstance().testMessage(this, message, null);
FirebaseInAppMessaging.getInstance().setMessagesSuppressed(false);
}
#Override
protected void onResume() {
super.onResume();
}
}
To display In App Messaging we need a Activity. I think we can only In App Messaging display on splash screen TWA but i didn't try it.

IdentityServer4 How can I redirect after login to the revious url page without registering all routes at IdP

As recommend I would have register the authorize callback url/redirect_url at IdP, which it works.
But what if a client using MVC app tries to access a page with an unauthorized state, will be redirect to idsrv login page.
The redirect_url is always (Home page entry point) as configured.
To change this behavior I would have to register all possible routes at IdP.
That can not a be solution!
On idsrv Login method I have tried:
Login(string returnUrl)
checking the value from returnUrl it gives /connect/authorize/callback?client_id=...
Shouldn't returnUrl have the url of the previous page? Like in a normal mvc app has..
I have tried to get Referer store it on session and then redirect..
if (!string.IsNullOrEmpty(Request.Headers["Referer"].ToString()))
{
this.httpContextAccessor.HttpContext.Session.SetString("Referer", Request.Headers["Referer"].ToString());
}
But that doesn't work Referer comes null...
I have checked what's coming on context from interation services
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
context.RedirectUri
And returns /signin-oidc/ this is the automated way for returning (Home page entry point).
Any chance to get the previous url, so that the user can be redirect?
So what can I do else?
I'm using Hybrid flow to manage the following clients : mvc-app, classic-asp, web api
Here's an example of implementation allowing you to achieve what you want. Keep in mind that there's other ways of doing it.
All the code goes on your client, the server never knows anything about the end url.
First, you want to create a custom attribute that will be decorating all your actions/controllers that you want to protect:
using System;
using System.Web.Mvc;
namespace MyApp
{
internal class MyCustomAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.Result is HttpUnauthorizedResult)
{
filterContext.RequestContext.HttpContext.Session["oidc-returnUrl"] = filterContext.RequestContext.HttpContext.Request.UrlReferrer?.PathAndQuery;
}
}
}
}
And then you are going to create a login route/action that will handle all your authorize requests:
using System.Web.Mvc;
namespace MyApp
{
public class AccountController : Controller
{
[MyCustomAuthorize]
public ActionResult Login()
{
returnUrl = Session["oidc-returnUrl"]?.ToString();
// clean up
Session["oidc-returnUrl"] = null;
return Redirect(returnUrl ?? "/");
}
}
}
The login path can be changed in your startup code:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
LoginPath = "/my-login"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
// setting up your client
});
}
}

How to provide user login with a username and NOT an email?

I'd like to use Firebase for my web app that is for people with dementia in a care home. They do not have email or social network accounts so will need a simple username / password sign up / sign in.
What is the easiest way to do this? From what I can see in the docs I'd have to use a custom auth flow but I do not have an existing auth server.
If I do need ot do this what is the easiest way to provide the token? In Azure there is Functions and AWS has Lambda but I see nothing here is Firebase
You are correct that username/password sign-in is not supported natively in Firebase Auth at this moment.
You can implement a custom provider as shown in this example. This allows you to meet any custom requirements, but is admittedly a bit more involved than using the built-in providers. There is an example of this here that you can use as a starting point.
A workaround you could take without needing to use custom auth with another backend is to accept usernames in your UI, but on the underlying logic, append "#yourowndomain.com" before calling the functions to sign up or sign in with email.
So you would be using email/password authentication, mapping <username> to <username>#yourowndomain.com
You can use sign in with custom token
Firebase gives you complete control over authentication by allowing you to authenticate users or devices using secure JSON Web Tokens (JWTs). You generate these tokens on your server, pass them back to a client device, and then use them to authenticate via the signInWithCustomToken() method.
You need to save username and password in your database or rtdb or firestore
When user touch the login button, client will send username and password to your backend. If the username and password correct, generate custom token and send it back to the client
Client then can login with custom token from the server using signInWithCustomToken() method
More detail can be read in this documentation
Appending a dummy domain at end is a kind of a patch up and should be avoided.
To enable username login just follow these simple steps.
Sign Up
During sign up take the userid , email and password . Register the user with normal email and password. On Success of it save the email against the user_id in a separate node(branch).
mButtonSignUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(isValid()){
mProgressBar.setVisibility(View.VISIBLE);
final String userId = mEditTextUserId.getText().toString();
final String emailId = mEditTextEmail.getText().toString() ;
String password = mEditTextPassword.getText().toString() ;
firebaseRef.createUser(emailId, password, new Firebase.ResultHandler() {
#Override
public void onSuccess() {
firebaseRef.child("user_ids").child(userId).setValue(emailId);
Toast.makeText(getBaseContext(),"You are successfully registered ",Toast.LENGTH_SHORT).show();
mProgressBar.setVisibility(View.GONE);
}
#Override
public void onError(FirebaseError firebaseError) {
mProgressBar.setVisibility(View.GONE);
Toast.makeText(getBaseContext(),firebaseError.toString(),Toast.LENGTH_SHORT).show();
}
});
}
}
});
Database
Database structure will look like this
Login
Check if the user has entered an email or userId. If it is a email id then directly perform login with it otherwise fetch the email id associated with the username and perform login.
Button buttonLogIn = (Button)findViewById(R.id.button_login);
buttonLogIn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mProgressBar.setVisibility(View.VISIBLE);
String username = mEditTextEmail.getText().toString() ;
final String password = mEditTextPassWord.getText().toString() ;
// Check if it is an email or not
if(android.util.Patterns.EMAIL_ADDRESS.matcher(username).matches()) {
performLogin(username,password);
}else{
//get the emailId associated with the username
firebaseRef.child("user_ids").child(username)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot!=null){
String userEmail = dataSnapshot.getValue(String.class);
performLogin(userEmail,password);
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
//Handle Error
}
});
}
}
});
private void performLogin(String emailId, String password) {
firebaseRef.authWithPassword(emailId,password, new Firebase.AuthResultHandler() {
#Override
public void onAuthenticated(AuthData authData) {
uid = authData.getUid() ;
Toast.makeText(getBaseContext(), authData.toString(), Toast.LENGTH_SHORT).show();
}
#Override
public void onAuthenticationError(FirebaseError firebaseError) {
Toast.makeText(getBaseContext(), firebaseError.toString(), Toast.LENGTH_SHORT).show();
}
});
}
You may use Alfonso's solution as well. And where you need a real e-mail, you can set an textfield for an e-mail when the user registers and you can keep it in your database and you can use it.
I couldn't find support by firebase for this.
However, doing the following will solve your problem.
SIGN UP
private void clickListener() {
registerbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String email = emailEdit.getText().toString();
String pass = passwordEdit.getText().toString();
if (email.isEmpty()) {
emailEdit.setError("Lutfen Kullanici Adinizi Giriniz.");
return;
}
if (pass.isEmpty()) {
passwordEdit.setError("Lütfen Parolanizi Giriniz.");
return;
}
email=email+"#gmail.com";
createAccount(email,pass);
}
});
}
private void createAccount(String email, String pass){
auth.createUserWithEmailAndPassword(email, pass)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()){
FirebaseUser user=auth.getCurrentUser();
updateUi(user,pass);
}
else{
Toast.makeText(MainActivity.this, "Error: "+
task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
This way we add the phrase "#gmail.com" to the email. email=email+"#gmail.com";
Let the value we take as an example be "abdulkerim". Even if the user enters the phrase "abdulkerim", we get a username instead of an email, thanks to the "#gmail.com" we added to the end.
database view

Intercepting an encrypted login token in a request

I am working on an MVC site that has some pages that need authentication and others that don't. This is determined using the Authorize and AllowAnonymous attributes in a pretty standard way. If they try to access something restricted they get redirected to the login page.
I'm now wanting to add the functionality to automatically log them in using an encrypted token passed in the querystring (the link will be in emails sent out). So the workflow I want now is that if a request goes to a page that is restricted and there is a login token in the querystring I want it to use that token to log in. If it logs in successfully then I want it to run the original page requested with the new logged in context. If it fails to log in then it will redirect to a custom error page.
My question is where would I need to insert this logic into the site?
I have seen some suggestions on subclassing the Authorize attribute and overriding some of the methods but I'm not 100% sure how to go about this (eg what I would override and what I'd do in those overridden methods.
I've also had a look at putting the logic at a controller level but I am led to understand that the authorize attribute would redirect it away from the controller before any code in the controller itself was run.
It would be better to write a custom authorization attribute that will entirely replace the default functionality and check for the query string parameter and if present, decrypt it and authenticate the user. If you are using FormsAuthentication that would be to call the FormsAuthentication.SetAuthCookie method. Something along the lines of:
public class TokenAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
string token = filterContext.HttpContext.Request["token"];
IPrincipal user = this.GetUserFromToken(token);
if (user == null)
{
this.HandleUnAuthorizedRequest(filterContext);
}
else
{
FormsAuthentication.SetAuthCookie(user.Identity.Name, false);
filterContext.HttpContext.User = user;
}
}
private IPrincipal GetUserFromToken(string token)
{
// Here you could put your custom logic to decrypt the token and
// extract the associated user from it
throw new NotImplementedException();
}
private void HandleUnAuthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Shared/CustomError.cshtml",
};
}
}
and then you could decorate your action with this attribute:
[TokenAuthorize]
public ActionResult ProcessEmail(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}

ASP.Net/MVC Authorize Vs Authenticate

So I set this above my Controller:
[Authorize(Roles="Administrator")]
The problem is whether they are not logged in, or don't have the right role, it redirects them to the login page. Is there a way to have it handle authorization and authenticate differently?
I might not understand you clearly, but authentication and authorization are always coming together.. One says which mechanism use to authenticate user (forms, windows etc.), and second which roles or users are allowed to see the content...
As far as authentication method is set in your web config it is fixed, and only think you can use to protect your controller methods is to put those attributes.
Also if you want to use it diffrently, f.e. redirect to diffrent page you can use following code:
public class RedirectAuthorizeAttribute : AuthorizeAttribute
{
public string RedirectUrl { get; set; }
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectResult(RedirectUrl);
}
}
and then put it onto your controller method like that:
[RedirectAuthorize(Roles = "MyRole", RedirectUrl = "SomeUrl")]
public ActionResult SomeAction()
{
...
}