retrieving username with signInWithEmailAndPassword - database

i manage my users with the Firebase Admin SDK.
On signup i send email,password and username to my endpoint.
i createUserWithEmailAndPassword and create a doc in my firestore
This way i can check if a document already exists and return an error that the username/handle is already taken.
Firestore
- users
- handle
* email
* userId (from createUserWithAndPassword Response)
* createdAt
After the user signInWithEmailandPassword i only have the token, email and userId.. but i need the handle to get the right user details.
what i get from the docs is that there is a default displayName property but i dont know how to set it on signup.
or should i create a custom Token and store the handle inside of it..
im not sure how to go from here
thanks for your help

This is a simple method for what you want:
mAuth.createUserWithEmailAndPassword(email, password).addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if(task.isSuccessful()){
// Sign in success
FirebaseUser user = mAuth.getCurrentUser();
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName(mName).build();
user.updateProfile(profileUpdates).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "User profile updated.");
}
}
});
}
});
Then, to retrieve it, use this wherever required:
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
// Name, email address etc
String name = user.getDisplayName();
String email = user.getEmail();
}
There are multiple ways, but I recommend to use this one :)

Related

Graph API: Insufficient privileges to complete the operation

I have the following permissions on my registered Azure Function..
This azure function will work as web hook and called by some application/API
but when I try to get data
public async Task<string> FindUpnByEmail(string email)
{
if (string.IsNullOrEmpty(email)) return email;
try
{
var request = new RestRequest("users")
.AddQueryParameter("$filter", $"mail eq '{email}'")
.AddQueryParameter("$select", "userPrincipalName");
var rest = new RestClient(GraphUrl)
{
Authenticator = await GetAuthenticator(),
};
var response = await rest.ExecuteGetAsync(request);
var userResponse = JsonConvert.DeserializeObject<ODataResponse<User>>(response.Content);
return userResponse.Value.Length > 0 ? userResponse.Value[0].UserPrincipalName : email;
}
catch (Exception ex)
{
return email;
}
}
I receive the following error:
Authorization_RequestDenied","message":"Insufficient privileges to
complete the operation.
Try to update the Admin consent to Yes for profile, Read Basic profile.
Or Add read write delegate permission and grant admin consent for that as well.

IdentityServer Refresh Extension Grant

I have implemented an extension grant in my Identity Server instance. The purpose of this is for a mobile app to switch contexts between an authenticated user and a public kiosk type device.
When the user enters this mode, I acquire a new token and include the proper grant type.
I used the IS documentation as a base. Nothing crazy going on here at all, I just add some additional claims to this token to be able to access things in the API the user may otherwise not be set up for.
public class KioskGrantValidator : IExtensionGrantValidator
{
private readonly ITokenValidator _validator;
public KioskGrantValidator(ITokenValidator validator)
{
_validator = validator;
}
public string GrantType => "kiosk";
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
var userToken = context.Request.Raw.Get("token");
if (string.IsNullOrEmpty(userToken))
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
var result = await _validator.ValidateAccessTokenAsync(userToken);
if (result.IsError)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
// get user's identity
var sub = result.Claims.FirstOrDefault(c => c.Type == "sub").Value;
// I add some custom claims here
List<Claim> newClaims = new()
{
new Claim(ClaimTypes.Name, "kiosk")
}
context.Result = new GrantValidationResult(sub, GrantType, claims: newClaims);
return;
}
}
Now, the question is refreshing this token.
For this grant to work I'm passing in the access token, which expires, eventually causing the ValidateAccessTokenAsync to fail.
Wanted to see what the best way to refresh this token is? Currently the best way I have found is to refresh the original user access token when this one is about to expire, then get a second token with the new grant. This works, but seems maybe unnecessary.
Thanks for any input!

Cannot sign in with different account or "Use another account"

I'm trying to integrate Microsoft sso with a Xamarin.Forms app.
I'm using Microsoft.Identity.Client 4.7.1
I struggling to sign in with different accounts on the same device since it seems that the first account is always picked no matter what I do.
User A signs in
User A signs out
User B enters the app opens the webview with the Microsoft login page and prompts the "Use another account" button but even after typing his account, the webview redirects it to back to the mobile app as user A.
Here's the code that handles sign-in and sing-out:
private IPublicClientApplication _publicClientApplication;
public AuthService()
{
_publicClientApplication = PublicClientApplicationBuilder.Create(Constants.MicrosoftAuthConstants.ClientId.Value)
.WithAdfsAuthority(Constants.MicrosoftAuthConstants.Authority.Value)
.WithRedirectUri(Constants.MicrosoftAuthConstants.RedirectUri.Value)
.Build();
}
public async Task<string> SignInAsync()
{
var authScopes = Constants.MicrosoftAuthConstants.Scopes.Value;
AuthenticationResult authResult;
try
{
// call to _publicClientApplication.AcquireTokenSilent
authResult = await GetAuthResultSilentlyAsync();
}
catch (MsalUiRequiredException)
{
authResult = await _publicClientApplication.AcquireTokenInteractive(authScopes)
.WithParentActivityOrWindow(App.ParentWindow)
.ExecuteAsync();
}
return authResult.AccessToken;
}
private async Task<IAccount> GetCachedAccountAsync() => (await _publicClientApplication.GetAccountsAsync()).FirstOrDefault();
public async Task SignOutAsync()
{
var firstCachedAccount = await GetCachedAccountAsync();
await _publicClientApplication.RemoveAsync(firstCachedAccount);
}
A workaround is to use Prompt.ForceLogin but what's the point of sso if you have to type the credentials every time.
The line of code await _publicClientApplication.RemoveAsync(firstCachedAccount); can jsut remove the user from the cache, it doesn't implement a signout method. So you need to do logout manually by the api below:
https://login.microsoftonline.com/common/oauth2/v2.0/logout?post_logout_redirect_uri=https://localhost/myapp/

Blazor with AzureAD Auth, Context.Identity.User.Name is null

Only authenticated users can access the application as expected. I need to be able to track users via signalr. For example, if I run a ChatHub type of service, I'd like people to be able to chat using their AzureAD username which should be set automatically and not let people set their own usernames.
The hub always shows Context.Identity.User.Name is null.
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.AddTransient<HubConnectionBuilder>();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub<App>(selector: "app");
endpoints.MapFallbackToPage("/_Host");
endpoints.MapHub<SomeHub>("/SomeHub");
});
Any idea if here is a way to preserve identity information and pass to SignalR?
Inspect your JWT token and check its claims. You can past it on http://jwt.ms/ to decode it. Then, look for the claims that are being returned that references the user name (in my case it is preferred_username).
Then you can change the default mapping of the Identity.Name using this code:
services.Configure<OpenIdConnectOptions>(AzureADDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters.NameClaimType = "<claim_name_that_returns_username>";
});
My workaround at the moment will be to just pass the username when the connection is created to the hub.
In codebehind (SomePage.razor.cs)
public class SomePageBase : ComponentBase
{
[Inject]
private HubConnectionBuilder _hubConnectionBuilder { get; set; }
[Inject]
private AuthenticationStateProvider authProvider { get; set; }
protected async override Task OnInitializedAsync()
{
var user = (await authProvider.GetAuthenticationStateAsync()).User.Identity.Name;
// in Component Initialization code
var connection = _hubConnectionBuilder // the injected one from above.
.WithUrl("https://localhost:44331/SomeHub")
.Build(); // Build the HubConnection
await connection.StartAsync();
var stringResult =
await connection.InvokeAsync<string>("HubMethodName", user);
}
}

How to list all users along with user profile from ASP.NET Membership?

Right now I'm working with silverlight project and I'm stuck on how to list all of users and user profile together.
Now I'm using this method to get all user via WCF
public IEnumerable<MembershipServiceUser> GetAllUsers()
{
return Membership.GetAllUsers().Cast<MembershipUser>().Select(u => new MembershipServiceUser(u));
}
public void FromMembershipUser(MembershipUser user)
{
this.Comment = user.Comment;
this.CreationDate = user.CreationDate;
this.Email = user.Email;
this.IsApproved = user.IsApproved;
this.UserName = user.UserName;
}
I can get all user from those code above but I don't know how extactly to get user profile
eg. Firstname , Lastname , etc..
You can create a new instance of ProfileBase and access the profile fields with the method GetPropertyValue("propertyName"), where propertyName is the name of your custom registration data.
var profile = ProfileBase.Create(user.UserName);
this.CustomProperty = profile.GetPropertyValue("customPropertyName");
I'm not 100% sure about the syntax, I come from a vb environment and haven't written any c# in a while.
ProfileInfoCollection profiles = ProfileManager.GetAllProfiles(ProfileAuthenticationOption.All);
foreach (ProfileInfo pi in profiles)
{
ProfileCommon p = Profile.GetProfile(pi.UserName);
countries.Add(p.Country);
}

Resources