I'm developing a Blazor Server app using .NET 6.0. I would like for the index page to utilize a route parameter:
#page "/{vendorId}"
#functions {
[Parameter]
public string? vendorId { get; set; }
}
So, a user navigating to https://localhost:7071/Chesters will be retrieving data for Chesters, while a user navigating to https://localhost:7071/SmokeyJoe will be retrieving data for SmokeyJoe. The problem is when the user first navigates to that URL, he must first login to be authenticated. Once he's authenticated, the page is redirected back to https://localhost:7071 where I lose the route parameter. If the authenticated user then navigates to his intended URL, everything works as expected. So, how do I keep the authentication process from wiping out the initial navigation to the preferred URL?
Related
I have set up a web application with Keycloak in my local machine. Since Im using Keycloak as SSO implementation, I want in my web app that whenever SIGNUP button is click, user is directed into the registration page, and not going through the LOGIN page.
This is the example URL directed to the registration form, however, it contains a tab_id that is generated randomly like a session id.
https://site.test/auth/realms/custom/login-actions/authenticate?client_id=test&tab_id=qIdW92Bvwmk
I read about this link
Yes, as long as you use the "registrations" instead of "auth" in the
end of login ( AuthorizationEndpoint ) URL
But my endpoint in https://site.test/auth/realms/custom/.well-known/openid-configuration cannot be modified.
You can change the button link to this format -
http://<domain.com>/auth/realms/<realm-name>/protocol/openid-connect/registrations?client_id=<client_id>&response_type=code&scope=openid email&redirect_uri=http://<domain.com>/<redirect-path>&kc_locale=<two-digit-lang-code>
The registration page is exposed via an openid-connect endpoint, accessible in the same way as the standard auth screen. To construct the correct URL you can simply replace openid-connect/auth in the URL with openid-connect/registrations from the .well-known auth endpoint.
authEndpoint.replace("openid-connect/auth","openid-connect/registrations");
Using this endpoint the user will be directed to the registration screen instead of the login screen.
It is not documented or exposed via .well-known/openid-configuration, but you can see it in the source code:
public static UriBuilder registrationsUrl(UriBuilder baseUriBuilder) {
UriBuilder uriBuilder = tokenServiceBaseUrl(baseUriBuilder);
return uriBuilder.path(OIDCLoginProtocolService.class, "registrations");
}
For Keycloak 17 this worked for me:
http://<mykeycloakdomain.com>/realms//protocol/openid-connect/registrations?client_id=<myclient_id>&response_type=code&scope=openid+email&redirect_uri=https%3A%2F%2Fmywebsiteurl.com&kc_locale=
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.
I have a problem with one Laravel plugin
its called Socialite
it allows my site users to loggin with facebook, twitter etc
i need some help
the problem is that after login it redirects to index page and the page visitor initiated login
ogin url /login?redirect=https://www...
socialite doesnt take this GET parameter
default laravel login works with this parameter, but not socialite
i think it doesnt support redirect, because callback URL is set static in config
I cannot figure out how to modify it to take GET paramenter with customs URLs
class SocialAuthController extends Controller {
public function redirect() {
return Socialite::driver('facebook')->redirect();
}
public function callback(SocialAccountService $service) {
$user = $service->createOrGetUser(Socialite::driver('facebook')->user()); auth()->login($user);
return redirect()->to('/home');
}
}
How can i put a dynamic link to this intead of /home SO that it redirects me to the same page where i was before log-in.
redirect()->to('/home')
Laravel Socialite plugin after login with Facebook or Twitter redirects to:
SocialAuthController.php
...
public function callback(SocialAccountService $service, $provider)
....
return redirect()->to('/');
I want to redirect visitor to page where login was initiated and not static page '/'
My login page receives GET parameter /login?redirect=http://www.website.com/dynamic_url
Please make code changes that Socialite plugin redirects after login to dynamically set redirect URL.
It is really easy.I search it by myself.No one replied me so i think it was really embarrisng.
So the solution is return Redirect::to(URL::previous());
A more easy way to do that is just:
return redirect()->back();
Happy coding! :D
I am using windows basic authentication, if user is verified home controller is displayed. Only user those who belong to "Admin" group are allowed to access this site. so far all works well. I am using below code. I want to log message in database if user tries to login and does not belong to "Admin" group. Any suggestion how to achieve that?
[Authorize(Roles = "Admin")]
public ActionResult Home()
{
..............
return View();
}
Thanks,
You have 2 options, write a custom authorize attribute which logs on failure or create an HTTPModule which detects 401's and logs there. Personally i prefer the custom authorize attribute approach
I am making a website and I want the user to register and validate fields before sending to the data access layer.
The page contains 2 forms, login and register forms.
When the user press on register button the url of the page become /uers/register but if the user refreshed the page the browser ask him if he want to repost or not.
I want to implement it as if the user refreshed the page it will not ask for repost.
To make things more clear by example: go to stackoverflow.com login page and press Login with empty openid, it will show error message but when refresh the page it will not ask for repost.
How stackoverflow doing this while the form is post not get?
This is called the redirect after post pattern and in ASP.NET MVC it could look like this:
public ActionResult Index()
{
var model = new SomeViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(SomeViewModel model)
{
if (!ModelState.IsValid)
{
// validation failed => redisplay the form
return View(model);
}
// Validation passed => update database and redirect
return RedirectToAction("Index");
}