【问题标题】:Oauth2 Node Passport Authorization header error (Anonymous user)Oauth2 节点护照授权标头错误(匿名用户)
【发布时间】:2016-07-22 11:24:14
【问题描述】:

最近升级了我们连接的 Oauth 2 服务器。为了让我们遵守并获得新数据,我们需要对这个新服务器进行身份验证。

以前的服务器在我将要布置的代码上运行良好,但是在新服务器上,当我们完全验证并得到一致的错误时。

这是错误

app.js

所有的护照魔法都在这里发生

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var config = require('./config.js')
var passport = require('passport');
var OAuth2Strategy = require('passport-oauth').OAuth2Strategy;
var session = require('express-session');
var async = require('async');

var index = require('./routes/index');
var users = require('./routes/users');
var patientdata = require('./routes/patient');
var account = require('./routes/account');
var logout = require('./routes/logout');

var about = require('./routes/about');


// serialize and deserialize
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});

// config

passport.use('vendor', new OAuth2Strategy({
    authorizationURL: config.vendor.authorizationURL,
    tokenURL: config.vendor.tokenURL,
    clientID: config.vendor.clientID,
    clientSecret: config.vendor.clientSecret,
    callbackURL: config.vendor.callbackURL,
    passReqToCallback: true
    },
    function(req, accessToken, refreshToken, profile, done) {
        process.nextTick(function () {
            // store access token
            req.session.accessToken=accessToken;
            return done(null, profile);
        });
    }
));

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({secret: 'secretword'}))
app.use(passport.initialize());
app.use(passport.session());

app.get('/', index.execute);

app.get('/users', users.execute);
app.get('/account', ensureAuthenticated, account.execute);

app.get('/logout', logout.execute);

// vendor 
app.get('/auth/vendor',
passport.authenticate('vendor'),
function(req, res){
});

app.get('/auth/vendor/callback',
passport.authenticate('vendor', { failureRedirect: '/' }),
function(req, res) {
 res.redirect('/account');
}); 

// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

function ensureAuthenticated(req, res, next) {
    if (req.isAuthenticated()) {
        return next();
    }
    res.redirect('/')
}

module.exports = app;

URL、ID、秘密等信息正确,正在由供应商验证。

我们似乎已通过 Oauth2 服务器的身份验证,并且我们正在获得一个批准的令牌,但是我们在他们的日志中显示为“匿名”用户。

这里是来自供应商的日志文件。

我的应用程序尝试进​​行身份验证的服务器日志

-- Us starting request for token
...
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.s.w.h.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@77bd892d
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/oauth/token'; against '/logout'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 5 of 11 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@6faab5ec: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffffc434: RemoteIpAddress: 68.46.8.80; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
Break -- returns anonymous

09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/oauth/token'; against '/oauth/token'
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /oauth/token; Attributes: [fullyAuthenticated]
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@6faab5ec: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffffc434: RemoteIpAddress: 68.46.8.80; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
09:43:47.974 [http-nio-8282-exec-2] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@73821302, returned: -1
09:43:47.975 [http-nio-8282-exec-2] DEBUG o.s.b.a.audit.listener.AuditListener - AuditEvent [timestamp=Thu Mar 24 09:43:47 CDT 2016, principal=anonymousUser, type=AUTHORIZATION_FAILURE, data={type=org.springframework.security.access.AccessDeniedException, message=Access is denied}]
09:43:47.975 [http-nio-8282-exec-2] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point
...

我要求供应商使用他们制作的测试应用程序登录并将日志发送给我,见下文,他们有一个“基本身份验证标头”

供应商管理应用验证的服务器日志

Break -- Starting  admin request for token
...
10:07:48.560 [http-nio-8282-exec-3] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
10:07:48.560 [http-nio-8282-exec-3] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
10:07:48.560 [http-nio-8282-exec-3] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
10:07:48.560 [http-nio-8282-exec-3] DEBUG o.s.s.w.h.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@77bd892d
10:07:48.560 [http-nio-8282-exec-3] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
10:07:48.560 [http-nio-8282-exec-3] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/oauth/token'; against '/logout'
10:07:48.560 [http-nio-8282-exec-3] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 5 of 11 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
10:07:48.562 [http-nio-8282-exec-3] DEBUG o.s.s.w.a.w.BasicAuthenticationFilter - Basic Authentication Authorization header found for user ' admin'
10:07:48.562 [http-nio-8282-exec-3] DEBUG o.s.s.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
10:07:48.563 [http-nio-8282-exec-3] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'scopedTarget.clientDetailsService'
10:07:48.564 [http-nio-8282-exec-3] DEBUG o.s.s.w.a.w.BasicAuthenticationFilter - Authentication success: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@36677da1: Principal: org.springframework.security.core.userdetails.User@664f353: Username:  admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_CLIENT; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff6a82: RemoteIpAddress: 66.41.28.185; SessionId: null; Granted Authorities: ROLE_CLIENT
10:07:48.580 [http-nio-8282-exec-3] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
10:07:48.580 [http-nio-8282-exec-3] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
10:07:48.580 [http-nio-8282-exec-3] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
10:07:48.580 [http-nio-8282-exec-3] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@36677da1: Principal: org.springframework.security.core.userdetails.User@664f353: Username:  admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_CLIENT; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff6a82: RemoteIpAddress: 66.41.28.185; SessionId: null; Granted Authorities: ROLE_CLIENT'
10:07:48.580 [http-nio-8282-exec-3] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
10:07:48.580 [http-nio-8282-exec-3] DEBUG o.s.s.w.a.s.CompositeSessionAuthenticationStrategy - Delegating to org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy@285827ac
10:07:48.580 [http-nio-8282-exec-3] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
10:07:48.580 [http-nio-8282-exec-3] DEBUG o.s.security.web.FilterChainProxy - /oauth/token at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
10:07:48.580 [http-nio-8282-exec-3] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/oauth/token'; against '/oauth/token'
10:07:48.580 [http-nio-8282-exec-3] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /oauth/token; Attributes: [fullyAuthenticated]
10:07:48.580 [http-nio-8282-exec-3] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@36677da1: Principal: org.springframework.security.core.userdetails.User@664f353: Username:  admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_CLIENT; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff6a82: RemoteIpAddress: 66.41.28.185; SessionId: null; Granted Authorities: ROLE_CLIENT
10:07:48.581 [http-nio-8282-exec-3] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@73821302, returned: 1
10:07:48.581 [http-nio-8282-exec-3] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Authorization successful
...

我们知道我们没有发送“授权:基本(随机代码)”,但我们无法手动将其注入。我的印象是护照为我们做了这个,但似乎不是。

最后,我们返回并检查了旧服务器,并确认我们之前从未在标头中发送过此 BASIC 代码。我不确定服务器以前是怎么发现这个问题的,但我们需要尽快升级,并且一直在努力解决这个问题。

【问题讨论】:

  • 嗨@Kent0111。我已经修剪了“感谢所有帮助”,就像我们修剪“你好”、“谢谢”、“亲切的问候”等一样——它很好而且很有礼貌,但这只是噪音,我们更喜欢这里简洁。我还删除了“尽快”,因为社区已经厌倦了试图将自己的紧迫感转移到志愿者身上的海报(并不是说你自己这样做)。如果您决定再次回滚,请告诉我,以便我邀请管理员。

标签: node.js authentication oauth-2.0 passport.js


【解决方案1】:

因此,对于 OAuth2,预期的令牌标头是 Authorization: Bearer 标头。您可以在此处查看:规范文档中的https://www.rfc-editor.org/rfc/rfc6750#section-2.1

在这种情况下,如果供应商正在寻找 Basic 标头,则他们是错误的。引擎盖下的 passport.js oauth2 策略是利用以下 node.js 模块:https://github.com/ciaranj/node-oauth

要真正开始更深入地研究这一点,您可能需要手动实现一个示例,以查看问题在哪里“卡住”了。但是,在查看此内容时,您可以将标头从不记名令牌更改为 node-oauth 库中的基本令牌:https://github.com/ciaranj/node-oauth/blob/master/lib/oauth2.js#L15

为此,我们需要更改一些代码并访问一些可能更容易更改的区域,因为我们正在访问内部属性。

首先,改变策略,将其推入变量中:

var oauthStrategy = new OAuth2Strategy({
    authorizationURL: config.vendor.authorizationURL,
    tokenURL: config.vendor.tokenURL,
    clientID: config.vendor.clientID,
    clientSecret: config.vendor.clientSecret,
    callbackURL: config.vendor.callbackURL,
    passReqToCallback: true
    },
    function(req, accessToken, refreshToken, profile, done) {
        process.nextTick(function () {
            // store access token
            req.session.accessToken=accessToken;
            return done(null, profile);
        });
    }
);

然后,我们需要访问“受保护”属性(它并没有真正受到保护,因为 javascript 无法强制执行) - 更多信息请参阅:https://github.com/jaredhanson/passport-oauth2/blob/master/lib/strategy.js#L91

oauthStrategy._oauth2.setAuthMethod('BASIC');

现在重新组合策略:

passport.use('vendor', oauthStrategy);

我还没有实际测试过这个,但是通过查看源代码这应该对你有用,或者至少让你走上正确的道路。

【讨论】:

  • 谢谢!这与一些小的语法更改效果很好!
【解决方案2】:

Mwillbank,您对您解决的问题是正确的,但这里的问题在不同的领域。我在这个问题上与供应商合作。问题在于在授权码流期间对授权服务器的 getAccessToken 调用中的授权标头。您引用的标准是关于如何在拥有访问令牌后从资源服务器请求数据。对于那个电话,您是绝对正确的,我们按照您所引用的那样实施该标准。适用于我们情况的 OAuth2 标准部分是主要标准 (rfc6749) 的 Section 4.1.3。本节引用了关于客户端身份验证的第 3.2.1 节,该节又引用了Section 2.3, also on Client Authentication。在这里,您将看到对于机密客户端的客户端身份验证没有强制性标准。但是,在下一页的 2.3.1 中给出的一个示例是使用基本身份验证。我相信这是在 authentication_code 流程的 getAccessToken 调用期间对客户端进行身份验证的默认方法,也是最常用的方法。

也许问题的一部分是,passport.js 假设这种情况涉及移动应用程序和 Web 服务器之间的implicit_grant 流,其中没有对 getAccessToken 的单独调用。我注意到您在链接中发表的评论说护照实施主要是 OAuth1,对 OAuth2 的支持参差不齐。也许我们遇到了错过的那些地方之一。尽管如此,我发现您的帖子有助于更好地了解护照的工作原理。谢谢。

【讨论】:

    猜你喜欢
    • 2018-09-26
    • 2018-02-19
    • 1970-01-01
    • 1970-01-01
    • 2017-10-18
    • 1970-01-01
    • 1970-01-01
    • 2020-03-08
    • 2020-03-27
    相关资源
    最近更新 更多