RFC 8252
OAuth 2.0 for Native Apps
RFC 8252: OAuth 2.0 for Native Apps π
Okay bestie, first the paperwork at the top π
- Stream: Internet Engineering Task Force (IETF)
- Authors: W. Denniss (Google) and J. Bradley (Ping Identity) π«Ά
- Category: Best Current Practice, which is BCP 212 π
- Date: October 2017
- ISSN: 2070-1721
- Updates: 6749 (that's RFC 6749) β¨
- Obsoletes: nothing listed
Abstract πΈ
Okay so the big idea: OAuth 2.0 authorization requests from native apps should only be made through external user-agents, and the main one is the user's browser π. This spec explains the security and usability reasons why, and how native apps and authorization servers can actually do this best practice. It's giving "do it right, bestie."
Status of This Memo β¨
This memo documents an Internet Best Current Practice.
This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). More info on BCPs is in Section 2 of RFC 7841.
If you want the current status of this doc, any errata, or a way to send feedback, go to https://www.rfc-editor.org/info/rfc8252 π¦
Copyright Notice π
Copyright (c) 2017 IETF Trust and the persons identified as the document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, because they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they are provided without warranty as described in the Simplified BSD License.
Table of Contents π
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
2. Notational Conventions . . . . . . . . . . . . . . . . . . . 3
3. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 3
4. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . 4
4.1. Authorization Flow for Native Apps Using the Browser . . 5
5. Using Inter-App URI Communication for OAuth . . . . . . . . . 6
6. Initiating the Authorization Request from a Native App . . . 6
7. Receiving the Authorization Response in a Native App . . . . 7
7.1. Private-Use URI Scheme Redirection . . . . . . . . . . . 8
7.2. Claimed "https" Scheme URI Redirection . . . . . . . . . 9
7.3. Loopback Interface Redirection . . . . . . . . . . . . . 9
8. Security Considerations . . . . . . . . . . . . . . . . . . . 10
8.1. Protecting the Authorization Code . . . . . . . . . . . . 10
8.2. OAuth Implicit Grant Authorization Flow . . . . . . . . . 11
8.3. Loopback Redirect Considerations . . . . . . . . . . . . 11
8.4. Registration of Native App Clients . . . . . . . . . . . 12
8.5. Client Authentication . . . . . . . . . . . . . . . . . . 12
8.6. Client Impersonation . . . . . . . . . . . . . . . . . . 13
8.7. Fake External User-Agents . . . . . . . . . . . . . . . . 13
8.8. Malicious External User-Agents . . . . . . . . . . . . . 14
8.9. Cross-App Request Forgery Protections . . . . . . . . . . 14
8.10. Authorization Server Mix-Up Mitigation . . . . . . . . . 14
8.11. Non-Browser External User-Agents . . . . . . . . . . . . 15
8.12. Embedded User-Agents . . . . . . . . . . . . . . . . . . 15
9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 16
10. References . . . . . . . . . . . . . . . . . . . . . . . . . 16
10.1. Normative References . . . . . . . . . . . . . . . . . . 16
10.2. Informative References . . . . . . . . . . . . . . . . . 17
Appendix A. Server Support Checklist . . . . . . . . . . . . . . 18
Appendix B. Platform-Specific Implementation Details . . . . . . 18
B.1. iOS Implementation Details . . . . . . . . . . . . . . . 18
B.2. Android Implementation Details . . . . . . . . . . . . . 19
B.3. Windows Implementation Details . . . . . . . . . . . . . 19
B.4. macOS Implementation Details . . . . . . . . . . . . . . 20
B.5. Linux Implementation Details . . . . . . . . . . . . . . 21
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . 21
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 21
1. Introduction β¨
Okay so Section 9 of the OAuth 2.0 authorization framework [RFC6749] describes two ways native apps can talk to the authorization endpoint: an embedded user-agent and an external user-agent π§.
This best current practice requires that only external user-agents like the browser get used for OAuth by native apps. It explains how native apps can run authorization flows using the browser as the preferred external user-agent, plus what authorization servers need to support that. Main character energy π
People also call this the "AppAuth pattern," named after the open-source libraries [AppAuth] that implement it π
2. Notational Conventions π
Here's the key-word rule. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. Lowercase versions don't count, so no accidental vibes π
3. Terminology π
Besides the terms defined in the referenced specs, this doc uses the following terms π
- "native app": An app or application that's installed by the user on their device, as opposed to a web app that only runs in the browser context. Apps built with web tech but distributed as a native app, so-called "hybrid apps," count as equivalent to native apps for this spec π±
- "app": A "native app" unless further specified πΈ
- "app store": An e-commerce store where users can download and purchase apps ποΈ
- "OAuth": The authorization protocol specified by the OAuth 2.0 Authorization Framework [RFC6749] π
- "external user-agent": A user-agent that can handle the authorization request, and that is a separate entity or security domain from the native app making the request. So the app can't access the cookie storage, and it can't inspect or modify page content π¦
- "embedded user-agent": A user-agent hosted by the native app making the authorization request. It's part of the app or shares the same security domain, so the app can access the cookie storage and/or inspect or modify page content π
- "browser": The default application the operating system launches to handle "http" and "https" scheme URI content π
- "in-app browser tab": A programmatic version of the browser that shows up inside a host app but keeps the full security properties and authentication state of the browser. It has different platform-specific product names, several of which are detailed in Appendix B β¨
- "web-view": A web browser UI (user interface) component embedded in apps to render web pages under the app's control πΌοΈ
- "inter-app communication": Communication between two apps on a device π€
- "claimed "https" scheme URI": Some platforms let apps claim an "https" scheme URI after proving they own the domain name. URIs claimed this way open in the app instead of the browser π
- "private-use URI scheme": As used by this document, a URI scheme defined by the app (following the requirements of Section 3.8 of [RFC7595]) and registered with the operating system. URI requests to such schemes launch the app that registered it to handle the request π§
- "reverse domain name notation": A naming convention based on the domain name system, but with the domain components reversed. For example, "app.example.com" becomes "com.example.app" π
4. Overview πΈ
Okay so for authorizing users in native apps, the best current practice is to do the OAuth authorization request in an external user-agent (typically the browser), not an embedded user-agent (like one built with web-views) ππ
Embedded user-agents, usually built with web-views, used to be super common for OAuth authorization requests in native apps. That approach has a lot of drawbacks, like the host app being able to copy user credentials and cookies, and the user having to authenticate from scratch in every app π Section 8.12 has a deeper analysis of those drawbacks for OAuth.
Native app authorization requests that use the browser are more secure, and they can use the user's existing authentication state π₯. Being able to reuse the browser's authentication session gives you single sign-on, since users don't have to authenticate to the authorization server every time they open a new app (unless the authorization server policy requires it) β¨
Supporting authorization flows between a native app and the browser works without changing the OAuth protocol itself, because the OAuth authorization request and response are already defined in terms of URIs. That includes URIs that can be used for inter-app communication π¦ Some OAuth server implementations assume all clients are confidential web clients, and those will need to understand public native app clients and the types of redirect URIs they use to support this best practice π«Ά
Next up in part 2: Section 4.1, where the actual flow gets its moment π
4.1. Authorization Flow for Native Apps Using the Browser β¨
Okay bestie, here's the whole flow for native apps using the browser π The app and the browser pass the baton back and forth, and the authorization server and token endpoint each have their own job π
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| User Device |
| |
| +--------------------------+ | (5) Authorization +---------------+
| | | | Code | |
| | Client App |---------------------->| Token |
| | |<----------------------| Endpoint |
| +--------------------------+ | (6) Access Token, | |
| | ^ | Refresh Token +---------------+
| | | |
| | | |
| | (1) | (4) |
| | Authorizat- | Authoriza- |
| | ion Request | tion Code |
| | | |
| | | |
| v | |
| +---------------------------+ | (2) Authorization +---------------+
| | | | Request | |
| | Browser |--------------------->| Authorization |
| | |<---------------------| Endpoint |
| +---------------------------+ | (3) Authorization | |
| | Code +---------------+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
Figure 1: Native App Authorization via an External User-Agent
Figure 1 shows the interaction between a native app and the browser when it comes to authorizing the user π¦
- The client app pops open a browser tab with the authorization request πΈ
- The authorization endpoint receives the authorization request, authenticates the user, and obtains authorization π Authenticating the user may involve chaining to other authentication systems, so it can get pretty layered ngl.
- The authorization server issues an authorization code to the redirect URI π
- The client receives the authorization code from the redirect URI β¨
- The client app presents the authorization code at the token endpoint π
- The token endpoint validates the authorization code and issues the tokens requested π
5. Using Inter-App URI Communication for OAuth π
Okay so, URIs are how OAuth 2.0 [RFC6749] works on the web. A website uses them to kick off the authorization request and to get the authorization response sent back to it. Native apps can do the exact same thing, so they can start the authorization request in the device's browser and have the response come back to the app that asked for it π¦
Using the same methods that the web uses for OAuth means the good stuff carries over too. You still get the usability of a single sign-on session and the security of a separate authentication context, which is so iconic. Reusing the same approach also cuts down on implementation complexity and boosts interoperability, since it leans on standards-based web flows that aren't tied to one particular platform π
To conform to this best practice, native apps MUST use an external user-agent to perform OAuth authorization requests. This happens by opening the authorization request in the browser (more on that in Section 6) and using a redirect URI that returns the authorization response back to the native app (defined in Section 7) π«Ά
6. Initiating the Authorization Request from a Native App π
Native apps that need user authorization create an authorization request URI with the authorization code grant type, per Section 4.1 of OAuth 2.0 [RFC6749]. They use a redirect URI that the native app is able to receive π
The redirect URI does the same job here as it does in a web-based authorization request, just with a twist. Instead of sending the authorization response back to the OAuth client's server, the redirect URI used by a native app sends the response to the app itself π§ There are several options for a redirect URI that returns the authorization response to a native app on different platforms, and they're all covered in Section 7. Any redirect URI that lets the app receive the URI and inspect its parameters is viable β¨
Public native app clients MUST implement the Proof Key for Code Exchange (PKCE [RFC7636]) extension to OAuth, and authorization servers MUST support PKCE for such clients, for the reasons detailed in Section 8.1. Main character energy for security π
After building the authorization request URI, the app uses platform-specific APIs to open that URI in an external user-agent. Usually the external user-agent is the default browser, which is the application set up to handle "http" and "https" scheme URIs on the system. Different browser selection criteria and other categories of external user-agents MAY be used, though πΈ
This best practice is all about the browser, which is the RECOMMENDED external user-agent for native apps π An external user-agent that's designed specifically for user authorization and can process authorization requests and responses the way a browser does MAY also be used. Other external user-agents, such as a native app provided by the authorization server, may meet the criteria set out in this best practice (including using the same redirection URI properties), but their use is out of scope for this specification π
Some platforms have a browser feature called "in-app browser tabs". With it, an app can show a tab of the browser inside the app without switching apps, and it still keeps the big browser perks like a shared authentication state and security context π¦ On platforms that support them, it is RECOMMENDED, for usability reasons, that apps use in-app browser tabs for the authorization request π
7. Receiving the Authorization Response in a Native App π
There are a few redirect URI options that native apps can use to receive the authorization response from the browser. How available they are and how they feel for the user both depend on the platform π«§
To fully support this best practice, authorization servers MUST offer at least the three redirect URI options described in the following subsections to native apps. Native apps MAY use whichever redirect option suits their needs best, taking into account platform-specific implementation details π
7.1. Private-Use URI Scheme Redirection π
Lots of mobile and desktop platforms support inter-app communication through URIs. They let apps register private-use URI schemes (sometimes called "custom URL schemes") like "com.example.app". When the browser or another app tries to load a URI with a private-use URI scheme, the app that registered it gets launched to handle the request π
To do an OAuth 2.0 authorization request with a private-use URI scheme redirect, the native app launches the browser with a standard authorization request. The difference is that the redirection URI uses a private-use URI scheme that the app registered with the operating system π¦
When choosing a URI scheme to associate with the app, apps MUST use a URI scheme based on a domain name under their control, expressed in reverse order, as recommended by Section 3.8 of [RFC7595] for private-use URI schemes.
For example, an app that controls the domain name "app.example.com" can use "com.example.app" as its scheme. Some authorization servers assign client identifiers based on domain names, like "client1234.usercontent.example.net". Those can also be used as the domain name for the scheme when reversed in the same manner. A scheme such as "myapp", however, would not meet this requirement, because it isn't based on a domain name π ββοΈ
When there are multiple apps from the same publisher, care must be taken so that each scheme is unique within that group. On platforms that use app identifiers based on reverse-order domain names, those identifiers can be reused as the private-use URI scheme for the OAuth redirect, which helps avoid this problem π§©
Following the requirements of Section 3.2 of [RFC3986], only a single slash ("/") appears after the scheme component, since there's no naming authority for private-use URI scheme redirects. A complete example of a redirect URI using a private-use URI scheme is:
com.example.app:/oauth2redirect/example-provider
When the authorization server finishes the request, it redirects to the client's redirection URI like it normally would. Because that redirection URI uses a private-use URI scheme, the operating system launches the native app and passes in the URI as a launch parameter. Then the native app uses its normal processing for the authorization response π
7.2. Claimed "https" Scheme URI Redirection πΈ
Some operating systems let apps claim "https" scheme [RFC7230] URIs for the domains they control. When the browser runs into a claimed URI, the page doesn't load in the browser. Instead, the native app is launched with the URI supplied as a launch parameter πͺ
These URIs can be used as redirect URIs by native apps, and to the authorization server they look exactly like a regular web-based client redirect URI. Here's an example:
https://app.example.com/oauth2redirect/example-provider
The redirect URI alone isn't enough to tell public native app clients apart from confidential web clients, so it is REQUIRED in Section 8.4 that the client type be recorded during client registration. That way the server can figure out the client type and act accordingly π¨
App-claimed "https" scheme redirect URIs have some perks over the other native app redirect options. The operating system guarantees the identity of the destination app to the authorization server, which is why native apps SHOULD use them over the other options where possible π
7.3. Loopback Interface Redirection π¦
Native apps that can open a port on the loopback network interface without needing special permissions (usually the ones on desktop operating systems) can use the loopback interface to receive the OAuth redirect π₯οΈ
Loopback redirect URIs use the "http" scheme and are built with the loopback IP literal plus whatever port the client is listening on π
That is, "http://127.0.0.1:{port}/{path}" for IPv4, and "http://[::1]:{port}/{path}" for IPv6. Here's an example redirect using the IPv4 loopback interface with a randomly assigned port:
http://127.0.0.1:51004/oauth2redirect/example-provider
And here's an example redirect using the IPv6 loopback interface with a randomly assigned port:
http://[::1]:61023/oauth2redirect/example-provider
The authorization server MUST allow any port to be specified at the time of the request for loopback IP redirect URIs, so clients can grab an available ephemeral port from the operating system right when they make the request. That's a must-have, no cap π§
Clients SHOULD NOT assume that the device supports a particular version of the Internet Protocol. It is RECOMMENDED that clients attempt to bind to the loopback interface using both IPv4 and IPv6, and use whichever one is available π
8. Security Considerations π
8.1. Protecting the Authorization Code π
Okay so the redirect URI options in Section 7 all have one big perk: only a native app on the same device or the app's own website can catch the authorization code, which shrinks the attack surface π . Still, a different native app on the same device might be able to intercept the code π¬.
Private-use URI schemes have a catch. Multiple apps can usually register the same scheme, so it's totally unclear which app gets the authorization code π. Section 1 of PKCE [RFC7636] explains how this gap can be used to pull off a code interception attack.
Loopback IP-based redirect URIs may be vulnerable to interception by other apps that access the same loopback interface on some operating systems π«£.
App-claimed "https" scheme redirects are harder to intercept thanks to the URI authority π¦. But the app is still a public client, and the URI gets sent through the operating system's URI dispatch handler, which has unknown security properties β¨.
PKCE [RFC7636] was literally built to stop this exact attack π. It's a proof-of-possession extension to OAuth 2.0 that keeps the authorization code from being used if someone intercepts it. The client generates a secret verifier, passes a hash of it in the initial authorization request, and has to present the unhashed verifier when redeeming the code. An app that grabbed the code doesn't have that secret, so the code is useless π.
Section 6 requires that both clients and servers use PKCE for public native app clients. Authorization servers SHOULD reject native app authorization requests that don't use PKCE by returning an error message, as defined in Section 4.4.1 of PKCE [RFC7636] π .
8.2. OAuth Implicit Grant Authorization Flow πΈ
The OAuth 2.0 implicit grant flow (defined in Section 4.2 of OAuth 2.0 [RFC6749]) generally fits the pattern of doing the authorization request in the browser and getting the response back through URI-based inter-app communication. But bestie, the implicit flow can't be protected by PKCE [RFC7636], and PKCE is required in Section 8.1. So using the Implicit Flow with native apps is NOT RECOMMENDED π«.
Access tokens from the implicit flow also can't be refreshed without user interaction. That makes the authorization code grant flow, which can issue refresh tokens, the more practical pick for native app authorizations that need to refresh access tokens π.
8.3. Loopback Redirect Considerations π
Loopback interface redirect URIs use the "http" scheme, meaning no Transport Layer Security (TLS). That's acceptable here because the HTTP request never leaves the device π .
Clients should open the network port only when they start the authorization request, and close it once the response comes back β°.
Clients should listen on the loopback network interface only, so other network actors can't interfere π‘οΈ.
Redirect URIs using localhost (http://localhost:{port}/{path}) work similarly to the loopback IP redirects in Section 7.3, but using localhost is NOT RECOMMENDED. Using the loopback IP literal avoids accidentally listening on network interfaces other than the loopback one. It's also less vulnerable to client-side firewalls and misconfigured host name resolution on the user's device π.
8.4. Registration of Native App Clients π
Unless you're using something like Dynamic Client Registration [RFC7591] to provision per-instance secrets, native apps count as public clients, as defined in Section 2.1 of OAuth 2.0 [RFC6749]. They MUST be registered with the authorization server as public clients π. Authorization servers MUST record the client type in the client registration details so they can identify and process requests accordingly.
Authorization servers MUST require clients to register their complete redirect URI, path component included. They also have to reject any authorization request whose redirect URI doesn't exactly match the registered one. The one exception is loopback redirects, where everything has to match exactly except the port π.
For private-use URI scheme-based redirects, authorization servers SHOULD enforce the Section 7.1 rule that clients use reverse domain name based schemes. At a minimum, any private-use URI scheme that doesn't contain a period character (.) SHOULD be rejected π«.
Beyond collision resistance, requiring a scheme based on a domain the app controls helps prove ownership in a dispute where two apps claim the same private-use scheme and one is acting maliciously. For example, if two apps both claimed com.example.app, the owner of example.com could petition the app store operator to remove the counterfeit app π΄ββ οΈ. That petition is way harder to prove if a generic URI scheme was used π€.
Authorization servers MAY request other platform-specific information, like the app package or bundle name, or anything else that's useful for verifying the calling app's identity on operating systems that support those functions π§©.
8.5. Client Authentication π
Secrets that are statically included in an app distributed to multiple users should not be treated as confidential, since one user can inspect their copy and learn the shared secret π. For this reason, and for those stated in Section 5.3.1 of [RFC6819], it is NOT RECOMMENDED for authorization servers to require client authentication of public native app clients using a shared secret. It adds very little beyond client identification, which the "client_id" request parameter already provides π .
Authorization servers that still require a statically included shared secret for native app clients MUST treat the client as a public client (as defined in Section 2.1 of OAuth 2.0 [RFC6749]), and not accept the secret as proof of the client's identity. Without additional measures, these clients are open to client impersonation (see Section 8.6) π°.
8.6. Client Impersonation π
As Section 10.2 of OAuth 2.0 [RFC6749] states, the authorization server SHOULD NOT process authorization requests automatically without user consent or interaction, except when the client's identity can be assured. That includes the case where the user has already approved an authorization request for a given client id. Unless the client's identity can be proven, the request SHOULD be processed as if no previous request had been approved π .
Measures such as claimed "https" scheme redirects MAY be accepted by authorization servers as identity proof. Some operating systems may offer alternative platform-specific identity features that MAY be accepted, as appropriate β¨.
8.7. Fake External User-Agents π΅οΈ
The native app starting the authorization request has a lot of control over the user interface. It can potentially show a fake external user-agent, which is an embedded user-agent made to look like an external one π.
When all the good actors use external user-agents, security experts can spot the bad ones, because anyone faking an external user-agent is provably bad π©. But if good and bad actors alike use embedded user-agents, the bad actors don't need to fake anything, which makes them way harder to detect π¬. Once a malicious app is detected, that knowledge can be used to blacklist its signature in malware scanning software, take removal action (for apps distributed by app stores), and take other steps to reduce the impact and spread of the app π§Ή.
Authorization servers can also directly protect against fake external user-agents by requiring an authentication factor that's only available to true external user-agents π.
Users who are particularly worried about their security in in-app browser tabs can open the request in the full browser from the in-app tab and finish the authorization there. Most in-app browser tab implementations offer that option π.
8.8. Malicious External User-Agents π
If a malicious app can configure itself as the default handler for "https" scheme URIs in the operating system, it can intercept authorization requests that use the default browser. It can then abuse that trust for shady stuff like phishing the user π£.
This attack isn't confined to OAuth. A malicious app configured this way would be a general, ongoing risk to users beyond native app OAuth usage. Many operating systems mitigate this by requiring an explicit user action to change the default handler for "http" and "https" scheme URIs π.
8.9. Cross-App Request Forgery Protections π‘οΈ
Section 5.3.5 of [RFC6819] recommends using the "state" parameter to link client requests and responses, which prevents CSRF (Cross-Site Request Forgery) attacks π.
To mitigate CSRF-style attacks over inter-app URI communication channels (so called "cross-app request forgery"), native apps are similarly RECOMMENDED to include a high-entropy secure random number in the "state" parameter of the authorization request, and to reject any incoming authorization response without a state value that matches a pending outgoing authorization request π―.
8.10. Authorization Server Mix-Up Mitigation π₯£
To protect against a compromised or malicious authorization server attacking another authorization server used by the same app, it is REQUIRED that a unique redirect URI is used for each authorization server the app uses (for example, by varying the path component). Authorization responses are also rejected if the redirect URI they were received on doesn't match the redirect URI in an outgoing authorization request π§.
The native app MUST store the redirect URI used in the authorization request with the authorization session data (along with "state" and other related data), and MUST verify that the URI on which the authorization response was received exactly matches it π.
The requirement of Section 8.4, specifically that authorization servers reject requests with URIs that don't match what was registered, is also required to prevent such attacks π.
8.11. Non-Browser External User-Agents π
This best practice recommends one particular type of external user-agent: the user's browser. Other external user-agent patterns may also be viable for secure and usable OAuth. This document makes no comment on those patterns π€·.
8.12. Embedded User-Agents π
okay bestie, so Section 9 of OAuth 2.0 [RFC6749] gives native apps two ways to talk to the authorization endpoint π¦. This best current practice says native apps MUST NOT use embedded user-agents to make authorization requests π ββοΈ. It also lets authorization endpoints MAY step in and detect and block authorization requests that come from embedded user-agents π‘οΈ. The security reasons behind all of this are coming up below β¨.
Embedded user-agents are an alternative way to authorize native apps, and they are unsafe for third parties to the authorization server, by definition π. The app hosting the embedded user-agent can see the user's entire authentication credential, not just the OAuth authorization grant that was meant for the app. That's a whole lot more than the app should ever get to see π.
In typical web-view-based embedded user-agents, the host app can record every keystroke in the login form to grab usernames and passwords β¨οΈπ«£. It can also submit forms automatically to skip user consent, and copy session cookies to take authenticated actions as the user. Not it π.
Even when the app is a trusted one from the same party as the authorization server, embedded user-agents break the principle of least privilege π. The app ends up with way more powerful credentials than it needs, which can make the attack surface bigger π―.
Asking users to type in their credentials inside an embedded user-agent is also a red flag π©. Those views don't have the usual address bar or visible certificate validation features that browsers have, so the user has no real way to know if they're signing in to the legit site β. Even when they are on the right site, it trains them to enter credentials without checking the site first, which is so not it π€.
Besides the security stuff, embedded user-agents don't share authentication state with other apps or the browser π. That means the user has to log in again for every single authorization request, which is honestly considered an inferior experience π©.
9. IANA Considerations πΈ
This document doesn't require any IANA actions, so IANA gets a day off π§.
Section 7.1 explains how private-use URI schemes are used for inter-app communication in OAuth protocol flows β¨. In Section 7.1, this document requires that those schemes be based on domain names owned or assigned to the app, as recommended in Section 3.8 of [RFC7595] π. Per Section 6 of [RFC7595], you don't need to register domain-based URI schemes with IANA, so no extra paperwork, bestie π.
10. References π
10.1. Normative References π
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
Resource Identifier (URI): Generic Syntax", STD 66,
RFC 3986, DOI 10.17487/RFC3986, January 2005,
<https://www.rfc-editor.org/info/rfc3986>.
[RFC6749] Hardt, D., Ed., "The OAuth 2.0 Authorization Framework",
RFC 6749, DOI 10.17487/RFC6749, October 2012,
<https://www.rfc-editor.org/info/rfc6749>.
[RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
Protocol (HTTP/1.1): Message Syntax and Routing",
RFC 7230, DOI 10.17487/RFC7230, June 2014,
<https://www.rfc-editor.org/info/rfc7230>.
[RFC7595] Thaler, D., Ed., Hansen, T., and T. Hardie, "Guidelines
and Registration Procedures for URI Schemes", BCP 35,
RFC 7595, DOI 10.17487/RFC7595, June 2015,
<https://www.rfc-editor.org/info/rfc7595>.
[RFC7636] Sakimura, N., Ed., Bradley, J., and N. Agarwal, "Proof Key
for Code Exchange by OAuth Public Clients", RFC 7636,
DOI 10.17487/RFC7636, September 2015,
<https://www.rfc-editor.org/info/rfc7636>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
10.2. Informative References π¦
[RFC6819] Lodderstedt, T., Ed., McGloin, M., and P. Hunt, "OAuth 2.0
Threat Model and Security Considerations", RFC 6819,
DOI 10.17487/RFC6819, January 2013,
<https://www.rfc-editor.org/info/rfc6819>.
[RFC7591] Richer, J., Ed., Jones, M., Bradley, J., Machulak, M., and
P. Hunt, "OAuth 2.0 Dynamic Client Registration Protocol",
RFC 7591, DOI 10.17487/RFC7591, July 2015,
<https://www.rfc-editor.org/info/rfc7591>.
[AppAuth] OpenID Connect Working Group, "AppAuth", September 2017,
<https://openid.net/code/AppAuth>.
[AppAuth.iOSmacOS]
Wright, S., Denniss, W., et al., "AppAuth for iOS and
macOS", February 2016,
<https://openid.net/code/AppAuth-iOS>.
[AppAuth.Android]
McGinniss, I., Denniss, W., et al., "AppAuth for Android",
February 2016, <https://openid.net/code/AppAuth-Android>.
[SamplesForWindows]
Denniss, W., "OAuth for Apps: Samples for Windows", July
2016,
<https://openid.net/code/sample-oauth-apps-for-windows>.
Appendix A. Server Support Checklist β
OAuth servers that support native apps must:
- Support private-use URI scheme redirect URIs. This is required to support mobile operating systems. See Section 7.1. π±
- Support
"https"scheme redirect URIs for use with public native app clients. This is used by apps on advanced mobile operating systems that allow app-claimed"https"scheme URIs. See Section 7.2. π - Support loopback IP redirect URIs. This is required to support desktop operating systems. See Section 7.3. π₯οΈ
- Not assume that native app clients can keep a secret. If secrets are distributed to multiple installs of the same native app, they should not be treated as confidential. See Section 8.5. π€«
- Support PKCE [RFC7636]. Required to protect authorization code grants sent to public clients over inter-app communication channels. See Section 8.1 ππ
Appendix B. Platform-Specific Implementation Details π§
This document mostly defines best practices in a generic way, pointing at techniques that show up across a bunch of environments π. This non-normative section documents how the best practice gets implemented on various operating systems π οΈ.
The implementation details here were accurate when published, but they'll probably change over time β³. The hope is that those changes won't break the generic principles in the rest of the document, and that those principles win if there's ever a conflict π.
B.1. iOS Implementation Details π
Apps can start an authorization request in the browser without the user leaving the app, using the SFSafariViewController class or its successor SFAuthenticationSession, which both implement the in-app browser tab pattern πͺ. Safari can handle requests on old versions of iOS that don't have in-app browser tab functionality π°οΈ.
To get the authorization response back, both private-use URI scheme redirects (called "custom URL scheme" redirects) and claimed "https" scheme URIs (known as "Universal Links") are good options π«. Apps can claim private-use URI schemes with the CFBundleURLTypes key in the app's property list file, Info.plist. They can claim "https" scheme URIs using the Universal Links feature, with an entitlement file in the app and an association file hosted on the domain ποΈ.
Claimed "https" scheme URIs are the preferred redirect choice on iOS 9 and above, thanks to the ownership proof the operating system provides π.
A complete open-source sample is included in the AppAuth for iOS and macOS [AppAuth.iOSmacOS] library π.
B.2. Android Implementation Details π€
Apps can start an authorization request in the browser without the user leaving the app, using the Android Custom Tab feature, which implements the in-app browser tab pattern π·. If no browser supports Custom Tabs, the user's default browser can handle the request π.
Android browser vendors should support the Custom Tabs protocol by providing an implementation of the CustomTabsService class, so their users get the in-app browser tab experience optimization π
. Chrome is one browser that implements Custom Tabs π.
To receive the authorization response, private-use URI schemes are widely supported through Android Implicit Intents π¨. Claimed "https" scheme redirect URIs through Android App Links are available on Android 6.0 and above πͺ. Both types of redirect URIs get registered in the app's manifest π.
A complete open-source sample is included in the AppAuth for Android [AppAuth.Android] library π.
B.3. Windows Implementation Details πͺ
Both traditional apps and Universal Windows Platform (UWP) apps can make authorization requests in the user's browser π. Traditional apps usually use a loopback redirect to receive the authorization response, and listening on the loopback interface is allowed by default firewall rules π. When creating the loopback network socket, apps SHOULD set the SO_EXCLUSIVEADDRUSE socket option, so no other app can bind to the same socket π«.
UWP apps can use private-use URI scheme redirects to receive the authorization response from the browser, which brings the app to the foreground π. This is known on the platform as "URI Activation". The URI scheme is limited to 39 characters, and it may include the "." character, which makes short reverse-domain-name-based schemes (as required in Section 7.1) possible π.
UWP apps can also use the Web Authentication Broker API in Single Sign-on (SSO) mode, which is an external user-agent designed for authorization flows π§ . Cookies are shared between invocations of the broker but not with the user's preferred browser, so the user will need to log in again even with an active session in their browser π . The session created in the broker will still be available to later apps that use the broker π€. Personalizations the user has made to their browser, like a configured password manager, may not be available in the broker π. To qualify as an external user-agent, the broker MUST be used in SSO mode β οΈ.
To use the Web Authentication Broker in SSO mode, the redirect URI must be of the form "msapp://{appSID}", where {appSID} is the app's security identifier (SID). You can find the SID in the app's registration information or by calling the GetCurrentApplicationCallbackUri method π. Windows enforces the URI authority on these redirects, so only the app with the matching SID can receive the response on Windows π‘οΈ. However, the URI scheme could be claimed by apps on other platforms that don't have the same authority present, so this redirect type should be treated similarly to private-use URI scheme redirects for security purposes π§.
An open-source sample demonstrating these patterns is available [SamplesForWindows] π.
B.4. macOS Implementation Details π
Apps can kick off an authorization request in the user's default browser using the platform APIs for opening URIs in the browser π.
To catch the authorization response, private-use URI schemes are a really good redirect URI choice on macOS π. The user gets sent right back to the app they launched the request from, which is so smooth. You register these in the app's bundle information property list under the CFBundleURLSchemes key π. Loopback IP redirects are another solid option too β¨, and listening on the loopback interface is allowed by default firewall rules π‘οΈ.
There's a complete open-source sample in the AppAuth for iOS and macOS [AppAuth.iOSmacOS] library π¦. Go check it out, bestie!
B.5. Linux Implementation Details π§
Opening the authorization request in the user's default browser needs a distro-specific command π
. xdg-open is one such tool π οΈ.
The loopback redirect is the recommended redirect choice for desktop apps on Linux to receive the authorization response πΈ. Apps SHOULD NOT set the SO_REUSEPORT or SO_REUSEADDR socket options in order to prevent other apps binding to the same socket π
ββοΈ.
Acknowledgements π«Ά
The authors would like to acknowledge the work of Marius Scurtescu and Ben Wiley Sittler, whose design for using private-use URI schemes in native app OAuth 2.0 clients at Google formed the basis of Section 7.1 π.
The following individuals contributed ideas, feedback, and wording that shaped and formed the final specification π: Andy Zmolek, Steven E. Wright, Brian Campbell, Nat Sakimura, Eric Sachs, Paul Madsen, Iain McGinniss, Rahul Ravikumar, Breno de Medeiros, Hannes Tschofenig, Ashish Jain, Erik Wahlstrom, Bill Fisher, Sudhi Umarji, Michael B. Jones, Vittorio Bertocci, Dick Hardt, David Waite, Ignacio Fiorentino, Kathleen Moriarty, and Elwyn Davies π.
Authors' Addresses π
William Denniss
Google
1600 Amphitheatre Pkwy
Mountain View, CA 94043
United States of America
Email: rfc8252@wdenniss.com
URI: http://wdenniss.com/appauth
John Bradley
Ping Identity
Phone: +1 202-630-5272
Email: rfc8252@ve7jtb.com
URI: http://www.thread-safe.com/p/appauth.html
π¦ where this page comes from
- ietf: rfc8252 2017-10
- π girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms