sahat / satellizer

Token-based AngularJS Authentication
https://satellizer-sahat.rhcloud.com
MIT License
7.85k stars 1.13k forks source link

request Authorization header #120

Closed connor11528 closed 10 years ago

connor11528 commented 10 years ago

I have a setup following your node.js example. Creating users, logging in and logging out all works fine. When I try to go to the "Protected" page though I get rejected by the ensureAuthenticated middleware.

The request to /api/me does not have an authorization header. Instead the req.headers.authorization looks like this:

{ host: 'localhost:3000',
  connection: 'keep-alive',
  accept: 'application/json, text/plain, */*',
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36',
  referer: 'http://localhost:3000/',
  'accept-encoding': 'gzip,deflate,sdch',
  'accept-language': 'en-US,en;q=0.8,fr;q=0.6' }

So how do I make sure my request has an authorization header when hitting /api/me?

It looks like this part of angular sends the request:

angular.module('MyApp')
  .factory('Account', function($http) {
    return {
      getUserInfo: function() {
        return $http.get('/api/me');
      }
    };
  });

After login the server successfully sends a token, but when trying to access protected resources my requests don't have a req.body.authorization header, so they don't pass the ensureAuthenticated middleware:

screen shot 2014-09-17 at 12 24 58 pm

owentran commented 10 years ago

Hi @jasonshark, can you check if you have a satellizer.token in your local storage after you successfully login?

jgentes commented 10 years ago

I'm running into this same problem.. no authorization header. Here's the flow that I'm seeing:

  1. Call authenticate(linkedin) from Angular page
  2. Popup appears, authentication with Linkedin succeeds, redirects to callback (/) within the popup
  3. Client sends POST to /auth/linkedin, server exchanges code for access token
  4. Node server uses access token to retrieve profile data from Linkedin, no problem
  5. Server tries to pull authorization from req.headers but nothing there

I checked the client, and I don't have anything in my local storage.

owentran commented 10 years ago

@jgentes - where are you sending back a JWT token to the client? You need a server to generate a valid JWT token after you LinkedIn redirects back to your app. See...

https://github.com/sahat/satellizer/wiki/Login-with-OAuth-2.0

jgentes commented 10 years ago

Here's the logged req/res from the process:

{ request: 
   { uri: 'https://www.linkedin.com/uas/oauth2/accessToken',
     method: 'POST',
     headers: 
      { host: 'www.linkedin.com',
        'content-type': 'application/x-www-form-urlencoded; charset=utf-8',
        accept: 'application/json',
        'content-length': 307 },
     body: 'client_id=75s9q8uelaanvf&redirect_uri=https%3A%2F%2Fhost-c9-jgentes.c9.io&client_secret=kus8etN9TWmgJBba&code=TRIMMEDkoeC3NFPIbSJdtP6gHF9vrIUuwSNgg&grant_type=authorization_code&scope[0]=r_fullprofile%20r_emailaddress' } }
{ response: 
   { headers: 
      { server: 'Apache-Coyote/1.1',
        p3p: 'CP="CAO CUR ADM DEV PSA PSD OUR"',
        'content-type': 'application/json;charset=UTF-8',
        'content-language': 'en-US',
        'content-length': '219',
        vary: 'Accept-Encoding',
        date: 'Wed, 17 Sep 2014 23:00:56 GMT',
        'x-fs-uuid': '829be6a458dc941380226e7b672b0000',
        'x-li-uuid': 'gpvmpFjclBOAIm57ZysAAA==',
        'x-content-type-options': 'nosniff',
        'x-xss-protection': '1; mode=block',
        'x-li-fabric': 'PROD-ELA4',
        'strict-transport-security': 'max-age=0',
        'set-cookie': [Object],
        pragma: 'no-cache',
        expires: 'Thu, 01 Jan 1970 00:00:00 GMT',
        'cache-control': 'no-cache, no-store',
        connection: 'keep-alive',
        'x-li-pop': 'PROD-ELA4' },
     statusCode: 200,
     body: 
      { access_token: 'TRIMMEDqhyax8M7-6R97hcOPKsHFJq5c',
        expires_in: 5122401 } } }
EXCHANGED TOKEN
{ request: 
   { uri: 'https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address,skills,picture-url;secure=true,headline,summary,public-profile-url)?oauth2_access_token=TRIMMEDmns-h0Y0uM-r7Y_Zev6qAkgqdojb_TP40EhCe7TuXQHqhyax8M7-6R97hcOPKsHFJq5c&format=json',
     method: 'GET',
     headers: { host: 'api.linkedin.com', accept: 'application/json' } } }
{ response: 
   { headers: 
      { server: 'Apache-Coyote/1.1',
        'x-li-request-id': '53BGS5ETAO',
        'x-li-uuid': 'Jhx3uFjclBOw0RlJ0ioAAA==',
        vary: '*',
        'x-li-format': 'json',
        'content-type': 'application/json;charset=UTF-8',
        date: 'Wed, 17 Sep 2014 23:00:57 GMT',
        'x-li-fabric': 'PROD-ELA4',
        'transfer-encoding': 'chunked',
        connection: 'keep-alive',
        'x-li-pop': 'PROD-ELA4',
        'set-cookie': [Object] },
     statusCode: 200,
     body: 
      { emailAddress: 'james@EMAIL.com',
        firstName: 'James',
        headline: 'Founder & CEO at The Social Business',
        id: 'SnhCuif41W',
        lastName: 'Gentes',
        pictureUrl: 'https://media.licdn.com/mpr/mprx/0_iIoQnZ2ok3cBeamY_EuMnV2Q6b3ceHmYS7OJnRxUJCz6BuMO7DVE4UIvHdTdIf7tGHHZMyTi0IrA',
        publicProfileUrl: 'https://www.linkedin.com/in/jgentes',
        skills: [Object],
        summary: '.' } } }
GOT PROFILE
{ host: 'host.c9.io',
  'content-length': '212',
  accept: 'application/json, text/plain, */*',
  origin: 'https://HOST.c9.io',
  'user-agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36',
  'content-type': 'application/json;charset=UTF-8',
  referer: 'https://host.c9.io/',
  'accept-language': 'en-US,en;q=0.8',
  cookie: '__TRIMMED14842bf06979f6%22%2C%22%24initial_referrer%22%3A%20%22%24direct%22%2C%22%24initial_referring_domain%22%3A%20%22%24direct%22%7D; ajs_user_id=%22197783%22; ajs_group_id=null',
  'x-forwarded-proto': 'https',
  'x-region': 'usw',
  'x-forwarded-for': '208.100.130.92',
  connection: 'keep-alive' }
AUTHORIZATION NOT IN HEADER
jgentes commented 10 years ago

Thanks @owentran I assumed the server example included that. If not I can use jwt to create the token, I just didn't know that was needed.

owentran commented 10 years ago

Which server example are you using? Your best bet is to turn on Chrome inspector to watch the traffic going back and forth. You should see a response from login come back with a valid 200 and a JWT token. satellizer will then store that JWT token into local storage.

jgentes commented 10 years ago

I'm using Node - and looking at the linkedin response (in the main client window, not the popup) I see an error, so I'll look into that a bit more closely ({"message":"missing required parameters, includes an invalid parameter value, parameter more than once. : Unable to retrieve access token : appId or redirect uri does not match authorization code or authorization code expired"})

It seems strange, since I do get the profile data back from Linkedin properly.

On Wed, Sep 17, 2014 at 4:14 PM, Owen Tran notifications@github.com wrote:

Which server example are you using? Your best bet is to turn on Chrome inspector to watch the traffic going back and forth. You should see a response from login come back with a valid 200 and a JWT token. satellizer will then store that JWT token into local storage.

— Reply to this email directly or view it on GitHub https://github.com/sahat/satellizer/issues/120#issuecomment-55975215.

jgentes commented 10 years ago

Here's what I'm seeing on the client:

Remote Address:23.251.156.127:443
Request URL:https://host.c9.io/auth/linkedin
Request Method:POST
Status Code:400 Bad Request
Request Headers
POST /auth/linkedin HTTP/1.1
Host: host.c9.io
Connection: keep-alive
Content-Length: 212
Accept: application/json, text/plain, */*
Origin: https://host.c9.io
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: https://host.c9.io/
Accept-Encoding: gzip,deflate
Accept-Language: en-US,en;q=0.8
Cookie: TRIMMED; ajs_group_id=null
Request Payload
{"code":"AQS5FWtA_UDxZRZAeOJRuneeS9bOc","clientId":"75selaanvf","redirectUri":"https://host.c9.io"}
Response Headersview parsed
HTTP/1.1 400 Bad Request
x-powered-by: Express
vary: X-HTTP-Method-Override
content-type: application/json; charset=utf-8
content-length: 227
date: Wed, 17 Sep 2014 23:46:16 GMT
jgentes commented 10 years ago

Solved it: In my Linkedin handler function, I was using a q.deferred method to create a promise for interacting with my database. As a result, the token wasn't being created properly. Once I set that to simply res.send the token back instead of using the deferred promise, it worked fine.

Thanks for the help @owentran, sorry for taking over this thread :)

-James

sahat commented 10 years ago

Hey @owentran thanks for helping out @jgentes. Try clearing the local storage and using the latest version of Satellizer. @jasonshark Token is no longer stored as token like in your screenshot, instead it is satellizer_token, where satellizer_ is an overridable prefix via $authProvider.tokenPrefix.

connor11528 commented 10 years ago

Thanks @sahat I upgraded to the most recent build and have auth working

umasgn commented 9 years ago

I'm getting the same problem as @cleechtech , in my client local storage i can see token, but req object on server has no authorization header. @owentran

mbejda commented 8 years ago

same...

sahat commented 8 years ago

Have you used $authProvider.httpInterceptor flag at all?

Default value: httpInterceptor: function() { return true; } which means it will use HTTP interceptor that adds this authorization header to every request.

Here is the source code:

.factory('SatellizerInterceptor', [
      '$q',
      'SatellizerConfig',
      'SatellizerStorage',
      'SatellizerShared',
      function($q, config, storage, shared) {
        return {
          request: function(request) {
            if (request.skipAuthorization) {
              return request;
            }

            if (shared.isAuthenticated() && config.httpInterceptor(request)) {
              var tokenName = config.tokenPrefix ? config.tokenPrefix + '_' + config.tokenName : config.tokenName;
              var token = storage.get(tokenName);

              if (config.authHeader && config.authToken) {
                token = config.authToken + ' ' + token;
              }

              request.headers[config.authHeader] = token;
            }

            return request;
          },
          responseError: function(response) {
            return $q.reject(response);
          }
        };
      }])
    .config(['$httpProvider', function($httpProvider) {
      $httpProvider.interceptors.push('SatellizerInterceptor');
    }]);

I am not sure why it doesn't work for you, but you can easily add this at the application level in your app's config.

connor11528 commented 8 years ago

@umasgn you're talking express server? Here are some express tips on accessing headers: http://stackoverflow.com/questions/13147693/how-to-extract-request-http-headers-from-a-request-using-nodejs-connect

You using passport for authentication on server side?

samarhaider commented 7 years ago

@umasgn @mbejda I have commit below line and my problem solved

// $authProvider.httpInterceptor = false;