Cannot pass idpHint option to Keycloak - keycloak

I am using IDP provider for authentication and trying to bypass the standard keycloak login screen (so I need to go immediately to the IDP specific authorization screen). According to this documentation https://keycloak.gitbooks.io/server-adminstration-guide/content/topics/identity-broker/suggested.html we can simply provide idpHint for this. Though that doesn't work.
let keycloakAuth : any = new Keycloak('keycloak.json');
keycloakAuth.createLoginUrl({idpHint: 'ad-oidc'});
It failed with
Uncaught TypeError: Cannot read property 'redirectUri' of undefined
at Keycloak.kc.createLoginUrl (keycloak-core.js:212)
As far as I understand that's because adapter is not created yet. So probably we need to pass this option sometimes later (but not sure at which phase).
I was able to do this only by hardcoding the idpHint inside of the keycloak-core.js itself temporarily. Looking forward to avoid this.
Thanks in advance.

I solved the problem like this:
let kc = Keycloak('/backend/frontend.json');
let kcLogin = kc.login;
kc.login = (options) => {
options.idpHint = 'some-hint';
kcLogin(options);
};
kc.init({ onLoad: 'login-required' }).success((authenticated) => {
console.log('Logged in!');
}});
This way the very first login attempt already uses the hint. I somehow don't get, why there is no simple way to use the idpHint option, the inceptor is the only way I found to use it, without directly patching the keycloak.js file.
It would be perfect, if the Keycloak constructor just would accept an idpHint option.

You have to initialize the keycloak object first.
You don't need to call createLoginUrl(), you need to call login(options) which by its turn going to call createLoginUrl.
var keycloak = new keycloak(JSON);
keycloak.init().success(function(authenticated){
if(authenticated){
console.log("logged in");
}
else{
keycloak.login({idpHint:'ad-oidc'});
}
}).error(function(){
console.log("failed to initialize");
});
Sorry there might be syntax error but I hope it explains how to do it.

Related

Vapor 4 User Authentication and Authorisation

I've recently started with vapor4 (didn't use any older version) and I'm trying to figure out how to implement user authorization and authentication. While i understand basic concepts, having worked with Laravel before I still can't figure out what to do in vapor.
I extended my User with. Ik there is no pw hashing, this is for testing and basic understanding. We'll ignore that for now.
extension User: ModelAuthenticatable
{
static let usernameKey = \User.$name
static let passwordHashKey = \User.$password
func verify(password: String) throws -> Bool {
return password == self.password
}
}
The problem is i can't find a tutorial how to use this authentication. I just kind of try stuff to get it to work, but to no success. This is in my routes file.
let auth = app.grouped(User.authenticator())
auth.get("sign-in") { req in
"I'm authenticated"
}
My first goal would just be to receive a success or failure answer when trying this route. Ultimately i want to switch to a token based solution but one step at a time.
stuff i read was: https://docs.vapor.codes/4.0/authentication/ and https://theswiftdev.com/all-about-authentication-in-vapor-4/. Anyway i couldn't quit figure it out how to use the described authenticators.
While writing this i finally figured it out. Anyway for people stumbling on this. It's as easy as this:
let auth = app.grouped(User.authenticator(), User.guardMiddleware())
auth.get("sign-in") { req in
"I'm authenticated"
}
Your user-class offers a guardMiddleware by default. You don't have to implement anything else, just use it in your route.

Meteor - Password recovery / Email confirmation dynamic url

Basically, I'm using the accounts-base package on meteor and on meteor startup, I set up what template the server should use for the password recovery mail, email confirmation mail, etc.
For example, in my server/startup.js on meteor startup I do many things like :
Accounts.urls.verifyEmail = function (token) {
return Meteor.absoluteUrl(`verify-email/${token}`);
};
Accounts.emailTemplates.verifyEmail.html = function (user, url) {
return EmailService.render.email_verification(user, url);
};
The problem is that my app is hosted on multiple host names like company1.domain.com, company2.domain.com, company3.domain.com and if a client wants to reset his password from company1.domain.com, the recovery url provided should be company1.domain.com/recovery.
If another client tried to connect on company2.domain.com, then the recovery url should be company2.domain.com.
From my understanding, this is not really achievable because the method used by the Accounts Package is "Meteor.absoluteUrl()", which returns the server ROOT_URL variable (a single one for the server).
On the client-side, I do many things based on the window.location.href but I cannot seem, when trying to reset a password or when trying to confirm an email address, to send this url to the server.
I'm trying to find a way to dynamically generate the url depending on the host where the client is making the request from, but since the url is generated server-side, I cannot find an elegent way to do so. I'm thinking I could probably call a meteor server method right before trying to reset a password or create an account and dynamically set the ROOT_URL variable there, but that seems unsafe and risky because two people could easily try to reset in the same timeframe and potentially screw things up, or people could abuse it.
Isn't there any way to tell the server, from the client side, that the URL I want generated for the current email has to be the client current's location ? I would love to be able to override some functions from the account-base meteor package and achieve something like :
Accounts.urls.verifyEmail = function (token, clientHost) {
return `${clientHost}/verify-email/${token}`;
};
Accounts.emailTemplates.verifyEmail.html = function (user, url) {
return EmailService.render.email_verification(user, url);
};
But I'm not sure if that's possible, I don't have any real experience when it comes to overriding "behind the scene" functionalities from base packages, I like everything about what is happening EXCEPT that the url generated is always the same.
Okay so I managed to find a way to achieve what I was looking for, it's a bit hack-ish, but hey..
Basically, useraccounts has a feature where any hidden input in the register at-form will be added to the user profile. So I add an hidden field to store the user current location.
AccountsTemplates.addField({
_id: 'signup_location',
type: 'hidden',
});
When the template is rendered, I fill in this hidden input with jQuery.
Template.Register.onRendered(() => {
this.$('#at-field-signup_location').val(window.location.href);
});
And then, when I'm actually sending the emailVerification email, I can look up this value if it is available.
Accounts.urls.verifyEmail = function (token) {
return Meteor.absoluteUrl(`verify-email/${token}`);
};
Accounts.emailTemplates.verifyEmail.html = function (user, url) {
const signupLocation = user.profile.signup_location;
if (signupLocation) {
let newUrl = url.substring(url.indexOf('verify-email'));
newUrl = `${signupLocation}/${newUrl}`;
return EmailService.render.email_verification(user, newUrl);
}
return EmailService.render.email_verification(user, url);
};
So this fixes it for the signUp flow, I may use the a similar concept for resetPassword and resendVerificationUrl since the signupLocation is now in the user profile.
You should probably keep an array of every subdomains in your settings and keep the id of the corresponding one in the user profile, so if your domain changes in the future then the reference will still valid and consistent.

Grails withForm, reset token on error?

Currently using grails 2.2.2
I've been trying to implement tokens into my application and have come up with this issue. We try to avoid re-rendering pages because it can be very slow so we return JSON instead. The following is a basic controller call that we use but I'm not sure what I should be doing to reset/get a new token.
public saveThing(ThingCommand cmd) {
Map model = [:]
withForm {
try {
thingService.saveThing(cmd)
model.success = true
} catch (Exception e) {
model.error = true //any validation errors or anything else
// RESET TOKEN HERE/GET NEW TOKEN?
}
}.invalidToken {
model.invalidToken = true
}
render model as JSON
}
From my understanding the token is thrown away once the withForm closure is executed. This causes an issue since I don't actually re-render the form which seems to be the normal way of generating a new token. How could I do this manually or is there an easier way to do this (plugin?)
Thanks!
Form tokens through withForm are not designed to be used with AJAX requests. They are designed to be used with HTML forms and POST requests which re-render the form and generate a new token for the form.
In order to make them work with JSON/AJAX requests you will need to implement your own token generation when you process the request and reject it. A good starting place would be to look at the old tests which test withForm. This should give you an idea on how tokens are created and stored.

Authentication That Doesn't Require Javascript?

I have a Web API app, initialized thusly:
app.UseCookieAuthentication();
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOAuthBearerTokens(OAuthOptions);
app.UseGoogleAuthentication();
For calls to most controllers, it works great. However, it also requires a bit of javascript before client-side service calls are made:
function getSecurityHeaders() {
var accessToken = sessionStorage["accessToken"] || localStorage["accessToken"];
if (accessToken) {
return { "Authorization": "Bearer " + accessToken };
}
return {};
}
The problem is that we have a certain type of controller (one that accesses files) where no javascript can be run during the call. For example, the call might be to:
http://mysite/mycontroller/file/filename.jpg
...where the value is assigned as the src attribute of an img tag. The call works, but Thread.CurrentPrincipal.Identity is unauthenticated with a null name, so there's currently not a way to enforce security.
I'm new to Web API, so it may be a dumb question, but what's the way around this? What switches do I need to flip to not require javascript to add security headers? I was considering trying to find a way to force an authorization header in an IAuthorizationFilter or something, but I'm not even sure that would work.
So I figured out the solution to my problem.
First, I needed to configure the app to use an authentication type of external cookies thusly:
//the line below is the one I needed to change
app.UseCookieAuthentication(AuthenticationType = DefaultAuthenticationTypes.ExternalCookie);
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOAuthBearerTokens(OAuthOptions);
app.UseGoogleAuthentication();
Second, it turned out there was a line of code in my WebApiConfig file that was disabling reading the external cookie:
//this line needed to be removed
//config.SuppressDefaultHostAuthentication();
After that, I could see the external cookie from Google, which passed along an email address I could identify the user with.

lift Session management

I am new to lift. I have been working with MVC model so far and using basic session management model i.e. storing a token in the session and check on each request.
I am trying to do the same with lift, but my session getting expired abruptly. even some time I just logged in and it logged out. I have analysis that whenever I gets log message like this:
INFO - Session ucjrn5flnq9q1ke52z5zixgtt expired
I have searched but I couldn't find any step by step tutor
Sessions are managed by your servlet container. Which one are you using? You should look at the container's documentation.
Do not attempt to use S.get et al to access session bound information. This is just plain dangerous. Do it like this:
class Thing {
object SessionThing extends SessionVar[Box[String]](Empty)
...
def someMethod = {
...
SessionThing.is // returns you a Box[String].
// operates on the session variable if it exists,
// otherwise provides a sensible default
SessionThing.is.map(_.toLowerCase).openOr("default")
...
}
}
You need to understand the snippet and state lifecycles really, as it seems you're not fully understanding how lift's session mechanics work.
I found the solution of the problem. I was using embedded jetty server, where I was using ServletContextHandler to register lift filter. I changed it to WebAppContext and it started working fine.
Puneet