I have an app (currently in UWP) that makes use of MobileServiceClient and AutoRest to an Azure App Service API App. I successfully used the winfbsdk and can authenticate thru that and then get it to login to MobileService.LoginAsync with the FB access token as a JObject. I also take that JObject and send it in the x-zumo-auth header when making calls to the API App via AutoRest within the app.
What I would like to do is be able to authenticate using MicrosoftAccount. If I use MobileService.LoginAsync, I cannot get the proper token and pass it along to AutoRest - it always comes back as 401 Unauthorized.
I tried to used MSAL, but it returns a Bearer token and passing that along also comes back as 401 Unauthorized.
Is there any good way to do this? I started on the route of MSAL since that would support Windows desktop, UWP and Xamarin Forms which will be ideal. I just need info on how to get the proper token from it to pass along to an AutoRest HttpClient that goes back to the Azure App Service API App.
Update:
If I use the following flow, it works with Facebook, but not with MicrosoftAccount.
-Azure AppService with WebAPI (and swagger for testing via a browser)-Security setup through the Azure Dashboard on the service and configured to allow Facebook or MicrosoftAccount
1. On my UWP app, using winfbsdk, I login with Facebook, then grab the FBSession.AccessTokenData.AccessToken and insert that into a JObject:
JObject token = JObject.FromObject
(new{access_token = fbSession.AccessTokenData.AccessToken});
2. Login to MobileServiceClient
user = await App.MobileService.LoginAsync
(MobileServiceAuthenticationProvider.Facebook, token);
Login to API App with HttpClient and retrieve the token to use in X-ZUMO-AUTH
using (var client = new HttpClient())
{
client.BaseAddress = App.MobileService.MobileAppUri;
var jsonToPost = token;
var contentToPost = new StringContent(
JsonConvert.SerializeObject(jsonToPost),
Encoding.UTF8, "application/json");
var asyncResult = await client.PostAsync(
"/.auth/login/" + provider.ToString(),
contentToPost);
if (asyncResult.Content == null)
{
throw new InvalidOperationException("Result from call was null.");
return false;
}
else
{
if (asyncResult.StatusCode == System.Net.HttpStatusCode.OK)
{
var resultContentAsString = asyncResult.Content.AsString();
var converter = new ExpandoObjectConverter();
dynamic responseContentAsObject = JsonConvert.DeserializeObject<ExpandoObject>(
resultContentAsString, converter);
var applicationToken = responseContentAsObject.authenticationToken;
ApiAppClient.UpdateXZUMOAUTHToken(applicationToken);
}
}
}
ApiAppClient.UpdateXZUMOAUTH call just does the following:
if (this.HttpClient.DefaultRequestHeaders.Contains("x-zumo-auth") == true)
{
this.HttpClient.DefaultRequestHeaders.Remove("x-zumo-auth");
}
this.HttpClient.DefaultRequestHeaders.Add("x-zumo-auth", applicationToken);
Any subsequent calls using the ApiAppClient (created with AutoRest from the swagger json of my Azure AppService WebAPI) contain the x-zumo-auth header and are properly authenticated.
The problem occurs when trying to use MicrosoftAccount. I cannot seem to obtain the proper token to use in x-zumo-auth from either MSAL or LoginWithMicrosoftAsync.
For #1 above, when trying for MicrosoftAccount, I used MSAL as follows:
AuthenticationResult result = await MSAuthentication_AcquireToken();
JObject token = JObject.FromObject(new{access_token = result.Token});
And MSAuthentication_AcquireToken is defined below, using interfaces and classes as suggested in the Azure samples: https://github.com/Azure-Samples/active-directory-xamarin-native-v2
private async Task<AuthenticationResult> MSAuthentication_AcquireToken()
{
IMSAcquireToken at = new MSAcquireToken();
try
{
AuthenticationResult res;
res = await at.AcquireTokenAsync(App.MsalPublicClient, App.Scopes);
return res;
}
}
Update - ok with MobileServiceClient, but still not working with MSAL
I got it working with MobileServiceClient as follows:
1. Use MobileService.LoginAsync
2. Take the returned User.MobileServiceAuthenticationToken
3. Set the X-ZUMO-AUTH header to contain the User.MobileServiceAuthenticationToken
user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
applicationToken = user.MobileServiceAuthenticationToken;
ApiAppClient.UpdateAppAuthenticationToken(applicationToken);
MSAL still not working!
So the original question still remains, what part of the token returned from MSAL do we need to pass on to X-ZUMO-AUTH or some other header so that calls to the Azure AppService WebAPI app will authenticate?
I have an app (currently in UWP) that makes use of MobileServiceClient and AutoRest to an Azure App Service API App. I successfully used the winfbsdk and can authenticate thru that and then get it to login to MobileService.LoginAsync with the FB access token as a JObject. I also take that JObject and send it in the x-zumo-auth header when making calls to the API App via AutoRest within the app.
According to your description, I assumed that you are using Client-managed authentication. You directly contact the identity provider and then provide the token during the login with your mobile back-end, then you could leverage MobileServiceClient.InvokeApiAsync to call your API APP, which would add the X-ZUMO-AUTH header with the value authenticationToken after you invoke MobileServiceClient.LoginAsync(MobileServiceAuthenticationProvider.Facebook, token);
What I would like to do is be able to authenticate using MicrosoftAccount. If I use MobileService.LoginAsync, I cannot get the proper token and pass it along to AutoRest - it always comes back as 401 Unauthorized. I tried to used MSAL, but it returns a Bearer token and passing that along also comes back as 401 Unauthorized. Is there any good way to do this?
AFAIK, for the client-flow authentication patterns (AAD, Facebook, Google), the token parameter for LoginAsync would look like {"access_token":"{the_access_token}"}.
For the client-flow authentication (Microsoft Account), you could leverage MobileServiceClient.LoginWithMicrosoftAccountAsync("{Live-SDK-session-authentication-token}"), also you could use LoginAsync with the token parameter of the value {"access_token":"{the_access_token}"} or {"authenticationToken":"{Live-SDK-session-authentication-token}"}. I have tested LoginAsync with the access_token from MSA and retrieve the logged info as follows:
In summary, when you retrieve the authentionToken after you have logged with your mobile back-end, you could add the X-ZUMO-AUTH header to each of your API APP requests with the authentionToken.
For more details, you could refer to this official document about authentication works in App Service.
UPDATE
I have checked this https://github.com/Azure-Samples/active-directory-xamarin-native-v2 and used fiddler to capture the network packages when authenticating the user and get an access token. I found that MSAL is working against Microsoft Graph and REST and when the user is logged, you could only retrieve the access_token and id_token, and both of them could not be used for single sign-on with your mobile back-end.
While the official code sample about Client-managed authentication for Azure Mobile Apps with MSA is using the Live SDK. As the Live SDK REST API mentioned about signing users, you could get an access token and an authentication token which is used for single sign-on scenario. Also, I have checked the Server-managed authentication and found that app service authentication / authorization for MSA also uses the Live SDK REST API.
In summary, you could not use MSAL for client-managed authentication with MSA, for client-managed authentication, you need to leverage Live SDK to retrieve the authentication_token then invoke MobileServiceClient.LoginWithMicrosoftAccountAsync("{Live-SDK-session-authentication-token}") to retrieve the authenticationToken from your mobile backend. Or you could just leverage server-managed authentication for MSA. For more details about Live SDK, you could refer to LiveSDK.
Related
I have a client application which authenticates with facebook and returns the token successfully. I would like to persist this data to the server without having to pass the entire object. Instead I would like to pass the resulting token from the client side authentication to my C# api in the authorization header and validate this token on the server.
Question, is it possible to verify the token on the server side? How to do it?
I am doing this via google, and need a facebook equivalent:
var googleResult = GoogleJsonWebSignature.ValidateAsync(
accessToken,
new GoogleJsonWebSignature.ValidationSettings
{
Audience = new[] { "secret key here.apps.googleusercontent.com" }
}
).GetAwaiter().GetResult();
Create a default Blazor server (not webassembly) application using windows identity platform as authorisation (I've used VS2022 / .net 6).
Is it possible to get hold of a JWT token in a code section of a blazor component (e.g. the LoginDisplay)?
For instance - I can get hold of the claims from the authentication state as follows (for my example in LoginDisplay.razor)
#code
{
[CascadingParameter] private Task<AuthenticationState> authenticationStateTask { get; set; }
protected override async Task OnInitializedAsync()
{
var authState = await authenticationStateTask;
var user = authState.User;
var identity = user.Identity as ClaimsIdentity;
if (identity != null)
{
IEnumerable<Claim> claims = identity.Claims;
// Can I get the a JWT Token signed by Azure B2C here?
}
}
}
Can I also get a JWT Token from the Azure service (I don't want to regenerate my own - I want an original one signed by microsoft).
As clarification - when using the MSAL javascript libraries on a different project (for a Single Page Application) I could do calls such as MSAL.acquireTokenSilent to get a token from the Azure B2C service for this purpose.
UPDATE - If HttpContext.GetTokenAsync returns null
In addition enets answer below. If you find that you can't access the JWT token using HttpContext.GetTokenAsync then see this question
You can access the access token and the refresh tokenas describe below, provided that you've set your app to use Jwt Token authentication (OpenID Connect). See this answer how to do that. Note: There is also a second answer related to that question by the same user. Search for it. This answer can also be useful. See this answer, which contains links to useful answers. Note: You can Google search string such as this: "enet stackoverflow blazor jwt token", and such like to find answers by me. If you want to see answers about the AuthenticationStateProvider, just search "enet stackoverflow blazor AuthenticationStateProvider"
Getting the access token
in _Host.cshtml you can code something like this:
#using Microsoft.AspNetCore.Authentication
#{
var tokens = new InitialApplicationState
{
AccessToken = await HttpContext.GetTokenAsync("access_token"),
RefreshToken = await HttpContext.GetTokenAsync("refresh_token")
};
}
And then pass the tokens object to your Blazor app like this:
<component type="typeof(App)" render-mode="ServerPrerendered" param-
InitialState="tokens"/>
Note that the tokens object is passed as a parameter to the App component, something like this:
#code{
[Parameter]
public InitialApplicationState InitialState { get; set; }
protected override Task OnInitializedAsync()
{
TokenProvider.AccessToken = InitialState.AccessToken;
TokenProvider.RefreshToken = InitialState.RefreshToken;
return base.OnInitializedAsync();
}
}
Note: TokenProvider is a singleton service instance that hold the JWT tokens, and make it available to other parts of your app. You can save the JWT tokens in the local storage or better the the session storage and read them when needed, etc.
Note: If you don't use Web Api, then you don't need Jwt token (authentication). Use Microsoft Identity authentication
To quote MS:
The built-in AuthenticationStateProvider service for Blazor Server apps obtains authentication state data from ASP.NET Core's HttpContext.User. This is how authentication state integrates with existing ASP.NET Core authentication mechanisms.
The Jwt token is in there as bearer.
To get the header there's a Question/Answer here by #enet that shows you how to access the HttpRequest from a Blazor Server App. - How to use the HttpContext object in server-side Blazor to retrieve information about the user, user agent.
I don't have a handy project which has JWT tokens to russle up some code for you. Someone else may be able to add another answer with code or add some to this.
I have set up a Keycloak server and a user named 'sample' is given permissions to access the rest ADMIN APIs, I granted permissions to the relevant realm and client_id. And I'm able to access the rest APIs using the postman service using this user credentials 'sample/sample'.
so through Angular application, I was trying to access the API that fetches the roles in a specific realm. since not all the login user will have the rest admin access, I'm using the user credentials(sample/sample) that have the access to admin API, but when I try to access the API, the APIs are forbidden,
this.getKeycloakAccessToken().subscribe((Tokres:any)=>{
console.log('accessToken: ', Tokres.body.access_token);
if(Tokres && Tokres.status === 200 && Tokres.body.access_token){
this.getKeycloakRoles(Tokres.body.access_token).subscribe((roleRes:any)=>{
console.log(roleRes);
},(roleErr:any)=>{
console.log('error while fetching roles..');
console.log(roleErr);
})
}
},(tokErr:any)=>{
console.log('error while accessing keycloak token...');
console.log(tokErr);
})
getKeycloakAccessToken(){
const url = 'http://keycloak-keycloak.router.default.svc.cluster.local.......nip.io/auth/realms/myRealm/protocol/openid-connect/token';
const authH = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded');
const body = new HttpParams()
.set('username', 'sample')
.set('password', 'sample')
.set('grant_type', 'password').set('client_id','rest-client');
return this.http.post(url, body,{headers:authH,observe:'response'});
}
getKeycloakRoles(access_token){
const url = 'http://keycloak-keycloak.router.default.svc.cluster.local........nip.io/auth/admin/realms/myRealm/roles'
const authH = new HttpHeaders().set('Authorization','Bearer ' + access_token);// ({'Authorization':'Bearer ' + access_token});
return this.http.get(url,{headers:authH,observe:'response'});
}
and when I tried to debug, the access_token shown in console is different from that of request headers
[![network log][2]][2]
After debugging for couple of days, I figured out the reason for the difference in Access token, the API call is being invoked with the access_token of logged in session, though the program has source code written to set the headers set with access token of user 'sample/sample'. is there any way to trigger the API with the given access_token rather with the logged in user's access_token.
This might not be the solution, but just a couple of workarounds that worked for me.
Allow permissions (set 'Relam Management') to all the logged in users from the key cloak admin console, this way irrespective of user, whoever logs in will be able access the rest Admin APIs, follow this below
reference
From keycloak client library, we have a initializeKeycloak() , that has configurations set for the application, so disable the 'enableBearerInterceptor' which will say the application not to use the access_token generated by logged in user to set the headers of each request. this way we can avoid the forbidden error.
But with approach no.2, you can not use the AT of logged in user as we r disabling the enableBearerInterceptor.
And with solution no.1, if you are not having control on who are the users logging in to your application, i,e using some third party tool like LDAP to set the users, then it this won't serve the solution.
Soo...I have an app with flask backend and flutter frontend. It utilizes flask_login to manage users. Problem is - I don't know how to maintain session on client side. Flutter client gets response from server, but I don't see any token, or user_id inside.
So far, I've tried to parse responce, with no luck and I've used solution from How do I make an http request using cookies on flutter?
also, without success.
Server-side https://github.com/GreenBlackSky/COIN/blob/master/api_app/app/login_bp.py
Client-side https://github.com/GreenBlackSky/coin_web_client/blob/master/lib/session.dart
Maybe, using flask_login was not such a good idea after all..
Have you tried with the request_loader approach? You can log in from Flutter client using a url argument and the Authorization header. Quoting from the documentation,
For example, to support login from both a url argument and from Basic Auth using the Authorization header:
#login_manager.request_loader
def load_user_from_request(request):
# first, try to login using the api_key url arg
api_key = request.args.get('api_key')
if api_key:
user = User.query.filter_by(api_key=api_key).first()
if user:
return user
# next, try to login using Basic Auth
api_key = request.headers.get('Authorization')
if api_key:
api_key = api_key.replace('Basic ', '', 1)
try:
api_key = base64.b64decode(api_key)
except TypeError:
pass
user = User.query.filter_by(api_key=api_key).first()
if user:
return user
# finally, return None if both methods did not login the user
return None
If you don't want to use flask_login anymore, I would suggest flask_jwt_extended for your case. Note that authentication will be carried out using JWT tokens instead of sessions.
Basically, you would need to create three routes: one for creating access and refresh tokens when the user logged in, one for refreshing the expired access token with the refresh token and one for removing the tokens when the user logged out. Then you would protect your API endpoints with the #jwt_required decorators.
Please refer to the documentation for detailed implementation.
I'm using example Azure AD Open ID connect authentication code to authenticate my ASP.Net Core web application.
The standard authentication flow works flawlessly but when I try to acquire a JWT through the token acquisition service the call to GetAccountAsync and then GetAccountsAsync fail to return an account.
The crazy part is that it does not always fail.
In Edge, if the credentials are cached and I do not log in GetAccountAsync fails to return an account. If credentials are not cached and I log in, GetAccountAsync returns an account.
What would cause this?
I noticed that when the credentials are cached the OnAuthorizationCodeReceived is not received and the account is added to the cache here.
var handler = options.Events.OnAuthorizationCodeReceived;
options.Events.OnAuthorizationCodeReceived = async context =>
{
var _tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>();
await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, options.Scope);
await handler(context);
};
How would these credentials be added to the cache without going through the login as happens when they are cached in the browser?