Im trying to combine Java UserAdapter and Adapter-based authentication
I want to get the users list after authenticated, but it fails
On server-side, Adapter-based authentication, AuthenticationAdapter-impl.js
function onAuthRequired(headers, errorMessage) {
errorMessage = errorMessage ? errorMessage : null;
return {
authRequired: true,
errorMessage: errorMessage
};
}
function submitAuthentication(username, password){
if (username==="admin" && password === "admin"){
var userIdentity = {
userId: username,
displayName: username,
attributes: {
foo: "bar"
}
};
WL.Server.setActiveUser("AdapterAuthRealm", userIdentity);
return {
authRequired: false
};
}
return onAuthRequired(null, "Invalid login credentials");
}
function listUsers() {
var resourceRequest = new WLResourceRequest("/adapters/UserAdapter/", WLResourceRequest.GET);
return resourceRequest.send();
}
function onLogout() {
WL.Server.setActiveUser("AdapterAuthRealm", null);
WL.Logger.debug("Logged out");
}
And on client-side,
function listUsers(){
busyIndicator.show();
var invocationData = {
adapter : "AuthenticationAdapter",
procedure: "listUsers",
parameters: []
};
WL.Client.invokeProcedure(invocationData, {
onSuccess: listUsersSuccess,
onFailure: listUsersFailure
});
}
function listUsersSuccess(result){
WL.Logger.debug("Feed retrieve success");
busyIndicator.hide();
WL.Logger.debug(JSON.stringify(result));
if (result.responseJSON.length > 0){
displayUsers(result.responseJSON);
} else {
listUsersFailure();
}
}
function listUsersFailure(result){
WL.Logger.error("Feed retrieve failure");
busyIndicator.hide();
WL.SimpleDialog.show("Banking Application", "Service not available. Try again later.", [
{
text : 'Reload',
handler : WL.Client.reloadApp
},
{
text: 'Close',
handler : function() {}
}
]);
}
It returns onFailure response
WLResourceRequest is a client side API so you CAN NOT use it on an adapter since the adapter runs on the server.
You should update your listUsers function (client side) as follows:
function listUsers(){
busyIndicator.show();
var resourceRequest = new WLResourceRequest("/adapters/UserAdapter/", WLResourceRequest.GET);
resourceRequest.send().then(listUsersSuccess).fail(listUsersFailure);
}
Update
You can protect your Java Adapter methods by using the #OAuthSecurity annotation.
UserAdapter.java
#GET
#Path("/protectePath")
#OAuthSecurity(scope="YourRealm")
public String myProtectedMethod() {
// your code here
return "your-response";
}
Related
Today I create some Login Page with Session to make dynamic menu base on database, the web working fine but if I open specified page like localhost/Student/List before login I throw in to login page and after I'm login the page redirect me into localhost/Home/Index who is the default redirect after login page.
My question is how can i enter the specific address after i login? like using Identity.
I'm Sory I will update my Question.
I'm Using Ajax for my login page, if the result of ajax is true then i will redirect to Home/Index.
That's My Validate Function Login cshtml js
function Validate() {
if ($("#username").val() == null || $("#username").val() == "") {
$("#message").text("User Name Required!");
}
else if ($("#password").val() == null || $("#password").val() == "") {
$("#message").text("Password Required!");
}
else{
$.ajax({
type: "POST",
url: '#Url.Action("SignIn", "Account")',
data: {
username : $('#username').val(),
password : $('#password').val()
},
error: function (result) {
$("#message").text("There is a Problem, Try Again!");
},
success: function (result) {
console.log(result);
if (result.status == true) {
window.location.href = '#Url.Action("Index", "Home")';
}
else {
$("#message").text(result.message);
}
}
});
}
}
nah, you can see the window.location.href.
and then my sign in controller
public async Task<IActionResult> SignIn(LoginModel model)
{
var UserLogin = _dbContext.AspNetUsers.Where(a => a.UserName == model.username).FirstOrDefault();
if(UserLogin != null)
{
if(UserLogin.EmailConfirmed == true)
{
var result = await _signInManager.PasswordSignInAsync(model.username, model.password, lockoutOnFailure: false, isPersistent: false);
if (result.Succeeded)
{
HttpContext.Session.SetString("email", UserLogin.Email);
HttpContext.Session.SetString("username", UserLogin.UserName);
HttpContext.Session.SetString("id", UserLogin.Id);
HttpContext.Session.SetString("roleId", UserLogin.RolesId);
int roleId = Convert.ToInt32(Convert.ToDouble(HttpContext.Session.GetString("roleId")));
List<Menus> menus = _dbContext.LinkRolesMenu.Where(a => a.RolesId == roleId).Select(a => a.Menus).ToList();
DataSet ds = new DataSet();
ds = ToDataSet(menus);
DataTable table = ds.Tables[0];
DataRow[] parentMenus = table.Select("ParentId = 0");
var sb = new StringBuilder();
string menuString = GenerateUL(parentMenus, table, sb);
HttpContext.Session.SetString("menuString", menuString);
HttpContext.Session.SetString("menus", JsonConvert.SerializeObject(menus));
return Json(new { status = true, message = "Login Successfull!" });
}
else
{
return Json(new { status = false, message = "Invalid Password!" });
}
}
else
{
return Json(new { status = false, message = "Email Not Confirmed!" });
}
}
else
{
return Json(new { status = false, message = "Invalid UserName!" });
}
}
nah, that's the return return Json(new { status = true, message = "Login Successfull!" });
how can i return that into specific page
I open specified page like localhost/Student/List before login I throw in to login page
In my opinion, the only point is that you need to pass the current url (Student/List) as parameter to your login page.Refer to my demo using Custom Authorize Filter
1.Custom Authorize Filter: judge whether use has already logged in,if not, redirect to login page
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
public class MyAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
//your judgement to if user has logged in
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
//redirect to Account/Login
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Account",
action = "SignIn",
returnUrl = filterContext.HttpContext.Request.Path.ToUriComponent()
}));
}
}
}
2. StudentController:
[MyAuthorizeAttribute]
public class StudentController: Controller
3.AccountController
public IActionResult SignIn(string returnUrl = null)
{
// receive returnUrl and pass to View
ViewBag.ReturnUrl = returnUrl;
return View();
}
[HttpPost]
public async Task<IActionResult> SignIn(LoginModel model, string returnUrl = null)
{
//logic to login
return Json(new { status = true, message = "Login Successfull!" ,returnUrl = returnUrl });
}
4.SignIn.cshtml:redirect to returnUrl in success callback function
$.ajax({
type: "POST",
url: '#Url.Action("SignIn", "Account")',
data: {
username : $('#username').val(),
password : $('#password').val(),
returnUrl: '#ViewBag.ReturnUrl'
},
error: function (result) {
$("#message").text("There is a Problem, Try Again!");
},
success: function (result) {
console.log(result);
if (result.status == true) {
window.location.href = result.returnUrl;
}
else {
$("#message").text(result.message);
}
}
});
I have created angular service, where I'm registering challengeHandler this way:
azureChallengeHandler = WL.Client.createChallengeHandler(realm);
azureChallengeHandler.isCustomResponse = function (response) {
...
};
azureChallengeHandler.handleChallenge = function (response) {
...
};
So i'm logging in with this function:
WL.Client.login(realm, options)
And the first time it works ok, isCustomResponse gets called, returns "true", then handleChallenge gets called.
But after logging out with this function:
WL.Client.logout(realm, options)
When I try to login again, isCustomResponse gets called and still returns "true", but handleChallenge is not firing.
How can I fix that?
After calling WL.Client.reloadApp() or reloading app itself I can login again, but it's not a suitable solution.
UPDATE:
Here is adapter code:
function onAuthRequired(headers) {
return customLoginResponse(true, false, false);
}
function customLoginResponse(authRequired, azureTokenRequired, wrongTenant) {
return {
authRequired: authRequired,
azureTokenRequired: azureTokenRequired,
realm: 'AzureAuth',
wrongTenant: wrongTenant
};
}
function onLogout(){
WL.Server.setActiveUser("AzureAuth", null);
WL.Logger.debug("Logged out");
}
function submitLogout(uuid, orgId, ssogroup){
WL.Server.invokeProcedure({
adapter: "AzureTokenSqlAdapter",
procedure: "removeRefreshToken",
parameters: [uuid, orgId, ssogroup]
});
onLogout();
}
function submitLogin(uuid, orgId, ssogroup, code) {
var tokenObject = getTokens(code);
if (tokenObject.id_token) {
var jwtParsed = parseJWT(tokenObject.id_token);
var tenantId = jwtParsed.tid;
var invocationResult = WL.Server.invokeProcedure({
adapter: "AzureTokenSqlAdapter",
procedure: "checkTenant",
parameters: [orgId, tenantId]
});
if (!invocationResult.tenantRegistered) {
return customLoginResponse(true, true, true);
}
}
return authUser(tokenObject, uuid, orgId, ssogroup);
}
And here is the client code:
function azureAuthService($q, _, $state) {
var loginPromise;
azureChallengeHandler = WL.Client.createChallengeHandler(realm);
//first response after protected call
azureChallengeHandler.isCustomResponse = function (response) {
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
return response.responseJSON.realm == realm;
};
//when isCustomResponse returns true
azureChallengeHandler.handleChallenge = function (response) {
WL.Logger.debug("challenge handler -- handleChallenge");
var authRequired = response.responseJSON.authRequired;
var azureTokenRequired = response.responseJSON.azureTokenRequired;
var wrongTenant = response.responseJSON.wrongTenant;
if (wrongTenant) {
loginPromise.reject('wrong tenant');
} else if (authRequired && azureTokenRequired) {
fullLogin();
} else if (authRequired) {
fastLogin();
} else {
loginPromise.resolve();
}
};
azureChallengeHandler.handleFailure = function (error) {
console.log('failure');
console.log(error);
};
return {
init: init,
login: login,
logout: logout
};
function init(config) {
ssogroup = config.ssogroup;
orgId = config.orgId;
}
function login() {
loginPromise = $q.defer();
WL.Client.login(realm, {
onSuccess: function(info) {
loginPromise.resolve();
},
onFailure: function(error) {
loginPromise.reject();
}
});
return loginPromise.promise;
}
function logout() {
var logoutPromise = $q.defer();
var invocationData = {
adapter : 'AzureAuth',
procedure : 'submitLogout',
parameters : [device.uuid, orgId, ssogroup]
};
WL.Client.invokeProcedure(invocationData).then(function () {
WL.Client.logout(realm, {
onSuccess: function () {
logoutPromise.resolve();
},
onFailure: function () {
logoutPromise.reject();
}
});
}, function () {
logoutPromise.reject();
});
return logoutPromise.promise;
}
}
fastLogin and fullLogin is functions that perform some work and finally call
var options = {
parameters: [device.uuid, orgId, ssogroup, transitionAuthObject.requestToken],
adapter: "AzureAuth",
procedure: "submitLogin"
};
azureChallengeHandler.submitAdapterAuthentication(options);
Can't see your fullLogin() and fastLogin() methods so it's hard to say for sure. Make sure that you're calling challengeHandler's submitSuccess() or submitFailure() methods after successful/failed authentication. The authentication framework keeps a queue of requests/responses that require authentication. After successful/failed authentication you need to invoke submitSuccess/submitFailure on challenge handler in order for authentication framework to remove your requests from queue and process it. In case you're not doing so the request remains in the queue and once you're sending a new request that triggers authentication it is put into queue but not handled since there's another request already waiting for authentication.
I am using Adapter-based authentication in hybrid application.
this is my adapter code
function submitAuthentication(username, password){
WL.Logger.warn("Start intial---------------------");
var input = {
method : 'post',
returnedContentType : 'json',
path : <path>,
parameters : {
<username> : username,
<password> : password,
}
};
var authResult = WL.Server.invokeHttp(input);
if(authResult.statusCode==200){
WL.Logger.warn("Start-----------user id="+authResult.id);
var userIdentity = {
userId: authResult.id+"",
displayName: username,
isUserAuthenticated:1,
attributes: {
"firstName": authResult.firstName,
"email": authResult.email,
"role": authResult.authorities[0].authority,
},
};
WL.Server.setActiveUser("SingleStepAuthRealm", null);
WL.Server.setActiveUser("SingleStepAuthRealm", userIdentity);
return {
authRequired: false
};
}else{
return onAuthRequired(null, "Invalid login credentials");
}
return authResult;
}
Now i am facing an issue from the client side
Not getting the following value in the client side
WL.Client.getLoginName("SingleStepAuthRealm")
Any thoughts about this ?
LoginViewModel.js
function login(){
var username='worklight';
var password = 'worklight';
var invocationData = {
adapter : "AuthenticationAdapter",
procedure : "submitAuthentication",
parameters : [ username, password ]
};
adapterAuthRealmChallengeHandler.submitAdapterAuthentication(invocationData, {});
AdapterAuthRealmChallengeProcessor.js
var adapterAuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
adapterAuthRealmChallengeHandler.isCustomResponse = function(response) {
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
if (typeof(response.responseJSON.authRequired) !== 'undefined'){
return true;
} else {
return false;
}
};
adapterAuthRealmChallengeHandler.handleChallenge = function(response){
var authRequired = response.responseJSON.authRequired;
if (authRequired == true){
window.location = "#login";
alert(response.responseJSON.errorMessage);
} else if (authRequired == false){
adapterAuthRealmChallengeHandler.submitSuccess();
window.location = "#Home";
}
};
AuthenticationAdapter-impl.js
function onAuthRequired(headers, errorMessage){
errorMessage = errorMessage ? errorMessage : null;
return {
authRequired: true,
errorMessage: errorMessage
};
}
function submitAuthentication(username, password){
var userIdentity = {
userId: username,
displayName: username,
attributes: {
foo: "bar"
}
};
WL.Server.setActiveUser("AdapterAuthRealm", userIdentity);
return {
authRequired: false
};
}
I am not able to set setActiveUser. How to set it and get it. Above wat I have to done for maintaining session. Is it right way to do it. Sometimes I got error Illegal State: Cannot change identity of an already logged in user in realm 'Adapter AuthRealm'. The application must logout first. If I tried to set it null first then it causes my application to enter an infinite loop of attempting to authenticate. Any idea why I would see this behavior?
Anything I am doing wrong?
please help mi out.
Check the SO answer for Worklight logout does not clear active user
Try reloading app onSuccess of Logout.
WL.Client.logout("AdapterAuthRealm",{
onSuccess: function(){ WL.Client.reloadApp(); },
onFailure: function(){ WL.Logger.debug("Error on logout");}
});
And you can check realm is authenticated or not by using wl client Java script API
WL.Client.isUserAuthenticated("AdapterAuthRealm");
I'm having some trouble with authenticating with ServiceStack using the BasicAuthProvider. All works well when I authenticate using the provider route 'auth/myauth' but when I go to one of my other service DTOS that use the [Authenticate] attribute e.g. /hello, I always get a 401 Unauthorized error even when I always supply the basic authentication details in the 'Authorization' header using beforeSend with jQuery.
Basically, I'm building an API for a mobile app that involves credential authentication on the first time(or if a supplied token isn't expired), then subsequently basic authentication of supplied token for other requests. I'm trying to authenticate every request, as described here. Also here. Here's my code:
Custom Provider
public class MyAuthProvider : BasicAuthProvider
{
public new static string Name = "MyAuth";
public new static string Realm = "/auth/myauth";
public MyAuthProvider()
{
this.Provider = Name;
this.AuthRealm = Realm;
}
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
var httpReq = authService.RequestContext.Get<IHttpRequest>();
var basicAuth = httpReq.GetBasicAuthUserAndPassword();
if (basicAuth == null)
throw HttpError.Unauthorized("Invalid BasicAuth credentials");
var us = basicAuth.Value.Key;
var ps = basicAuth.Value.Value;
if (ps == "password")
{
return true;
}
return false;
}
}
Service
public class HelloService : Service
{
//handle OPTIONS in preflight - http://joeriks.com/2013/01/12/cors-basicauth-on-servicestack-with-custom-authentication/
public object Options(Hello request) { return true; }
[Authenticate("MyAuth")]
public object Post(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
[Authenticate("MyAuth")]
public object Get(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
Configure Method
public override void Configure(Container container)
{
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {
new MyAuthProvider()
}));
//register any dependencies your services use, e.g:
container.Register<ICacheClient>(new MemoryCacheClient() { FlushOnDispose = false });
//set endpoint information
SetConfig(new EndpointHostConfig
{
GlobalResponseHeaders =
{
{"Access-Control-Allow-Origin","http://localhost"},
{"Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS"},
{"Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, Origin" }
},
});
}
This works
function make_base_auth(user, password) {
var tok = user + ':' + password;
var hash = btoa(tok);
return "Basic " + hash;
}
////
$.ajax({
url: 'http://localhost:61750/auth/myauth?format=json',
type: 'POST',
beforeSend: function(xhr) {
xhr.setRequestHeader("Authorization", make_base_auth("id#email.com","password"));
}
}).done(function (data) {
if( console && console.log ) {
console.log("Sample of data:", data);
}
});
But this doesn't
$.ajax({
url: 'http://localhost:61750/hello?format=json',
data: { Name:"Foo" },
type: 'POST',
beforeSend: function(xhr) {
xhr.setRequestHeader("Authorization", make_base_auth("id#email","password"));
}
}).done(function (data) {
if( console && console.log ) {
console.log("Sample of data:", data);
}
});
Thanks for your help.
I had to create a custom authenticate attribute with guidance from this gist -> https://gist.github.com/joeriks/4518393
In the AuthenticateIfBasicAuth method, I set provider to use MyAuthProvider.Name
Then,
[CustomAuthenticate]
public object Post(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}