The OAuth2 SAML bearer spec describes how an application can present an assertion to a token endpoint as an authorization grant. For example, Salesforce's API allows this approach to enable apps to autonomously request access tokens for a user account (as long as the user has already given permission for this, out-of-band).
I'm having trouble making sense of what the assertion means, though. Most of it is clear enough, e.g.
Issuer is the party that generated (and signed) the assertion
Subject is the user for whose account an access token is being requested
AudienceRestriction limits the audience to the token endpoint.
But I'm having trouble understanding the meaning of:
AuthnStatement -- My understanding from the SAML spec is that the issuer of this assertion is making the statement that it (the issuer) has authenticated the subject. Is this right?
SubjectConfirmation -- who is confirming what here? The SAML spec helpfully states that this element "Information that allows the subject to be confirmed". But what is confirmation? And who performs it, and how, and when, and for what purpose?
AuthnStatement element describes the act of authentication at the identity provider.
If the Assertion issuer authenticated the subject, the Assertion SHOULD contain a single representing that authentication event.
Example:
<AuthnStatement AuthnInstant="2010-10-01T20:07:34.371Z">
<AuthnContext>
<AuthnContextClassRef>
<!--Authentication method, was the client authenticated with digital cert, password, kerberos token?-->
urn:oasis:names:tc:SAML:2.0:ac:classes:X509
<!--For example, the Password class is applicable when a principal authenticates to an authentication authority through the presentation of a password over an unprotected HTTP session. -->
urn:oasis:names:tc:SAML:2.0:ac:classes:Password
urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos
</AuthnContextClassRef>
</AuthnContext>
</AuthnStatement>
SubjectConfirmation element allows the authorization server to confirm it as a Bearer Assertion. Such element MUST have a Method attribute with a value of "urn:oasis:names:tc:SAML:2.0:cm:bearer".
The SubjectConfirmation element MUST contain a SubjectConfirmationData element (With exceptions) indicating the token endpoint URL of the authorization server. The authorization server MUST verify that the value of the Recipient attribute matches the token endpoint URL to which the Assertion was delivered.
Example:
<saml:SubjectConfirmation
<!-- Mandatory -->
Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData
<!-- The AuthRequest sent this ID -->
InResponseTo="aaf23196-1773-2113-474a-fe114412ab72"
<!-- It was through HTTP POST token endpoint URL -->
Recipient="https://sp.example.com/SAML2/SSO/POST"
<!-- Not valid ON or After this Date and Time -->
NotOnOrAfter="2004-12-05T09:27:05"/>
</saml:SubjectConfirmation>
Yes, the AuthnStatement is from the issuer of this assertion stating that it has authenticated the subject.
SubjectConfirmation tells how an entity that wants to rely on an assertion can confirm that the subject in question is the subject referenced in this assertion. Maybe the assertion is valid, but is it for the user making the request? If the method is bearer then any subject who can present this assertion to the endpoint referenced in Recipient before the date in NotOnOrAfter is confirmed. If the method is holder-of-key then only a subject who can prove possession of the key referenced by a nested KeyInfo element is confirmed.
Related
I have added bearer token authentication on an API for a client. In the access token that's sent to the API, the issuer is this: http://[some-domain.com]/adfs/services/trust.
Is the identity provider configured incorrectly or in an unsafe way when this URL is on HTTP, and not HTTPS? Or is this simple just a string, not used to make requests, and thus it doesn't matter if it says HTTP?
The iss claim is a string or URI, which means that the service receiving the token will use it to determine whether to trust the token or not by comparing the value to a list of issuers it is configured to trust.
The string comparison should follow the rules described in RFC 3986
The value is not a URL, so the value does not point to any real resource on the network.
See RFC 7519 for more information on JWT and standard claims.
I am trying to add the authorization in SAML as part of IDP implementation using SAML 2.0 Bearer Assertion Profiles for OAuth 2.0
Here, the SAML assertion is exchanged for Oauth2 access token. As per specs, the only access token should be returned in exchange of the SAML assertion and refresh token should not be returned. Following is mentioned in the spec regarding renewal of the access token (RFC7521)
An assertion used in this context is generally a short-lived
representation of the authorization grant, and authorization servers
SHOULD NOT issue access tokens with a lifetime that exceeds the validity period of the assertion by a significant period. In practice, that will usually mean that refresh tokens are not issued in response to assertion grant requests, and access tokens will be issued with a reasonably short lifetime. Clients can refresh an expired access token by requesting a new one using the same assertion, if it is still valid, or with a new assertion.
Now, if the access token is expired client can get new access token in exchange of assertion if the Assertion is still valid but how to get assertion if it is expired? If we re-initiate the SSO flow then assertion will be provided to the ACS (Assertion Consumer Service) URL which will cause a shifting from the current screen for an active user. This might cause an issue if the user has any unsaved activity on the page.
How can I provide an Assertion as a response to a request? Is there any provision in SAML for extending assertion directly via a single call to IDP?
You can't "extend" an existing assertion, you'd have to ask IdP for a new one.
The workflow where a new token is requested via an existing assertion is rare but technically possible if you can hold on to the assertion after it's issued. Doing so would require an intermediate step/component between the IdP that issues the assertion and the oAuth client, something that is certainly possible in a number of solution architectures.
The refresh of an expired token via a new assertion would be a much more common implementation choice.
I am trying to get OneDrive access token by following URL
https://login.live.com/oauth20_token.srf?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&redirect_uri=https://login.live.com/oauth20_desktop.srf&code=AUTHORIZATION_CODE&grant_type=authorization_code
but the response is as following
{"error":"invalid_request","error_description":"Public clients can't send a client secret."}
Can anyone explain this?
A "public client" is a mobile or desktop application (web services are "confidential clients"). MSA is giving you this response because you're redirecting to https://login.live.com/oauth20_desktop.srf. In this case, you should not be providing the client_secret value in the request, so your request should just look like this:
https://login.live.com/oauth20_token.srf?client_id=YOUR_CLIENT_ID&redirect_uri=https://login.live.com/oauth20_desktop.srf&code=AUTHORIZATION_CODE&grant_type=authorization_code
A more recent example from:
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
Request an access token with a client_secret
Now that you've acquired an authorization_code and have been granted permission by the user, you can redeem the code for an access_token to the resource. Redeem the code by sending a POST request to the /token endpoint:
// Line breaks for legibility only
POST /{tenant}/oauth2/v2.0/token HTTP/1.1 Host:
https://login.microsoftonline.com Content-Type:
application/x-www-form-urlencoded
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&scope=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&grant_type=authorization_code
&code_verifier=ThisIsntRandomButItNeedsToBe43CharactersLong
&client_secret=JqQX2PNo9bpM0uEihUPzyrh // NOTE: Only required for web apps. This secret needs to be URL-Encoded.
Parameter
Required/optional
Description
tenant
required
The {tenant} value in the path of the request can be used to control who can sign into the application. Valid values are common, organizations, consumers, and tenant identifiers. For more information, see Endpoints.
client_id
required
The Application (client) ID that the Azure portal – App registrations page assigned to your app.
scope
optional
A space-separated list of scopes. The scopes must all be from a single resource, along with OIDC scopes (profile, openid, email). For more information, see Permissions and consent in the Microsoft identity platform. This parameter is a Microsoft extension to the authorization code flow, intended to allow apps to declare the resource they want the token for during token redemption.
code
required
The authorization_code that you acquired in the first leg of the flow.
redirect_uri
required
The same redirect_uri value that was used to acquire the authorization_code.
grant_type
required
Must be authorization_code for the authorization code flow.
code_verifier
recommended
The same code_verifier that was used to obtain the authorization_code. Required if PKCE was used in the authorization code grant request. For more information, see the PKCE RFC.
client_secret
required for confidential web apps
The application secret that you created in the app registration portal for your app. Don't use the application secret in a native app or single page app because a client_secret can't be reliably stored on devices or web pages. It's required for web apps and web APIs, which can store the client_secret securely on the server side. Like all parameters here, the client secret must be URL-encoded before being sent. This step is usually done by the SDK. For more information on URI encoding, see the URI Generic Syntax specification. The Basic auth pattern of instead providing credentials in the Authorization header, per RFC 6749 is also supported.
So, if creating a desktop or mobile app and if you registered your app as such in the Azure portal at https://portal.azure.com/ then if you send client_secret you would get that particular error. So you must remove it from the POST request to successfully exchange received code for authentication token and refresh token. Note that confidential web app is the only type which requires to send client_secret. Other types of registered apps or public apps should not send the client_secret.
The code is valid for 10 minutes so it must be immediately exchanged for authentication token using the request similar to the above.
Note one more thing:
Whether or not you'll need to send a client_secret depends whether or not you've registered your application in AzureAD as "web" (requires sending client_secret) or "native app" (does not require sending the client_secret). So your implementation will depend on the registration you did. You can change the type of application in AzureAD -> App Registrations -> select Authentication from the left menu. Under the Platform Configurations choose your specific platform.
Suppose we have some RESTful API whose resources we want to expose. End users will work with this API through client applications like mobile apps and Javascript based clients that run on web browsers.
With OAuth 2.0 this RESTful API will lie on the Resource Server and we will have one Authorization Server on which the client applications are registered. The users will then be registered at the authorization server and will be able to grant permission to those applications access resources on their behalf or not.
So when the user access one client application he will be redirected to the Authorization Server and be prompted to grant permissions to said client app. After that an access token is issued and the client is able to make requests to the Resource Server.
All of this is quite clear to me. There's just one missing piece: the protection of each resource might be user-dependent. To be more precise it might be claims-dependent. What I mean by that is we can have the following situation:
The resource http://resourceserver.com/api/first-resource should only be accessible to users with claim "ExampleClaim" with value 123.
The resource http://resourceserver.com/api/second-resource should only be accessible to users with claim "AnotherClaim" with value 123.
The resource http://resourceserver.com/api/third-resource should be accessible to any users.
When I first heard of OAuth was dealing with ASP.NET WebAPI and I dealt with that in the following way: when the request was sent with the Authorization: Bearer [token] header, on server side the thread principal was set and I thought that this meant the user was authenticated with the API. So I used [Authorize] attributes in order to verify if the user could access the resource.
After studying OAuth more deeply I saw this was a terrible misuse of the protocol. As I've learned, OAuth authorizes applications and not users. When the request is made with the Authorization header, as I've learned, the access token shouldn't contain information about the user, just about the application being allowed to make the request.
Considering that, sending the Authorization header with the request doesn't identify the user and does don't say if the user can or cannot access said resource.
In that case, how does one perform this kind of authorization? I mean, not authorization of the client app performing the request, but the authorization of the user accessing the resource based on his claims? I believe this is where OpenID Connect and it's ID tokens come in, but I'm unsure. How does one manage this?
An access token does not contain a user's claims, but it contains the subject of the user who has granted permissions to the client application. "Subject" is a technical term and it means a unique identifier. Simply saying, "subject" is a user ID in your database.
At a protected resource endpoint, you will do:
Extract an access token from the request. (RFC 6750)
Get detailed information about the access token from the authorization server. (RFC 7662)
Validate the access token. The validation includes (a) whether the access token has expired or not, and (b) whether the access token covers scopes (permissions) that are required by the protected resource endpoint.
The steps above from 1 to 3 are an access control against client applications. OAuth 2.0 (RFC 6749) is for this. See "Protected Resource" by Authlete (by me) for details about these steps.
After the steps above, then you will do:
Extract the subject from the access token. Again, "subject" is a unique identifier of the user.
Retrieve claims of the user from your database.
Validate the claims as you like.
The steps above from 4 to 6 are an access control against users. OAuth 2.0 is NOT for this.
The primary purpose of OpenID Connect is to get an ID token in a verifiable manner. You can confirm that an ID token has been issued by the right party by verifying the signature attached to the ID token. See JSON Web Signature (JWS) (RFC 7515) for details about signature.
An ID token itself is not a technology to protect Web APIs. But you may be able to use it for that purpose if you use at_hash claim in an ID token properly (see "3.1.3.6. ID Token" in OpenID Connect Core 1.0). However, at a protected resource endpoint, it will be much easier to get claims directly from your database than to parse an ID token.
**[ Additional answer #1 for the comment ]**
In your use case, you don't need ID tokens. It's because an access token already contains information about the subject of the user. In normal cases, the information is equivalent to the value of sub claim in an ID token.
Therefore, you don't need an ID token to get the subject of the user. See the description of step 4, and you can find "extract the subject from the access token."
**[ Additional answer #2 for the comment ]**
So is there anything wrong in extracting the subject from the access token like that and verify the claims? Or this is the right way of doing things?
There is nothing wrong. For example, suppose you define a Web API, https://api.example.com/profile, which returns the profile information of a user. In normal cases, such an API would accept an access token and then extract the subject from the access token to determine which user to refer to. On the other hand, if the API did not extract the subject from the access token, it would have to require "subject" as a request parameter to determine which user to refer to (or require an ID token that contains "sub" claim). Even in such a case, the API must check whether the subject specified by the request parameter and the subject associated with the access token are identical because otherwise, it would become a security issue.
Checking claims after extracting the subject is also a normal step. For example, you may want to restrict the functionalities of your service based on the plan that the user has paid for (Free plan, Lite plan, Enterprise plan, or whatever). In this case, you would have to refer to plan claim. Of course, checking such a claim can be done only after extracting the subject from the access token.
Therefore, (1) extracting the subject from an access token and then (2) verifying the claims of the user are normal and even typical steps in implementations of protected resource endpoints.
You are right, OAuth is NOT an authentication protocol but rather a delegation protocol.
OpenID Connect adds two notable identity constructs to OAuth 2.0's token issuance model.
an Identity Token - the delivery of which from one party to another
can enable a Federated Identity SSO user experience
a standardized identity attribute API - at which a client can
retrieve desired identity attributes for a given user.
The ID TOken can be presented to the userinfo_endpoint to obtain the information and provides level of assurance that the user has been authenticated by the OpenID Provider.
BTW: The "sub" ie only unique within the context of the Authorization Server. It is recommended IF you store the sub you also store something like iss-sub. The thoughts are tsmith at Google may not be tsmith at Twitter
I found this in another SO thread:
Steps:
User connects to OpenID enabled website.
User enters credential information.
A POST is made with a BASE64 (website to provider)
An answer is built (that contains expiration)
The website redirects the user to the provider to login.
User enters password and submit.
Verification is done.
Login!
How are step 6-8 secured? The way I see it, the client is authenticating with the provider and reporting back the result to our server.
What is stopping the client from faking the authentication result?
Primarily, the authentication result is cryptographically signed by the provider. There are also other security measures protecting against other attacks.
Quoting the OpenID 2.0 specification, section 11.:
When the Relying Party receives a positive assertion, it MUST verify the following before accepting the assertion:
The value of "openid.return_to" matches the URL of the current request (Section 11.1)
Discovered information matches the information in the assertion (Section 11.2)
An assertion has not yet been accepted from this OP with the same value for "openid.response_nonce" (Section 11.3)
The signature on the assertion is valid and all fields that are required to be signed are signed (Section 11.4)
The client can, of course, send a fake authentication result, but it won't pass verification.