Angular 4 (2-5), Cache Authentication, Cache2017.1, REST

Hi, 

 
have you any working example of authentication and services in Angular 4 (2-5) and Cache 2017.1?
 
We have authentication, but we don't want CacheUsername and CachePassword to be in url (this works), but in header (we have not get this work, Problem 1).
We have REST services and they work with CacheUserName and CachePassword, but we want them work with cookie after authentication is done (we have not get this work, Problem 2).

How to code the client and what all we have to code and set on server?
 
We've tried last this (asked also WRC /Tomas, but not got the answer).
 
    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/login: Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response.

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" Method="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).GetUsersDataByCurrentUsername()

    set dynObj={}
    ...
    do dynObj.%Set("onOikeus",tOnOikeus,"boolean")
    do:'tOnOikeus dynObj.%Set("errorMessage","Sinulla ei ole vaadittavaa oikeutta "_tVaadittavaOikeus)
    do dynObj.%Set("username",array.GetAt("kayttajatunnus"))
    do dynObj.%Set("password",array.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 OnHandleCorsRequest() As %Status
{
#; Get the origin
     Set tOrigin=$Get(%request.CgiEnvs("HTTP_ORIGIN"))

     #; Allow requested origin
    Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Origin",tOrigin) 
    #; Set allow credentials to be true
    Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Credentials","true")
    #; Allow requested method
    Set tMethod=$Get(%request.CgiEnvs("HTTP_ACCESS_CONTROL_REQUEST_METHOD"))
    Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Method",tMethod)
    quit $$$OK
}

ClassMethod OnHandleOptionsRequest() As %Status
{

    quit $$$OK
}

}

Some tests works in SoapUI, but not this way :(

PS. No AngularJS, but Angular 4 (2-5).

  • 0
  • 0
  • 1080
  • 8
  • 2

Answers

For password authenticated web applications it is possible by following these steps:

  1. All brokers effectively have Parameter UseSession = 1;
  2. REST web application and client web application allow only authenticated (i.e. password) access.
  3. REST web application and client web application have reasonable Session timeout (i.e. 900, 3600).
  4. REST web application and client web application have the same GroupById value.
  5. 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.

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.

Comments

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

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 

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?