Loopback: Error "401 Authorization required" when querying methods involving relations

Created on 31 Jul 2014  Â·  40Comments  Â·  Source: strongloop/loopback

I've recently migrated an application to LoopBack 2.0.

All my models require user authentication and have these ACLs:

    "acls": [
      {
        "accessType": "*",
        "permission": "DENY",
        "principalType": "ROLE",
        "principalId": "$everyone"
      },
      {
        "accessType": "READ",
        "permission": "ALLOW",
        "principalType": "ROLE",
        "principalId": "$authenticated"
      }
    ]

I have a model called issues. After logging in through the POST method /Users/login:

  • I don't have any problems when calling the GET method /issues. I get a list of issues as expected;
  • When I query a method involving a relation between two models (e.g. GET /issues/{id}/categories), I get the error 401 Authorization required. The same model used to work in Loopback 1.0.

Any clues?

doc

Most helpful comment

I think the core User ACL's are interfering with the ACL's you defined. I recall the following from one of my projects, which overrides all the ACL's:

TestUser.settings.acls = [ 
      { principalType: 'ROLE',
        principalId: '$everyone',
        permission: 'DENY' },
      { principalType: 'ROLE', // this is the important bit
        principalId: '$owner',
        permission: 'ALLOW' },
      { principalType: 'ROLE',
        principalId: '$everyone',
        permission: 'ALLOW',
        property: 'create' },
      { principalType: 'ROLE',
        principalId: '$owner',
        permission: 'ALLOW',
        property: 'deleteById' },
      { principalType: 'ROLE',
        principalId: '$everyone',
        permission: 'ALLOW',
        property: 'login' },
      { principalType: 'ROLE',
        principalId: '$everyone',
        permission: 'ALLOW',
        property: 'logout' },
      { principalType: 'ROLE',
        principalId: '$owner',
        permission: 'ALLOW',
        property: 'findById' },
      { principalType: 'ROLE',
        principalId: '$owner',
        permission: 'ALLOW',
        property: 'updateAttributes' },
      { principalType: 'ROLE',
        principalId: '$everyone',
        permission: 'ALLOW',
        property: 'confirm' }
    ]

All 40 comments

I have the same issue. I also think the 401 Unauthorized is a wrong response. I am authenticated, so it should be 403 forbidden. I'm opening a new issue for that.

I think this has already been implemented: https://github.com/strongloop/loopback/issues/301

This issue (#426) itself is not yet resolved. Defining an ACL on a relation property does not seem to work for me.
Replicate by defining a model, adding a relationship to it (for example many user to many group). Then DENY ALL on * for ROLE $everyone, and finally ALLOW READ/EXECUTE on for ROLE $authenticated. I consequently get access denied for these relationships.

I am having the exact same issue! I spent a few hours digging through anything I can find, but I still couldnt get it to work!

@dagumak - can you post the ACL's you defined?

@fabien I posted it here (https://github.com/strongloop/loopback/issues/459#issuecomment-52635774), but here it is:

{
  "name": "TestUser",
  "base": "User",
  "strict": true,
  "properties": {
    "username": {
      "type": "string",
      "required": true
    }
  },
  "validations": [],
  "relations": {
    "message": {
      "type": "hasMany",
      "model": "Message",
      "foreignKey": "userId"
    }  
  },
  "acls": [

  ],
  "methods": []
}
{
  "name": "Message",
  "base": "PersistedModel",
  "properties": {
    "type": {
      "type": "string",
      "required": true
    },
    "text": {
      "type": "string",
      "required": true
    }
  },
  "validations": [],
  "relations": {
    "testUser": {
      "type": "belongsTo",
      "model": "TestUser",
      "foreignKey": "userId"
    }
  },
  "acls": [
    {
      "principalType": "ROLE",
      "principalId": "$everyone",
      "permission": "ALLOW"
    }
  ],
  "methods": []
}

I think the core User ACL's are interfering with the ACL's you defined. I recall the following from one of my projects, which overrides all the ACL's:

TestUser.settings.acls = [ 
      { principalType: 'ROLE',
        principalId: '$everyone',
        permission: 'DENY' },
      { principalType: 'ROLE', // this is the important bit
        principalId: '$owner',
        permission: 'ALLOW' },
      { principalType: 'ROLE',
        principalId: '$everyone',
        permission: 'ALLOW',
        property: 'create' },
      { principalType: 'ROLE',
        principalId: '$owner',
        permission: 'ALLOW',
        property: 'deleteById' },
      { principalType: 'ROLE',
        principalId: '$everyone',
        permission: 'ALLOW',
        property: 'login' },
      { principalType: 'ROLE',
        principalId: '$everyone',
        permission: 'ALLOW',
        property: 'logout' },
      { principalType: 'ROLE',
        principalId: '$owner',
        permission: 'ALLOW',
        property: 'findById' },
      { principalType: 'ROLE',
        principalId: '$owner',
        permission: 'ALLOW',
        property: 'updateAttributes' },
      { principalType: 'ROLE',
        principalId: '$everyone',
        permission: 'ALLOW',
        property: 'confirm' }
    ]

@dagumak the very least you could do is trace your current TestUser.settings.acls to compare them.

@Fabien I feel silly for asking, but other than console log, how can I do that?

On Tue, Aug 19, 2014 at 11:32 PM, Fabien Franzen [email protected]
wrote:

@dagumak the very least you could do is trace your current TestUser.settings.acls to compare them.

Reply to this email directly or view it on GitHub:
https://github.com/strongloop/loopback/issues/426#issuecomment-52642645

@dagumak console.log is fine

@fabien I am not very familiar with the framework yet, but where are the models loaded and a good place for me to console.log it?

You can put the following into a file at /server/boot/debug.js:

module.exports = function(app) {
  var TestUser = app.loopback.getModel('TestUser');
  console.log(TestUser.settings.acls);
};

You can run the application with DEBUG enabled:

$ DEBUG=loopback:security:* node .

Thanks,


Raymond Feng
Co-Founder and Architect @ StrongLoop, Inc.

StrongLoop makes it easy to develop APIs in Node, plus get DevOps capabilities like monitoring, debugging and clustering.

On Aug 19, 2014, at 8:29 AM, Fabien Franzen [email protected] wrote:

You can put the following into a file at /server/boot/debug.js:

module.exports = function(app) {
var TestUser = app.loopback.getModel('TestUser');
console.log(TestUser.settings.acls);
};
—
Reply to this email directly or view it on GitHub.

@fabien Thanks a bunch! Here it is:

[ { principalType: 'ROLE',
    principalId: '$everyone',
    permission: 'DENY' },
  { principalType: 'ROLE',
    principalId: '$everyone',
    permission: 'ALLOW',
    property: 'create' },
  { principalType: 'ROLE',
    principalId: '$owner',
    permission: 'ALLOW',
    property: 'deleteById' },
  { principalType: 'ROLE',
    principalId: '$everyone',
    permission: 'ALLOW',
    property: 'login' },
  { principalType: 'ROLE',
    principalId: '$everyone',
    permission: 'ALLOW',
    property: 'logout' },
  { principalType: 'ROLE',
    principalId: '$owner',
    permission: 'ALLOW',
    property: 'findById' },
  { principalType: 'ROLE',
    principalId: '$owner',
    permission: 'ALLOW',
    property: 'updateAttributes' },
  { principalType: 'ROLE',
    principalId: '$everyone',
    permission: 'ALLOW',
    property: 'confirm' } ]

Is this resolved ? I am still having the same issue
The last thread doesn't help

[ { principalType: 'ROLE',
principalId: '$everyone',
permission: 'DENY' },
{ principalType: 'ROLE',
principalId: '$everyone',
permission: 'ALLOW',
property: 'create' },
{ principalType: 'ROLE',
principalId: '$owner',
permission: 'ALLOW',
property: 'deleteById' },
{ principalType: 'ROLE',
principalId: '$everyone',
permission: 'ALLOW',
property: 'login' },
{ principalType: 'ROLE',
principalId: '$everyone',
permission: 'ALLOW',
property: 'logout' },
{ principalType: 'ROLE',
principalId: '$owner',
permission: 'ALLOW',
property: 'findById' },
{ principalType: 'ROLE',
principalId: '$owner',
permission: 'ALLOW',
property: 'updateAttributes' },
{ principalType: 'ROLE',
principalId: '$everyone',
permission: 'ALLOW',
property: 'confirm' },
{ principalType: 'ROLE',
principalId: '$everyone',
permission: 'ALLOW',
property: 'resetPassword',
accessType: 'EXECUTE' },
{ accessType: '*',
principalType: 'ROLE',
principalId: '$everyone',
permission: 'ALLOW' } ]

Something that helped me with ACL and relations is to run "DEBUG=loopback:security:* slc run" and then check the property it is trying to access.
At first I tried adding ACL to the "orders" property (since it was like /users/2/orders) but it seems like the ACL resolver tried to access this property: __get__orders.
I set that in my json acl file and it is working like a charm.

Example:
"acls": [
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW",
"property": "__get__orders"
}

Please create a test project on Github to reproduce the issue as per https://github.com/strongloop/loopback/wiki/Issues#bug-report

@rubentorresbonet the acl that you defined with the property __get__orders is for which model?
Is it for the user model or for the orders model?

@rubentorresbonet Holy cow, that worked! I had to use the property __get__customer for a relationship named customer. Thanks!

DEBUG=loopback:security:* slc run really helped me understand what was happening with the ACLs.

This was on a subclassed user model. The full thing looks like this now:

{
  "name": "user",
  "base": "User",
  "idInjection": true,
  "properties": {},
  "validations": [],
  "relations": {
    "customer": {
      "type": "hasOne",
      "model": "Customer"
    }
  },
  "acls": [
    {
      "principalType": "ROLE",
      "principalId": "$owner",
      "permission": "ALLOW",
      "property": "__get__customer"
    }
  ],
  "methods": []
}

This can't possibly be the intended design.

Agree with @jdhiro that from a design perspective, this is very uncomfortable. Would it not be better to introduce a single declaration in order to to set R/W/X privileges on a single relation, just as we can do for other built-in methods?

By the way, the link to the documentation for accessing related models provided above is broken, the correct one is here:

https://docs.strongloop.com/display/public/LB/Accessing+related+models

I spent a few hours on this too. Doc is not clear. It should clearly mention that default is denied

@crandmck ^

While trying CoffeeShop example given at https://docs.strongloop.com/display/LB/Getting+started+part+II I'm getting 401 only while editing the review. I've the ACLs defined as given @ https://docs.strongloop.com/display/public/LB/Define+access+controls

So the following ACL entry is failing:

{
"accessType": "WRITE",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW"
}

If I change $owner to $authenticated, then I don't get 401. So there seems to be some problem in isOwner().
I've tried using DEBUG=loopback:security:* and it gives following output

loopback:security:access-context ---AccessContext--- +1ms
loopback:security:access-context principals: +3ms
loopback:security:access-context principal: {"type":"USER","id":"562a105037cfea381ac14fbb"} +2ms
loopback:security:access-context modelName Review +3ms
loopback:security:access-context modelId 562a105037cfea381ac14fbe +1ms
loopback:security:access-context property upsert +1ms
loopback:security:access-context method upsert +2ms
loopback:security:access-context accessType WRITE +1ms
loopback:security:access-context accessToken: +2ms
loopback:security:access-context id "EoEwOGWIWLNfw4DLgxf9ga2zCoFht974DgCccMFkQLmLrmnQJ64eHJidDIf4e8dh" +1ms
loopback:security:access-context ttl 1209600 +3ms
loopback:security:access-context getUserId() 562a105037cfea381ac14fbb +2ms
loopback:security:access-context isAuthenticated() true +3ms
loopback:security:role Custom resolver found for role $everyone +0ms
loopback:security:role isInRole(): $everyone +2ms
loopback:security:access-context ---AccessContext--- +1ms
loopback:security:access-context principals: +1ms
loopback:security:access-context principal: {"type":"USER","id":"562a105037cfea381ac14fbb"} +1ms
loopback:security:access-context modelName Review +2ms
loopback:security:access-context modelId 562a105037cfea381ac14fbe +1ms
loopback:security:access-context property upsert +1ms
loopback:security:access-context method upsert +3ms
loopback:security:access-context accessType WRITE +1ms
loopback:security:access-context accessToken: +1ms
loopback:security:access-context id "EoEwOGWIWLNfw4DLgxf9ga2zCoFht974DgCccMFkQLmLrmnQJ64eHJidDIf4e8dh" +2ms
loopback:security:access-context ttl 1209600 +2ms
loopback:security:access-context getUserId() 562a105037cfea381ac14fbb +2ms
loopback:security:access-context isAuthenticated() true +1ms
loopback:security:role Custom resolver found for role $everyone +1ms
loopback:security:role isInRole(): $owner +2ms
loopback:security:access-context ---AccessContext--- +1ms
loopback:security:access-context principals: +2ms
loopback:security:access-context principal: {"type":"USER","id":"562a105037cfea381ac14fbb"} +2ms
loopback:security:access-context modelName Review +2ms
loopback:security:access-context modelId 562a105037cfea381ac14fbe +2ms
loopback:security:access-context property upsert +1ms
loopback:security:access-context method upsert +1ms
loopback:security:access-context accessType WRITE +2ms
loopback:security:access-context accessToken: +1ms
loopback:security:access-context id "EoEwOGWIWLNfw4DLgxf9ga2zCoFht974DgCccMFkQLmLrmnQJ64eHJidDIf4e8dh" +2ms
loopback:security:access-context ttl 1209600 +2ms
loopback:security:access-context getUserId() 562a105037cfea381ac14fbb +1ms
loopback:security:access-context isAuthenticated() true +2ms
loopback:security:role Custom resolver found for role $owner +2ms
loopback:security:role isOwner(): Review 562a105037cfea381ac14fbe userId: 562a105037cfea381ac14fbb +2ms
loopback:security:role Model found: {"date":"2015-10-19T10:47:44.791Z","rating":5,"comments":"A very good coffee shop.","id":"562a105037cfea381ac14fbe","coffeeShopId":"562a105037cfea381ac14fb8","publisherId":"562a105037cfea381ac14fbb"} +5ms
loopback:security:role Checking relation reviewer to Reviewer: {"name":"reviewer","type":"belongsTo","modelFrom":"Review","keyFrom":"pulisherId","modelTo":"Reviewer","keyTo":"id","multiple":false} +4ms
loopback:security:acl The following ACLs were searched: +4ms
loopback:security:acl ---ACL--- +1ms
loopback:security:acl model Review +2ms
loopback:security:acl property * +1ms
loopback:security:acl principalType ROLE +1ms
loopback:security:acl principalId $everyone +1ms
loopback:security:acl accessType * +1ms
loopback:security:acl permission DENY +2ms
loopback:security:acl with score: +1ms 7495
loopback:security:acl ---ACL--- +1ms
loopback:security:acl model Review +1ms
loopback:security:acl property * +1ms
loopback:security:acl principalType ROLE +1ms
loopback:security:acl principalId $everyone +2ms
loopback:security:acl accessType READ +1ms
loopback:security:acl permission ALLOW +1ms
loopback:security:acl with score: +1ms -1
loopback:security:acl ---Resolved--- +1ms
loopback:security:access-context ---AccessRequest--- +1ms
loopback:security:access-context model Review +2ms
loopback:security:access-context property upsert +1ms
loopback:security:access-context accessType WRITE +1ms
loopback:security:access-context permission DENY +1ms
loopback:security:access-context isWildcard() false +2ms
loopback:security:access-context isAllowed() false +2ms

@mike-aungsan I added a note to https://docs.strongloop.com/display/LB/Accessing+related+models#Accessingrelatedmodels-note.
Please let me know if there's any issue with it.

@crandmck Many Thanks

Are you guys still running into issues? Can I close this?

The design seems undesirable, but as long as the design is documented (and it looks like it is from @crandmck post), I don't see a problem closing it. I worked around it in my project -- no longer an immediate concern.

@jdhiro Sounds good. Please open a new issue if you guys are still running into issues. Better yet, submit a patch and we can go from there.

Yes - i do

sorry for this question but how do I enable the debug view ?

"You can run the application with DEBUG enabled:

$ DEBUG=loopback:security:* node . "

Thanks,

sorry for this question but how do I enable the debug view ?

What do you mean debug view? Run DEBUG=loopback:security:* node . when starting your loopback app.

Hi,
got it working. Thx. U just run the application DEBUG=loopback:security:* node . on commandline .

somebody can help me ? with the same themes ?

if somebody can help me, I send the problem !!!

Execuse me, but on gitter nobody responds, and I cant resolve the problem, I dont know what I can do !!!

@emazzu @crandmck , i think this problem has come again in the latest repo. @raymondfeng , please look into it. This exposes the security loophole for the app in production.
Refer this thread https://github.com/strongloop/loopback/issues/3518

@emazzu , @crandmck @raymondfeng , this problem is only with custom access token. I reverted to loopback AccessToken model than it started working as intended.

Was this page helpful?
0 / 5 - 0 ratings