Angular 4 (2-5), Cache Authentication, Cache2017.1, REST
Hi,
How to code the client and what all we have to code and set on server?
private _loginUrl = _param.baseUrl + 'login'; login(username: string, password: string) { let headers = new Headers(); headers.append(' Authorization', 'Basic ' + Base64.encode(username+':'+ password)); let options = new RequestOptions({ headers: headers, withCredentials: true }); options.headers= headers; let tUrl=this._ loginUrl return this.http.get( tUrl,options) .map((res: Response) => res.json()) .catch((error: any) => Observable.throw(new Error(error.status)) ); }
GOT:
Failed to load http://server:port/csp/xxx/
After this tried
let requestOptions={
headers: new Headers(
{'Accept':'application/json',
'Content-Type':'application/json',
'Authorization': 'Basic ' + Base64.encode(username+':'+password)}
)};
let tUrl=this._loginUrl
return this.http.get(tUrl,requestOptions)
.map((res: Response) => res.json())
.catch((error: any) => Observable.throw(new Error(error.status))
); WebApplication is /csp/xxx and we've enabled Application, CSP/Zen and inbound Web Services. Use Cookie for Session: allways. Session cookie path is Authentication: unauth, Password, Delegated. No login cookie, but do we need it after authentication? (Problem 2)
On server we have Dispatch Class xxx.service:
Class xxx.service Extends %CSP.REST { Parameter UseSession As Integer = 1; Parameter HandleCorsRequest = 1; Parameter CONTENTTYPE = "application/json"; XData UrlMap [ XMLNamespace = "http://radu.lforce.fi/urlmap" ] { <Routes> <Route Url="/login" M ethod="GET" Call="xxxlogin" /> <Route Url="/logout" Method=" GET" Call="xxxlogout" /> </Routes> } ClassMethod xxxlogin() As %Status { set $ZT="ERROR" #dim array As % ArrayOfDataTypes set array=##class(Radu. DataAccess.User).GetUsersDataB yCurrentUsername() set dynObj={} ... do dynObj.%Set("onOikeus",tOnO ikeus,"boolean") do:'tOnOikeus dynObj.%Set(" errorMessage","Sinulla ei ole vaadittavaa oikeutta "_tVaadittavaOikeus) do dynObj.%Set("username",arra y.GetAt("kayttajatunnus")) do dynObj.%Set("password",arra y.GetAt("salasana")) do dynObj.%Set("fullName",$ ZCONVERT(array.GetAt(" fullName"),"O","UTF8")) do dynObj.%Set("sessionId",% session.SessionId) set %session.AppTimeout=21600 write dynObj.%ToJSON() quit $$$OK ERROR do BACK^%ETN quit $$$ERROR($$$CacheError,"$ ZE") } ClassMethod xxxlogout() As %Status { set $ZT="ERROR" try { ... } catch { ... do BACK^%ETN } set dynObj={} write dynObj.%ToJSON() do %session.Logout() set %session.EndSession=1 set %session.AppTimeout=1 quit $$$OK ERROR do BACK^%ETN quit $$$ERROR($$$CacheError,"$ ZE") } ClassMethod OnHandleCorsReques t() As %Status { #; Get the origin Set tOrigin=$Get(%request .CgiEnvs("HTTP_ORIGIN")) #; Allow requested origin Do ..SetResponseHeaderIfEm pty("Access-Control-Allow- Origin",tOrigin) #; Set allow credentials to be true Do ..SetResponseHeaderIfEm pty("Access-Control-Allow- Credentials","true") #; Allow requested method Set tMethod=$Get(%request. CgiEnvs("HTTP_ACCESS_CONTROL_ REQUEST_METHOD")) Do ..SetResponseHeaderIfEm pty("Access-Control-Allow- Method",tMethod) quit $$$OK } ClassMethod OnHandleOptionsReq uest() As %Status { quit $$$OK } }
Some tests works in SoapUI, but not this way :(
PS. No AngularJS, but Angular 4 (2-5).
Comments
For password authenticated web applications it is possible by following these steps:
- All brokers effectively have Parameter UseSession = 1;
- REST web application and client web application allow only authenticated (i.e. password) access.
- REST web application and client web application have reasonable Session timeout (i.e. 900, 3600).
- REST web application and client web application have the same GroupById value.
- REST web application and client web application have the same cookie path.
If all these conditions are met, user would only consume one license slot per session and perform only one login.
Check out my upcoming webinar, we definitely would be discussing authentication.
I have no real answer to the logon issue
What about custom login pages?
<html>
<head>
<title>cUSTOM LOGIN PAGE</title>
</head>
<body>
<div style="">
<form name="loginForm" class="form-signin" method="post" action="#("./index.csp")#">
<p id="caption">Registration system</p>
<input name="CacheLogin" value="1" type="hidden">
<input id="CacheUserName" type="text" class="input-block-level" name="CacheUserName" placeholder="Login" value="_SYSTEM">
<input type="password" class="input-block-level" name="CachePassword" placeholder="Password" value="SYS">
<button class="btn btn-small btn-primary" type="submit" style="font-size: 1em;">Login</button>
</form>
</div>
</body>
</html>Hey Eduard
This I understand and I use this at the moment in my ZEN apps
But...
Anne needs a JSON restful method of getting a logon and get a session token - only consuming a single license slot.
*NOT* a HTML web page to logon that a CSP logon delivers - this is not what she is looking for.
As she said, you could pass CacheUsername and CachePassword as a URL parameter - but this don't work - if it fails Cahché delivers a HTML page - not what is required for a restful logon.
The display/content should be in control of the client UI - not delivering a CSP HTML page
I'm sorry if I seem to be dismissive of your answer - but the web is moving on and we can no longer rely on the "old" way of doing things.
Peter
We're using Custom Login pages and they don't show other page on fail - just User/pass incorrect message. @Sergei Sarkisian? There's also this discussion.
Basic authorization also works - it's used in DeepSeeWeb. OP has a problem with CORS and maybe roles.
Hi Anne
Looks like we are on the same journey - have a look here on what I have so far
= =
I have no real answer to the logon issue - I think the "correct" solution is for Intersystems to provide a restful logon service to provide this functionality that can be accessed without being already logged on.
One possibility that I can think of that *may* work (have not tried it *yet*) ....
create a separate CSP web site that has no restrictions on logging on - with a single restful service "logon" - in can be in the same namespace but with a different dispatch class.
The COS then performs a $System.Security.Login("username", "password")
and returns success or fail - and sets up the session token
I can see that there may be issues with CORS - but I don't understand this topic yet :}
= =
As an aside....
I have been working with Caché for (too) may years and their security model/features is hidden away, difficult and subject to change.
I can understand this - they need to protect their commercial interests - eg it is technically possible for a minimal license (5 users) to support 1,000's of end user clients - clearly not commercially sensible.
Peter
Failed to load http://server:port/csp/xxx/
login: Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response.
Add Access-Control-Allow-Headers header to OPTIONS response?
Please ask this as a separate question.
ok
For pre-flight request (OPTIONS):
The problem is caused by the way how the OPTIONS request is handled. According to documentation, OPTIONS request doesn't need the authorization even if the CSP application requires it. But the CSP process still needs to access given REST class to get response for OPTIONS request. The problem is that given process is run under the user which logs from CSP Gateway to Caché - this is set in CSP Gateway Management page > Server Access.
If given user doesn't have right to access given namespace/database, it gets the <PROTECT> error on server and the CSP sends back the HTTP 404 error.
The solution is to add role to given CSP Gateway user with minimum READ access to corresponding database.
This deleted. I added new question.