I added Shiro session management (based on Kim's and Leif's webinar) to the Vaadin quick ticket dashboard demo application. When I do a browser reload in the application I get thrown back to the login page with no session. How / where can I prevent this.
I have a standard shiro.ini setup
Login button handler:
signin.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
boolean loginOK = false;
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager((org.apache.shiro.mgt.SecurityManager)
securityManager);
Subject currentUser = SecurityUtils.getSubject();
//collect user principals and credentials in a gui specific manner
//such as username/password html form, X509 certificate, OpenID, etc.
//We'll use the username/password example here since it is the most common.
UsernamePasswordToken token =
new UsernamePasswordToken(username.getValue(), password.getValue());
//this is all you have to do to support 'remember me' (no config - built in!):
token.setRememberMe(true);
//currentUser.login(token);
try {
logger.log(Level.INFO, "trying login");
currentUser.login( token );
logger.log(Level.INFO, "login done");
//if no exception, that's it, we're done!
} catch ( Exception e ) {
logger.log(Level.INFO, "exception");
}
if ( currentUser.hasRole( "schwartz" ) ) {
loginOK = true;
} else {
loginOK = false;
}
if (loginOK) {
signin.removeShortcutListener(enter);
buildMainView();
} else {
if (loginPanel.getComponentCount() > 2) {
// Remove the previous error message
loginPanel.removeComponent(loginPanel.getComponent(2));
}
// Add new error message
Label error = new Label(
"Wrong username or password. <span>Hint: try empty values</span>",
ContentMode.HTML);
error.addStyleName("error");
error.setSizeUndefined();
error.addStyleName("light");
// Add animation
error.addStyleName("v-animate-reveal");
loginPanel.addComponent(error);
username.focus();
}
}
});
Use #preserveonRefresh annotation in UI init class
I recommend using Shiro's web filter. This way, your session will not be lost and you can prohibit unauthorized actions (e.g. Instantiating view objects) easily since Shiro's context is already set up when you display the login or any other view.
Related
I'm implementing apple-authentication in react native using expo-apple-authentication package.
Below is the code which I'm calling on button's onPress.
async handleSocialLogin() {
const { mutate, BB, onSuccess, navigation } = this.props;
try {
const result = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
Alert.alert(JSON.stringify(result))
// signed in
} catch (e) {
Alert.alert(e)
if (e.code === 'ERR_CANCELED') {
// handle that the user canceled the sign-in flow
} else {
// handle other errors
}
}
}
It should return me authentication-token, Full_Name and Email which I requested in scope but It is giving me null for Full_Name and Email.
As per the documentation:
requestedScopes (AppleAuthenticationScope[]) (optional) - Array of user information scopes to which your app is requesting access. Note that the user can choose to deny your app access to any scope at the time of logging in. You will still need to handle null values for any scopes you request. Additionally, note that the requested scopes will only be provided to you the first time each user signs into your app; in subsequent requests they will be null.
You have probably already logged in once and didn't catch the logs. Subsequent log in will result in this data being null
I'm making Android app with Xamarin, This use zxing.
When user click a button, It show QrScan page and dialog for asking allow camera permission.
I want to show dialog asking permission by user allow permission every time clicked button.
Now, If user click deny, permission dialog ever don't shown, before restart application.
Have you any idea?
This is my source.
Android --- MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
ZXing.Net.Mobile.Forms.Android.Platform.Init();
LoadApplication(new App { OSVersion = "Android Version " + "2.0" });
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
// If this is not be, occur unexpected exception when user click deny
if(grantResults[0] == Permission.Denied)
{
return;
}
global::ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
And this is my Executing QrScan Method In PCL project which called clicked button.
public async void ImgQrScan_Clicked(object sender, EventArgs e)
{
this.TappedEvent?.Invoke(sender, e);
CustomScanViewMaker();
await Navigation.PushModalAsync(oCustomQrScanPage);
zxingPage.IsScanning = true;
string sScanResult = "";
zxingPage.OnScanResult += (result) =>
{
sScanResult = result.Text;
zxingPage.IsScanning = false;
Device.BeginInvokeOnMainThread(async () =>
{
this.OnClicked?.Invoke(sender, new QrScannerClickEventArgs(sScanResult));
await Navigation.PopModalAsync();
});
};
this.OnClicked?.Invoke(sender, new QrScannerClickEventArgs(sScanResult));
}
Thank you.
I want to show dialog asking permission by user allow permission every time clicked button.
You could try using shouldShowRequestPermissionRationale method to implement this feature, as the document said :
To help find situations where the user might need an explanation, Android provides a utiltity method, shouldShowRequestPermissionRationale(). This method returns true if the app has requested this permission previously and the user denied the request.
For its usage, you could refer to the official document Requesting Permissions at Run Time, in C#, it's something like this :
// Here, this is the current activity
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) != Permission.Granted)
{
// Should we show an explanation?
if (ActivityCompat.ShouldShowRequestPermissionRationale(this, Manifest.Permission.Camera))
{
// Provide an additional rationale to the user if the permission was not granted
// and the user would benefit from additional context for the use of the permission.
// For example if the user has previously denied the permission.
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
Log.Info(TAG, "Displaying camera permission rationale to provide additional context.");
}
else
{
// No explanation needed, we can request the permission.
ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.Camera }, REQUEST_CAMERA);
// REQUEST_CAMERA is an app-defined int constant. The callback method gets the
// result of the request.
}
}
else
{
System.Diagnostics.Debug.WriteLine("Permission Granted!!!");
}
I have started playing with OpenID Connect server with ASOS by implementing the resource owner password credential grant. however when I test it using postman, I am getting generic 500 internal server error.
Here is my code for your debugging pleasure. I appreciate your feedback.
Thanks
-Biruk
here is my Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddAuthentication(options => {
options.SignInScheme = "ServerCookie";
});
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(30);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, LoggerFactory loggerFactory)
{
app.UseOAuthValidation();
app.UseOpenIdConnectServer(options => {
// Create your own authorization provider by subclassing
// the OpenIdConnectServerProvider base class.
options.Provider = new AuthorizationProvider();
// Enable the authorization and token endpoints.
// options.AuthorizationEndpointPath = "/connect/authorize";
options.TokenEndpointPath = "/connect/token";
// During development, you can set AllowInsecureHttp
// to true to disable the HTTPS requirement.
options.ApplicationCanDisplayErrors = true;
options.AllowInsecureHttp = true;
// Note: uncomment this line to issue JWT tokens.
// options.AccessTokenHandler = new JwtSecurityTokenHandler();
});
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseMvc();
}
and here is my AuthorizationProvider.cs
public sealed class AuthorizationProvider : OpenIdConnectServerProvider
{
public Task<User> GetUser()
{
return Task.Run(()=> new User { UserName = "biruk60", Password = "adminUser123" });
}
// Implement OnValidateAuthorizationRequest to support interactive flows (code/implicit/hybrid).
public override Task ValidateTokenRequest(ValidateTokenRequestContext context)
{
// Reject the token request that don't use grant_type=password or grant_type=refresh_token.
if (!context.Request.IsPasswordGrantType() && !context.Request.IsRefreshTokenGrantType())
{
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
description: "Only resource owner password credentials and refresh token " +
"are accepted by this authorization server");
return Task.FromResult(0);
}
// Since there's only one application and since it's a public client
// (i.e a client that cannot keep its credentials private), call Skip()
// to inform the server the request should be accepted without
// enforcing client authentication.
context.Skip();
return Task.FromResult(0);
}
public override async Task HandleTokenRequest(HandleTokenRequestContext context)
{
//// Resolve ASP.NET Core Identity's user manager from the DI container.
//var manager = context.HttpContext.RequestServices.GetRequiredService<UserManager<ApplicationUser>>();
// Only handle grant_type=password requests and let ASOS
// process grant_type=refresh_token requests automatically.
if (context.Request.IsPasswordGrantType())
{
// var user = await manager.FindByNameAsync(context.Request.Username);
var user = await GetUser();//new { userName = "briuk60#gmail.com", password = "adminUser123" };
if (user == null)
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "Invalid credentials.");
return;
}
if (user != null && (user.Password == context.Request.Password))
{
var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);
// Note: the name identifier is always included in both identity and
// access tokens, even if an explicit destination is not specified.
// identity.AddClaim(ClaimTypes.NameIdentifier, await manager.GetUserId(user));
// When adding custom claims, you MUST specify one or more destinations.
// Read "part 7" for more information about custom claims and scopes.
identity.AddClaim("username", "biruk60",
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
// Create a new authentication ticket holding the user identity.
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
context.Options.AuthenticationScheme);
// Set the list of scopes granted to the client application.
ticket.SetScopes(
/* openid: */ OpenIdConnectConstants.Scopes.OpenId,
/* email: */ OpenIdConnectConstants.Scopes.Email,
/* profile: */ OpenIdConnectConstants.Scopes.Profile);
// Set the resource servers the access token should be issued for.
// ticket.SetResources("resource_server");
context.Validate(ticket);
}
}
}
}
What am i doing wrong. I can put it in debug mode and step through it without any error it just 500 internal Server Error in fiddler and postman.
Here's the exception you're likely seeing:
System.InvalidOperationException: A unique identifier cannot be found to generate a 'sub' claim: make sure to add a 'ClaimTypes.NameIdentifier' claim.
Add a ClaimTypes.NameIdentifier claim and it should work.
I am using OAuth plugin for my Grails project for user to log in to my page. I am integrating facebook,google,and linkedIn to my web app. The OAuth plugin uses springSecurityOAuth plugin and respective OAuth plugins for facebook, google and linkedIn.
But the plugin is only fetching the userId from the social sites while I need to extract other profile info like firstname, lastname email etc. How can I achieve this?
I have already got required permissions for email and public_profile from facebook.
UPDATE:: I manually wrote code to get info such as firstname, lastname etc from providers. I am getting the required data from google but not from facebook. Am I doing any wrong here?
PS: I copied the same code from SpringSecurityOAuthService to get the info and made two for respective providers as shown:
def getUserDetailsGoogle(googleAccessToken){
if (provider=='google'){
def response = oauthService.getGoogleResource(googleAccessToken, 'https://www.googleapis.com/oauth2/v1/userinfo')
def googleResponse
try {
googleResponse = JSON.parse(response.body)
} catch (Exception e) {
log.error "Error parsing response from Google. Response:\n${response.body}"
throw new OAuthLoginException('Error parsing response from Google', e)
}
return googleResponse
}
}
def getUserDetailsFacebook(facebookAccessToken){
def response = oauthService.getFacebookResource(accessToken, 'https://graph.facebook.com/me')
def user
try {
facebookResponse = JSON.parse(response.getBody())
} catch (Exception e) {
log.error "Error parsing response from Facebook. Response:\n${response.body}"
throw new OAuthLoginException("Error parsing response from Facebook", e)
}
if (! facebookResponse?.id) {
log.error "No user id from Facebook. Response:\n${response.body}"
throw new OAuthLoginException("No user id from Facebook")
}
return facebookResponse
}
In my Grails 2.5.X app I use pac4j for authenticating with Facebook, Google, etc. by adding these dependencies to BuildConfig.groovy
dependencies {
compile 'org.pac4j:pac4j-core:1.6.0',
compile 'org.pac4j:pac4j-oauth:1.6.0'
}
The relevant controller class is shown below. If you want to look at the source of the OauthService it calls (or anything else), check out the GitHub repository I've linked to.
#Secured(['permitAll'])
class OauthController {
OauthService oauthService
GrailsApplication grailsApplication
SpringSecurityService springSecurityService
UserRegistrationService userRegistrationService
/**
* Starts the OAuth authentication flow, redirecting to the provider's Login URL. An optional callback parameter
* allows the frontend application to define the frontend callback URL on demand.
*/
def authenticate(String provider) {
BaseOAuthClient client = oauthService.getClient(provider)
WebContext context = new J2EContext(request, response)
RedirectAction redirectAction = client.getRedirectAction(context, true, false)
log.debug "Redirecting to ${redirectAction.location}"
redirect url: redirectAction.location
}
/**
* Handles the OAuth provider callback.
*/
def callback(String provider, String error) {
WebContext context = new J2EContext(request, response)
if (!error) {
try {
CommonProfile profile = oauthService.getUserProfile(provider, context)
User registeredUser = userRegistrationService.socialSignIn(profile, provider)
if (!registeredUser.isAttached()) {
// User is trying to register with an OAuth provider (e.g. Twitter, Yahoo), that doesn't provide their
// email address so they need to submit a form to supply us with their email
render view: '/register/confirmEmail', model: [user: registeredUser]
return
}
springSecurityService.reauthenticate(registeredUser.username)
flashHelper.info 'social.login.success': provider
redirect uri: '/'
return
} catch (ex) {
log.error "Error occurred during callback from OAuth2 provider '$provider'", ex
}
} else {
// Most likely explanation is that the user denied access on the consent screen which is not really an error
log.warn "Callback from OAuth2 provider '$provider' failed due to error: $error"
}
flashHelper.warn 'social.login.fail'
redirect uri: '/'
}
}
I am using Asp.Net with MVC 4 to build a web application. For authentication, I am using forms authentication. The login page is set correctly and login behaves properly. However, instead of using the default partial login view I am using my own and I use AJAX to log in.
The login controller works fine and here is the code for login.
Here is my code in login action. Here resp is my custom response object
resp.Status = true;
// sometimes used to persist user roles
string userData = "some user data";
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // ticket version
login.username, // authenticated username
DateTime.Now, // issueDate
DateTime.Now.AddMinutes(30), // expiryDate
false, // true to persist across browser sessions
userData, // can be used to store additional user data
FormsAuthentication.FormsCookiePath); // the path for the cookie
// Encrypt the ticket using the machine key
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
// Add the cookie to the request to save it
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
cookie.HttpOnly = true;
//Response.Cookies.Add(cookie);
Response.SetCookie(cookie);
return Json(resp);
Here is the code of cshtml page to handle this script response
function (respData) {
if (respData.Status) {
window.location.href = "/";
}
if (!respData.Status) {
if (respData.Errors[0].ErrorCode == 1) {
$('#invalid').show();
$('#username').val('');
$('#password').val('');
}
else if (respData.Errors[0].ErrorCode == -1) {
var msg = respData.Errors[0].ErrorDescription;
$('#error_email').text(msg);
}
else {
var msg = respData.Errors[0].ErrorDescription;
$('#error_pwd').text(msg);
}
}
$("#dialog").dialog("close");
},
Everything works fine and the user is successfully redirected to home page on successful login. Also gets a proper message on failure.
The problem is, when I browse any other page after this successful redirection, the subsequent requests are not authenticated.
I did a little bit research and found that the browser is not sending the forms authentication cookie in the subsequent requests and hence those requests are not authenticated.
Any idea on this behavior ? , Am I missing something ?
Try explicitly setting the expiry time on your cookie with:
Cookie.Expires(DateTime.Now.AddMinutes(30));