使用“隐式”流,客户端(可能是浏览器)将在资源所有者(即用户)授予访问权限后获得一个访问令牌。

然而,在“授权代码”流程中,客户端(通常是web服务器)只有在资源所有者(即用户)授予访问权限后才能获得授权代码。有了这个授权代码,客户端再一次调用API,将client_id和client_secret与授权代码一起传递,以获得访问令牌。这里都有详细的描述。

两个流都有完全相同的结果:一个访问令牌。然而,“隐式”流要简单得多。

问题是:当“隐式”流似乎工作得很好时,为什么要麻烦“授权代码”流呢?为什么不只是使用“隐式”的web服务器?

对于提供者和客户机来说,都需要做更多的工作。


这都是出于安全考虑。

OAuth 2.0希望满足以下两个标准:

您希望允许开发人员使用非https重定向URI,因为不是所有开发人员都有启用SSL的服务器,如果他们这样做,它并不总是正确配置(非自签名,受信任的SSL证书,同步的服务器时钟……)。 您不希望黑客能够通过拦截请求来窃取访问/刷新令牌。

细节如下:

由于安全原因,隐式流只能在浏览器环境中使用:

在隐式流中,访问令牌直接作为散列片段传递(而不是作为URL参数)。关于哈希片段的一个重要的事情是,一旦你跟随一个包含哈希片段的链接,只有浏览器知道这个哈希片段。浏览器会将散列片段直接传递到目标网页(重定向URI /客户端的网页)。哈希片段具有以下属性:

它们不是HTTP请求的一部分,因此它们不能被服务器读取,因此它们不能被中间服务器/路由器拦截(这很重要)。 它们只存在于浏览器(客户端)上,因此读取散列片段的唯一方法是使用运行在页面上的JavaScript。

这使得直接将访问令牌传递给客户端成为可能,而不会有被中间服务器拦截的风险。这只是可能的客户端,需要javascript运行客户端来使用访问令牌。

隐式流也有安全问题,需要进一步的逻辑来解决/避免,例如:

An attacker could get an access token from a user on a different website/app (let's say if he is the owner of the other website/app), log the token on their website, and then pass it as a URL param on your website therefore impersonating the user on your website. To avoid this you need to check the Client ID associated with the access token (for instance for Google you can use the tokeninfo endpoint) to make sure the token was issued with your own client ID (i.e by your own app) or check the signature if you are using an IDToken (but that requires your client secret). If the auth request did not originate from your own property (called Session Fixation attacks), to avoid this you'll want to generate a random hash from your website, save it in a cookie and pass that same hash in the state URL param of the auth request, when the user comes back you check the state param with the cookie and it must match.

在授权代码流中,直接在URL参数中传递访问令牌是不可能的,因为URL参数是HTTP请求的一部分,因此,如果您没有使用加密连接(HTTPS),允许所谓的中间人攻击,您的请求将通过的任何中间服务器/路由器(可能有数百个)都能够读取访问令牌。

Passing the access token directly in a URL param could in theory be possible but the auth sever would have to make sure the redirect URI is using HTTPS with TLS encryption and a 'trusted' SSL certificate (typically from a Certificate Authority that is not free) to be sure that the destination server is legitimate and that the HTTP request is fully encrypted. Having all developers purchase an SSL certificate and properly configure SSL on their domain would be a huge pain and would slow adoption down tremendously. This is why an intermediary one-time-use "authorization code" is provided that only the legitimate receiver will be able to exchange (because you need the client secret) and that the code will be useless to potential hackers intercepting the requests over unencrypted transactions (because they don't know the client secret).

您也可以认为隐式流不太安全,存在潜在的攻击向量,如重定向时欺骗域-例如通过劫持客户端网站的IP地址。这就是隐式流只授予访问令牌(应该有有限的时间使用)而从不刷新令牌(时间是无限的)的原因之一。为了解决这个问题,我建议你尽可能将你的网页托管在一个支持http的服务器上。


The Implicit Flow makes the whole flow pretty easy, but also less secure. As the client application, which is typically JavaScript running within a Browser is less trusted, no refresh tokens for long-lived access are returned. You should use this flow for applications that need temporary access (a few hours) to the user’s data. Returning an access token to JavaScript clients also means that your browser-based application needs to take special care – think of XSS Attacks that could leak the access token to other systems.

https://labs.hybris.com/2012/06/05/oauth2-the-implicit-flow-aka-as-the-client-side-flow


OAuth规范:

4.2. Implicit Grant The implicit grant type is used to obtain access tokens (it does not support the issuance of refresh tokens) and is optimized for public clients known to operate a particular redirection URI. These clients are typically implemented in a browser using a scripting language such as JavaScript. Since this is a redirection-based flow, the client must be capable of interacting with the resource owner's user-agent (typically a web browser) and capable of receiving incoming requests (via redirection) from the authorization server. Unlike the authorization code grant type, in which the client makes separate requests for authorization and for an access token, the client receives the access token as the result of the authorization request. The implicit grant type does not include client authentication, and relies on the presence of the resource owner and the registration of the redirection URI. Because the access token is encoded into the redirection URI, it may be exposed to the resource owner and other applications residing on the same device.

所以我们可以考虑:

This is for public OAuth i.e. when client not needed to be registered and doesn’t have it’s own client secrets. But what auth server checks redirect url and this is actually enough for security. The Access token occurs in browser’s address bar so user can copy the url and send to someone else and it also becomes logged as the user i.e. it’s something like Session fixation. But the browser make an additional redirect with replacing history to remove hash fragment from the url. It also possible to a hacker to stole the access token by sniffing a HTTP trafic but this can be easily protected by HTTPS. Some malicious browser extensions can have an access to urls from address bar but this is ultimately bad situation like broken HTTPS cert. And even Auth code flow can’t help here ether. So what I can see is that passing access token via hash fragment of url is absolutely safe. The separation of ephemeral access token and refresh token are useless when using a HTTPS and to be honest not so useful even on raw HTTP. But the fact that client via implicit flow can’t receive the refresh token is also nonsense.

因此,我认为我们应该引入一个新的授权流“安全隐式”,它严格地工作在https之上,允许刷新令牌(或者我们应该完全摆脱它们),并且比认证Cose授权流更可取


我的回答是:你不能在web应用服务器上以一种安全简单的方式实现隐式流。

web应用程序授权过程涉及用户交互,因此认证服务器应该在用户认证和同意后将用户的浏览器重定向到web应用程序的目标页面(我没有看到任何其他方法在与认证服务器交互后将用户传递回web应用程序)。

所以令牌应该通过web应用程序使用重定向URL,对吗?

正如@NicolasGarnier在他的回答和评论中解释的那样,没有办法将token作为URL片段传递-它不会到达web应用程序服务器。

并且将token作为重定向URL的URL参数即使在HTTPS下也是不安全的:如果目标页面(让它是“问候页面”)包含资源(图像,脚本等),这些资源将由浏览器通过一系列HTTP(S)请求获得(每个请求都有Referer HTTP头,包含“问候页面”的确切URL,包括URL参数)。这就是令牌泄漏的方式。

因此,似乎没有办法在重定向URL传递令牌。这就是为什么需要第二次调用(从身份验证服务器到客户端(但是到哪个URL?)或从客户端到身份验证服务器(授权代码流中的第二次调用))


For us, our clients wanted to be able to authenticate with our app on their phones once, and not have to log in again for weeks at a time. With code flow, you get a refresh token along with your access token. Implicit flow does not give you a refresh token. The access token has a relatively short expiration, but the refresh tokens can have up to a 90 day expiration. Whenever the access token expires, the client and server code can use that refresh token to get a new access token plus refresh token, all behind the scenes, without any user intervention whatsoever. A refresh token can only be used once. You cannot do this with Implicit Flow. If you're using Implicit Flow, and your user doesn't interact with your app for over an hour, they will have to log in again when they come back. That was not acceptable in our use case, and Code Flow supports our use case securely.

这是有效的,而且是安全的,因为可以撤销刷新令牌。如果客户说他们丢失了手机或笔记本电脑,或者黑客侵入了他们的桌面,我们可以简单地撤销该用户的所有刷新令牌。在整个过程中,没有任何个人身份信息(PII)触及我们的代码,即用户的密码。

代码流很棒,但它确实需要更多的工作。MS目前没有Angular的库来处理它,所以我不得不写一个。如果你感兴趣,我可以帮你。


在“隐式”流中,客户端(可能是浏览器)将通过浏览器重定向(get操作)获得访问令牌。基于浏览器的通信是不安全的,您的客户端机密或令牌可能被拦截或窃取。

在“授权码”流程中,客户端(通常是web服务器)仅通过浏览器重定向(get操作)获得授权码。然后服务器通过对授权服务器进行POST调用(非浏览器),将此代码与令牌交换。服务器包含客户端秘密仅用于令牌访问调用。

注意:根据oauth最佳实践,“客户端不应该使用隐式授权(响应类型“令牌”)或其他响应类型颁发访问权限 授权响应中的令牌”。

希望这能有所帮助。


因为谷歌员工:

你授权第三方访问你的Gmail联系人 访问权限以令牌的形式授予 任何拥有有效令牌的人都将获得访问权限 因此,您不希望公开令牌,并最小化其传输 使用隐式流,(不受控制的)浏览器获得访问令牌,从而将令牌放在公共位置 使用认证代码流,浏览器只能获得一个临时的认证代码,但从来没有访问令牌,而且如果没有只有第三方和Gmail知道的秘密,认证代码是无用的

结论

For an attacker to gain access to your Gmail contacts, it must break into your 3rd party account However, the attacker never gets a hold on the access token thus is unable to perform operations to your Gmail contacts directly You may authorize a 3rd party to access many services, so you do not want to store all the important tokens locally on your computer However, there is one scenario you can only use implicit flow: when the 3rd party runs locally, and does not have a backend to store tokens Then it can only rely on the front-end to store the tokens, which it has little control

比喻

Implicit flow: YOU ask PROVIDER for a key, YOU store it in your wallet, YOU are responsible to keep it safe, YOU use the key directly with care, and YOU exchange it for a new key in time Auth code flow: YOU ask for a code, code is handed to your VALET, your VALET combines the code and a secret text then exchange it for a key with PROVIDER, YOU ask your VALET to use the key when needed but never sees the key yourself, and your VALET is responsible to exchange new keys Most of the time your VALET is more security-aware than YOU :) When YOU do not have a VALET, YOU are on your own


谈论授权代码授予类型,我们有更多的安全性,通过删除客户端(用户代理或网站)对终端资源的特权访问,其中客户端(网站所有者)假装是你使用授权代码,也可以避免黑客在你的浏览器上使用XSS(网站漏洞)的CRSF(如果使用隐式方法可能会发生)。

关键成分是在向认证服务器发出的第一个请求中使用的客户端id。您可以将验证码步骤视为签名验证。

Moreover, even after finished with the Authorization code steps in acquiring an Access token, ultimately, the access_token ends up in the hands of the client. At that point there is no need to have a client-id for signature verification anymore with the Auth Server. So, I am not sure if Authorization Code method is also perfectly safe (from the client itself). Which is why you see the Auth Server asks you for Consent even after providing the login credentials. Meaning you trust the client with your Access Tokens.