Blazor Server Get User from Controller (Auth0) - server

I have based the issue from this Template from here: https://github.com/auth0-blog/blazor-quiz-manager
Which simply connects to Auth0 to authenticate users, which works perfectly fine. I can get the User info fine from the Blazor page itself perfectly fine.
However i need to get the User info from the Controller (without having to post userinfo), so i can use the logged in user info to update a db record (getting userID etc)
There are a number of people having this same issue, but never seems to be a resolution (here for example is one https://github.com/dotnet/aspnetcore/issues/17442 )
If i add to my startup.cs:
services.AddSingleton<AuthenticationStateProvider, ServerAuthenticationStateProvider>();
I can access the user from my controller :
private readonly AuthenticationStateProvider auth;
private readonly IHttpContextAccessor _currentContext;
public UserController(IHttpContextAccessor currentContext, AuthenticationStateProvider auth)
{
_currentContext = currentContext;
this.auth = auth;
}
var authState = await auth.GetAuthenticationStateAsync();
var user = authState.User;
Of course the issue is as it is a Singleton, i am having issues with users sharing the browser refresh. and it should be :
services.AddScoped<AuthenticationStateProvider, ServerAuthenticationStateProvider>()
However if i use AddScoped, i get the error:
'GetAuthenticationStateAsync was called before SetAuthenticationState.'
and all hope is lost :(
Any pointers ???

Related

Handle session time out with Wicket

I'm working on a wicket legacy-project and i'm trying to fix a bug with the session time-out.
Basically I'd like to have a redirect to a customed error page after session times out.
This is what I did:
web.xml :
<session-config>
<session-timeout>1</session-timeout>
</session-config>
in the application class:
#Override
public void init() {
super.init();
getApplicationSettings().setPageExpiredErrorPage(ErrorMessagePage.class);
This is not working. I mean after session time out, nothing happens.
What am I doing wrong?
EDIT 04.05.20
Based on the feedback from Martin I tried to implement a session validaty checker:
public class SessionValidityChecker implements IRequestCycleListener {
#Override
public void onBeginRequest(RequestCycle cycle) {
HttpServletRequest request = (HttpServletRequest) cycle.getRequest().getContainerRequest();
boolean sessionValid = request.isRequestedSessionIdValid();
if (!sessionValid) {
cycle.setResponsePage(SessionExpiredPage.class);
}
}
}
and in Application.class
public void init() {
super.init();
getRequestCycleListeners().add(new SessionValidityChecker());
}
Also what I may should have specified in my first post is that I use the wicket SignInPanel for authentification. After timeout, I'd like the user to be logged out and redirected to a specific page.
This is what I've tried with the above code, but after session time out, no redirect happens. Even worst, the user is still signed in. What am I missing?
You are mistaking page expiration with session expiration.
Stateful pages are stored in a PageStore (disk) and the store may grow up to some predefined size. Once this size is reached the oldest page is removed to make room for the newest one.
If your user uses the browser Back button many times at some point Wicket will throw PageExpiredException for the deleted page.
In your case when the session expires usually the web server (e.g. Tomcat) will just create a new one. If your application has authentication enabled then it will detect that there is no authenticated user in the new http session and most probably will redirect the user to the login page.
If there is no authentication in place then Wicket will create a new instance of the requested page and render it. You can change this by changing PageSettings#recreateBookmarkablePagesAfterExpiry to false
(see https://github.com/apache/wicket/blob/79f63f66eb588a5d69e9feff7066f1244f61f387/wicket-core/src/main/java/org/apache/wicket/settings/PageSettings.java#L46)
You may use javax/servlet/http/HttpServletRequest.html#isRequestedSessionIdValid() method to find whether the the request came with an expired JSESSIONID cookie/url. If it is false then the web server just created a new HttpSession. You can do the check in Wicket's IRequestCycleListener#onBeginRequest()

IdentityServer SSO - Trusted application

I need to SSO (single sign on) a user coming from an application of mine (identity provider using ASPNET Session State) and redirect them to another application of mine (service provider) that is configured to use implicit flow with IdentityServer4. I need to achieve this without requiring the user to log back in and without providing the user's password.
My initial thought was that I could use a client secret for the identity provider to redirect the user to the IdentityServer4 authentication end point with the access token as a query parameter and then use a custom validator or extension grant to issue an identity token for use with the service provider application without needing to also provide the user's password.
I've managed to issue an access token to the identity provider and then redirect the user to IdentityServer4, but issuing an identity token has proven difficult for me. I've poured over the samples and documentation and I'm confused to say the least.
I'm looking for direction on the appropriate approach to this scenario and perhaps a comprehensive example in C#. I've come to understand I can use a hybrid flow to issue an access token as well as an identity token. I think my biggest struggle is how to redirect the user and, based on the access token, issue the user an identity token (and if this is even an acceptable approach).
Simply put: I'd like to redirect the user from Application A to IdentityServer4 to Application B based on trust with the identity provider (via client secret?).
Note: I understand this could be considered an opinion-based question, but based on my research I believe there is one single best practice and that's what I'm asking for.
I managed to get this working by the following flow:
Authorize the user in Application A (Identity Provider)
Obtain Access Token from Identity Server 4 via Token Endpoint and shared secret.
Add access token as a query string parameter since headers are not preserved on redirect.
Redirect the user to an Account controller method that accepts identifying information such as username. This method is protected by a custom middleware class that checks the query string for an access token parameter. If the token exists, it is added to the authentication header; this authorizes the user to hit this controller method.
The controller method will then sign the user in and redirect them to the /connect/authorize/login endpoint.
Finally, the login endpoint sets the cookie and redirects the user to Application B (Service Provider), whose URL is specified via the redirect_uri query parameter.
Configuration for shared secret:
Add appropriate grant type, secret and new scope name to the client. The new scope will help in debugging Access token issues in your logs (especially if you have multiple applications hitting your ID4 server). Also make sure to add the Service Provider's URL to the client RedirectUris, otherwise you'll receive an "invalid redirect" error.
AllowedGrantTypes = new List<string> { GrantType.Implicit, GrantType.ClientCredentials },
ClientSecrets = new List<Secret> {
new Secret(_clientSecrets.ExternalIdpSecret.Sha256(), clientID)
},
AllowedScopes = new List<string>
{
"newScopeName"
},
RedirectUris = new List<string>
{
$"http://localhost:<portnumber>"
}
Next, add your custom middleware.
public class QueryStringOAuthBearerMiddleware
{
private readonly RequestDelegate next;
public QueryStringOAuthBearerMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
this.BeginInvoke(context);
await this.next.Invoke(context);
this.EndInvoke(context);
}
private void BeginInvoke(HttpContext context)
{
if (context.Request.Query.ContainsKey("accesstokenparametername"))
{
var accessToken = context.Request.Query.First(p => p.Key == "accesstokenparametername");
if (!string.IsNullOrEmpty(accessToken.Value))
{
context.Request.Headers.Add("Authorization", "Bearer " + accessToken.Value);
}
}
}
private void EndInvoke(HttpContext context)
{
}
}
And add the middleware to your configuration.
app.UseMiddleware<QueryStringOAuthBearerMiddleware>();
Create your login method.
[HttpGet]
[Authorize]
public async Task<IActionResult> Login2(string userName, string returnURL)
{
await _httpContextWrapper.SignInAsync(userName);
return Redirect(returnURL);
}
Configuration for Client application (IDP):
Your client side code should look like this:
var disco = await DiscoveryClient.GetAsync("http://localhost:<portnumber>");
var tokenClient = new TokenClient(disco.TokenEndpoint, "clientIdentifier", "IUsedAGuidHere");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("newScopeName");
var redirectURL = string.Format("http://localhost:2228/account/Login2?userName=<UserIDValue>&returnURL={1}&accesstokenparametername={0}",
tokenResponse.AccessToken,
Server.UrlEncode(
string.Format("/connect/authorize/login?client_id={3}&redirect_uri={2}&response_type=id_token%20token&scope=<ImplicitFlowScopes>&state={0}&nonce={1}",
CryptoRandom.CreateUniqueId(),
CryptoRandom.CreateUniqueId(),
Server.UrlEncode("http://localhost:<PortNumber>"),
"ClientIdentifier")));
Response.Redirect(redirectURL, false);
Note: Please understand you won't be able to take this code AS-IS and make it work. I've heavily modified it to protect the security of my resources.
I think I might take care of the Authentication with Application A first, then forward on to the next app...
Application A --> IdentityServer --> Application A --> Application B.
You could include some custom parameters in your returnUrl which Application A could read upon return from IdentityServer that would trigger the redirect to Application B.

Facebook OAuth stopped working suddenly

I noticed yesterday that my Facebook login for my website has stopped working.
This has been working great for the last 2 months, as far as I am aware I have not changed anything. I have tried everything I can on links such as: - as well as many more...
ASP.NET MVC5 OWIN Facebook authentication suddenly not working
I have noticed that the Stack Overflow Facebook auth has also stopped working.
Has anyone else noticed this and found any solution? It's worth noting I am using azure app services to host. But this issue is also found when I am using localhost.
My current setup looks like this...
in Startup.Auth.cs
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
AppId = "xxxxxxxxxxxxx",
AppSecret = "xxxxxxxxxxxx"
};
facebookOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookOptions);
In the following method, loginInfo is null every time.
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
I also added a session "WAKEUP" from a different post suggestion, fb auth failed once before and this fixed the issue this time, but it has come back.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
Session["WAKEUP"] = "NOW!";
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
As RockSheep explained. Facebook dropped the support vor API v2.2. You need to update your OWIN nuget packages.
You can find the issue on github (from the Katanaproject).
Ensure to activate pre releases in your nuget manager, than you are able to update the nuget packages to version v3.1.0-rc1. But beware: After the update, you need to test your login carefully (maybe you also have other authentication providers like Microsoft or Google, you should test them as well).
Technical
The Api changed the version number to v2.8 and the return value from the API is now in JSON-Format and no longer escaped in the URI. The 'old' OWIN packages can not handle this changes.
[Oauth Access Token] Format - The response format of
https://www.facebook.com/v2.3/oauth/access_token returned when you
exchange a code for an access_token now return valid JSON instead of
being URL encoded. The new format of this response is {"access_token":
{TOKEN}, "token_type":{TYPE}, "expires_in":{TIME}}. We made this
update to be compliant with section 5.1 of RFC 6749.
Here you can find the code-changes on GitHub for further informations and better understanding.
A lot of people started having trouble after yesterday. This is due to Facebook dropping support for v2.2 of their API. For some reason their system still redirects auth calls that don't use a version number to the 2.2 API. A quickfix is to ensure that the API version gets sent with the API call.
Starting at v2.3 Facebook also started returning JSON objects. So make sure to change that in the code as well.
I had the same issue, found solution here Fix facebook oauth 2017
Basically, you need to extend HttpClientHandler and decode JSON response instead of body
Here is a solution for those who are using scribe java.
public Token extract(String response)
{
Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string");
JSONObject obj = new JSONObject(response);
return new Token(obj.get("access_token").toString(), EMPTY_SECRET, response);
}
Create a new class and set the extractor to JSON.
import org.scribe.builder.api.DefaultApi20;
import org.scribe.extractors.AccessTokenExtractor;
import org.scribe.extractors.JsonTokenExtractor;
import org.scribe.model.OAuthConfig;
public class FaceFmApi extends DefaultApi20 {
#Override
public String getAccessTokenEndpoint()
{
return "https://graph.facebook.com/oauth/access_token";
}
#Override
public AccessTokenExtractor getAccessTokenExtractor()
{
return new JsonTokenExtractor();
}
#Override
public String getAuthorizationUrl(OAuthConfig config) {
return null;
}
}
and inject your newly created class as below. Then getAccessToken() will work as expected.
public OAuthService getService() {
return new ServiceBuilder().provider(FaceFmApi.class)
.apiKey(config.getApiKey()).apiSecret(config.getApiSecret())
.callback(config.getCallback()).build();
}

How can I use IdentityServer3 External Authentication to login a Local User?

Having worked through the IdentityServer3 MVC tutorial here I have a working sample application where I can log in as 'bob', or I can log in with any google account.
What I am trying to work out is, say bob has an additional field 'GoogleId':
public static class Users
{
public static List<InMemoryUser> Get()
{
new InMemoryUser
{
Username = "bob",
Password = "secret",
Subject = "1",
GoogleId = "60a71ff098f6509cbd4fbda2f495eacb",
Claims = new[]
{
new Claim(Constants.ClaimTypes.GivenName, "Bob"),
new Claim(Constants.ClaimTypes.FamilyName, "Smith"),
new Claim(Constants.ClaimTypes.Role, "Geek"),
new Claim(Constants.ClaimTypes.Role, "Foo")
}
}
}
}
When I log in with Google, if the incoming subject claim matches a User's GoogleId, log in with the local user account.
The IdentityServer3.AspNetIdentity project must be doing this as the Asp.Net Identity has external logins keyed off local user accounts. However I can't use the Asp.Net Identity user model as I'm working in a legacy application. I can't understand the code there well enough to apply it to my situation.
Is there a sample or tutorial somewhere that demonstrates this scenario?
You will want to implement the AuthenticateExternalAsync method in the UserService. You can use the ExternalIdentity provided to find the local user by your own mechanism and then set an AuthenticateResult.
Here is a good example of a custom UserService authenticating externally.

Check if facebook user is authenticated

I am building a helper class that would allow me to manage facebook account in a windows form application. I am using Facebook C# SDK. As it is suggested in its documentation, to know if the user is authenticated one would get the loginUrl
var loginUrl = oauth.GetLoginUrl(parameters);
and then afterward, navigate to that Url
webBrowser.Navigate(loginUrl);
Since I am on the back end of the application, I wonder how one can write a helper class that will return true or false to show if the user is authenticated or not. I would love to do something like:
public static bool IsUserAunthenticated (string appId, string[] extendedPermissions)
How can this function be written? Any ideas? Remember I am using windows form on .net 4.0
assuming this function is called some time after
webBrowser.Navigate(loginURL)
A solution like this could work:
assuming:
The user has already authorized your application
'facebookClient' is a static class field is already initialized
public static bool isUserAuthenticated() {
try
{
facebookClient.Get("/me");
//the lack of an exception thrown is a sign of success
return true;
}
catch (FacebookOAuthException e)
{
//your access token used to initialize 'facebookClient' is invalid
return false;
} }
Some general notes:
you didn't show any code to handle the retrieval of the actual access
token. I've been assuming you left that out for brevity.
passing in 'appID' and 'extendedPermissions' means that this method
would be used to REGISTER the user, not for testing to see if they
were already a user of your application.
related to the above point, you could pass in the access token as an
argument so you would be able to initialize 'facebookClient' inside
the method and run the authentication test.
To sum that last little bit up, if you're not already aware, you need to realize there are two distinct stages: getting the access token and using the access token.