RC3 Http missed Authorization Header in CORS preflight actual request


#1

Hello,

I think the problem most likely lies with Angular 2…

I am trying to put an Authorization header in the request to remote server in order to authenticate an user by a JWT token. and having problem extracting the “Authentication” headers from the request at the server side.

I tracked the message sequence between ionic 2 terminal and server. and found out that “Authentication” header was missed from the actual request header even though the server had responded correctly the previous OPTIONS request. see below message sequence

the OPTION round:

OPTIONS /gaters/Gate/display HTTP/1.1
Host: gate.gubnoi.com:9763
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:8100
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/53.0.2785.143 Chrome/53.0.2785.143 Safari/537.36
Access-Control-Request-Headers: content-type
Accept: */*
Referer: http://localhost:8100/?ionicplatform=ios
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,de;q=0.6,es;q=0.4,fr;q=0.2,zh-CN;q=0.2,zh-TW;q=0.2

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:8100
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1209600
Access-Control-Allow-Methods: POST, HEAD, GET, OPTIONS
Access-Control-Allow-Headers: Accept-Charset, Authorization, Origin, Accept, User-Agent, Accept-Encoding, Accept-Language, Content-Length, Accept-Datetime, Content-Type
Content-Length: 0
Date: Sun, 18 Dec 2016 08:32:36 GMT
Server: WSO2 Carbon Server

the POST round

POST /gaters/Gate/display HTTP/1.1
Host: gate.gubnoi.com:9763
Connection: keep-alive
Content-Length: 2
Origin: http://localhost:8100
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/53.0.2785.143 Chrome/53.0.2785.143 Safari/537.36
content-type: application/json
Accept: */*
Referer: http://localhost:8100/?ionicplatform=ios
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,de;q=0.6,es;q=0.4,fr;q=0.2,zh-CN;q=0.2,zh-TW;q=0.2

HTTP/1.1 500 Internal Server Error
Access-Control-Allow-Origin: http://localhost:8100
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Accept-Charset, Authorization, Origin, Accept, User-Agent, Accept-Encoding, Accept-Language, Content-Length, Accept-Datetime, Content-Type
Content-Type: text/html;charset=utf-8
Content-Language: en
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding
Date: Sun, 18 Dec 2016 08:32:36 GMT
Connection: close
Server: WSO2 Carbon Server

the post message triggered an server internal error because the srever get a null while extracting “Authentication” header

and my ts code:

ionViewWillEnter() {
    this.gabbriel.display().subscribe(
        data=> { console.log('after gabriel');
          this.profile.userName = data['userName'];
          this.profile.password = data['password'];
          debugger;
        },
        error => {
          console.log('somthing went wrong ');
          debugger;
        },
        () => console.log('left GeneralPage')
    );
  }

display():Observable<{}>{
    //only login user can display his /her profile          
    debugger;
    let options = Oauth2.sign(new RequestOptions({ headers: new Headers({'Content-Type': 'application/json'})}));
    let body = JSON.stringify({});
    debugger;
    return this.http.post(Urls.display, body, options).timeout(10000).map((res:Response) => res.json());  ;
  }

I tracked into the Oauth2.sign(), can be sure of the presence of the “Authorization” header in the “options” before calling the “http.post”

and what more, another process that does not involve with “Authorization” goes well … (see below)

the “OPTION” round

OPTIONS /gaters/Gate/require HTTP/1.1
Host: gate.gubnoi.com:9763
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:8100
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/53.0.2785.143 Chrome/53.0.2785.143 Safari/537.36
Access-Control-Request-Headers: content-type
Accept: */*
Referer: http://localhost:8100/?ionicplatform=ios
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,de;q=0.6,es;q=0.4,fr;q=0.2,zh-CN;q=0.2,zh-TW;q=0.2

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:8100
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1209600
Access-Control-Allow-Methods: POST, HEAD, GET, OPTIONS
Access-Control-Allow-Headers: Accept-Charset, Authorization, Origin, Accept, User-Agent, Accept-Encoding, Accept-Language, Content-Length, Accept-Datetime, Content-Type
Content-Length: 0
Date: Sun, 18 Dec 2016 08:31:13 GMT
Server: WSO2 Carbon Server

the actual “POST” round

POST /gaters/Gate/require HTTP/1.1
Host: gate.gubnoi.com:9763
Connection: keep-alive
Content-Length: 102
Origin: http://localhost:8100
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/53.0.2785.143 Chrome/53.0.2785.143 Safari/537.36
content-type: application/json
Accept: */*
Referer: http://localhost:8100/?ionicplatform=ios
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,de;q=0.6,es;q=0.4,fr;q=0.2,zh-CN;q=0.2,zh-TW;q=0.2

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:8100
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Accept-Charset, Authorization, Origin, Accept, User-Agent, Accept-Encoding, Accept-Language, Content-Length, Accept-Datetime, Content-Type
Date: Sun, 18 Dec 2016 08:31:21 GMT
Content-Type: text/plain
Content-Length: 96
Server: WSO2 Carbon Server

my “package.json”

 "dependencies": {
    "@angular/common": "2.1.1",
    "@angular/compiler": "2.1.1",
    "@angular/compiler-cli": "2.1.1",
    "@angular/core": "2.1.1",
    "@angular/forms": "2.1.1",
    "@angular/http": "2.1.1",
    "@angular/platform-browser": "2.1.1",
    "@angular/platform-browser-dynamic": "2.1.1",
    "@angular/platform-server": "2.1.1",
    "@ionic/storage": "1.1.6",
    "client-oauth2": "^3.2.0",
    "ionic-angular": "^2.0.0-rc.3",
    "ionic-native": "^2.2.3",
    "rxjs": "5.0.0-beta.12",
    "serialize-javascript": "^1.3.0",
    "text-encoding": "^0.6.1",
    "zone.js": "^0.6.21"
  },

my “ionic info”

Your system information:

Cordova CLI: 6.4.0 
Ionic Framework Version: 2.0.0-rc.3
Ionic CLI Version: 2.1.12
Ionic App Lib Version: 2.1.7
Ionic App Scripts Version: 0.0.46
ios-deploy version: Not installed
ios-sim version: Not installed
OS: Linux 4.4
Node Version: v4.6.2
Xcode version: Not installed

#2

Where you successful with this? I was trying to get it working with client-oauth2 as well.


#3

sorry for the late response.
yes, i get done.
there are two aspect:
1, CORS is controlled from the server side. so basically, you will need to do the config on the server side. it could be different based on the backend platform you are using.

  1. it seems that there were some bugs on the angular while forming the HttpHeader
    I raised an issue https://github.com/angular/angular/issues/13554 to angular.
    the problem might also very likely lie with the client-oauth2 function: Oauth2.sign(), even though i have not been able to confirm it

below are my current implementation for http header manipulation

query(process: string, bucket: string): Observable<string> {
    let gateway = "12";
    let session = Util.uuidv4();
    let body = JSON.stringify({"gateway": gateway, "session": session, "device": this.user.id, "process": process, "bucket": bucket});
    return Observable.fromPromise(this.oauth2.getToken())
      .pipe(
        map(tk => {
            if(this.user._login){
              return new HttpHeaders().set("Content-Type", "application/json").set("Authorization", tk);    
            }
            return new HttpHeaders().set("Content-Type", "application/json");}),
        mergeMap(httpheaders => this.http.post(Cons.require, body, {"headers":httpheaders})),
        timeout(10000),
        map(res => res["bucket"]));
  }
@Injectable()
export class Oauth2 {
  private static _tk: ClientOAuth2.Token;
 
  private static oauth2 = new ClientOAuth2({
    clientId: Cons.ISKEY,
    clientSecret: Cons.ISSECRET,
    accessTokenUri: Cons.token,
    authorizationUri: Cons.authorize,
    scopes: []
  });
  ...

async getToken(): Promise<string> {
    let ret: string;
    // login and token expired
    if(Oauth2._tk && Oauth2._tk.expired()){
      try {
        let tk: ClientOAuth2.Token = await Oauth2._tk.refresh();
        this.doLogin(tk);
      } catch (e){
        this.doLogout();
      }                    
    }
    if(Oauth2._tk){
      ret = "Bearer " + Oauth2._tk.accessToken;
    }
    return ret;
  }
...