IdentityServer4 JWT scope as space delimited string instead of array? - oauth-2.0

I've been trying to understand the IETF RFC for OAuth2 Authorization scopes.
Scope
OAuth2
The value of the scope parameter is expressed as a list of space-delimited, case-sensitive strings. The strings are defined by the authorization server. If the value contains multiple space-delimited strings, their order does not matter, and each string adds an additional access range to the requested scope.
IdentityServer outputs the scope in JWT as an array of strings whereas almost all other providers output the scope as a single space delimited string. Is there an adapter or a configuration setting I can use to have ID4 output the scope in the JWT as a single space delimited string ?
"scope": [
"openid",
"profile",
"email",
"api"
],
"scope": "openid profile email api"

According to the RFC8693 draft and RFC7662 (OAuth 2.0 Token Introspection):
The value of the "scope" claim is a JSON string containing a space-
separated list of scopes associated with the token, in the format
described in Section 3.3 of [RFC6749].
And, even though there is not a formal definition written there, in this other draft, the example showed has also the scope items space-separated.
Personally, I would prefer it to be an array (as it would represent better the nature of scope claim), but the space-separated format is indeed more compliant with OAuth2's standard.

Related

Does the Microsoft Graph API #odata.type include the "#" character?

The Microsoft Graph API is based on OData and each object has an #odata.type
Looking at the user resource type, the #odata.type is #microsoft.graph.user"
{
"#odata.type": "#microsoft.graph.user",
"id": "String (identifier)",
"deviceEnrollmentLimit": 5
}
But the example from group resource type the #odata.type is microsoft.graph.user and does not start with a #
{
...
"members": [ { "#odata.type": "microsoft.graph.directoryObject" } ],
"membersWithLicenseErrors": [{"#odata.type": "microsoft.graph.user"}],
...
}
The Types section of the OData Version 4.0 Common Schema Definition Language (CSDL) doesn't seem to indicate any standard related to this topic.
Question: Is the # character part of the Microsoft defined #odata.type or part of OData spec or just a convention some OData providers use?
I wish there was an easy answer to this question. The OData specification that describes the OData JSON format has the following to say:
For payloads described by an OData-Version header with a value of 4.0, this name MUST be prefixed with the hash symbol (#); for non-OData 4.0 payloads, built-in primitive type values SHOULD be represented without the hash symbol, but consumers of 4.01 or greater payloads MUST support values with or without the hash symbol.
http://docs.oasis-open.org/odata/odata-json-format/v4.01/cs01/odata-json-format-v4.01-cs01.html#_Toc499720587
The beta version of Microsoft Graph supports 4.01, but most of the V1.0 API uses 4.0. The best option would be to always include the hash symbol for non-primitive types.
Note: Marking Darrel's answer as correct since he found this, but answering in a different way
From Graph Explorer you can explore the $metadata directly using https://graph.microsoft.com/v1.0/$metadata
The namespace defined in the schema is microsoft.graph
<Schema Namespace="microsoft.graph" Alias="graph" xmlns="http://docs.oasis-open.org/odata/ns/edm">
and for the entity in question, the type name is user
<EntityType Name="user" BaseType="graph.directoryObject" OpenType="true">
So, I would conclude the fully namespaced type is actually microsoft.graph.user with an alias of graph.user but the value of #odata.type is #microsoft.graph.user because it requires the # character (hash symbol) per the OData JSON Format spec.

What is an opaque token?

And what does it mean that they are in a "proprietary format"? I am reading about JWT refresh tokens and they are opaque tokens, but I don't understand the term.
A JWT has readable content, as you can see for example on https://jwt.io/.
Everyone can decode the token and read the information in it. The format is documented in RFC 7519.
An opaque token on the other hand has a format that is not intended to be read by you. Only the issuer knows the format.
The meaning of the word already gives a hint:
opaque
/ə(ʊ)ˈpeɪk/
adjective
not able to be seen through; not transparent.
Here's a quote from https://auth0.com/docs/tokens:
Opaque tokens: Tokens in a proprietary format that typically contain some identifier to information in a server’s persistent storage. To validate an opaque token, the recipient of the token needs to call the server that issued the token.
A "opaque JWT refresh token" is a contradiction as per definition above. What actually is meant here is, that in some JWT frameworks only the authentication token is a JWT, but as refresh token they use opaque tokens.
Here, the term "opaque" means the string (that serves as token) is like a reference (in OOPs), or pointer (in C), or foreign keys (in relational DBs).
i.e. You need an external content to resolve it.
Simple versus Composite:
The string is a "simple" string, as opposed to JWS, where is "composite"; It has parts "inside" it.
Inside versus Outside:
You can extract a payload (with claims, etc) out of it without referring to an external server or storage, "outside" this string.
Since an opaque token is a simple string it is just a reference, hence, naturally, its format is entirely arbitrarily determined by the server that issues it (hence the term "proprietary format"). The token string is determined at the time of creation of the underlying (referred-to) content, i.e. when it is paired (associated) with the contents that this token (as the reference or foreign key) refers to.

Should the WWW-Authenticate realm parameter be encoded?

When building the WWW-Authenticate header value for OAuth/OAuth1a it calls for percent-encoding the parameters. The spec and implementations are ambiguous on whether the realm parameter should be percent-encoded or not.
Section 5.4.1. Authorization Header of the spec reads:
The OAuth Protocol Parameters are sent in the Authorization header the
following way:
Parameter names and values are encoded per Parameter Encoding.
For each parameter, the name is immediately followed by an '=' character (ASCII code 61), a '"' character (ASCII code 34), the parameter value (MAY be empty), and another '"' character (ASCII code 34).
Parameters are separated by a comma character (ASCII code 44) and OPTIONAL linear whitespace per [RFC2617].
The OPTIONAL realm parameter is added and interpreted per [RFC2617], section 1.2.
For example:
Authorization: OAuth realm="http://sp.example.com/",
oauth_consumer_key="0685bd9184jfhq22",
oauth_token="ad180jjd733klru7",
oauth_signature_method="HMAC-SHA1",
oauth_signature="wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",
oauth_timestamp="137131200",
oauth_nonce="4572616e48616d6d65724c61686176",
oauth_version="1.0"
If these steps are meant to be taken in order, then it seems like only the OAuth specific parameters are meant to be url encoded.
If these steps are not meant to be taken in order, then maybe the realm parameter is included in step 1. However, the WWW-Authenticate header example in the OAuth1a spec shows the realm as realm="http://sp.example.com/" which is not percent-encoding the colon or the slashes.
To make matters more confusing, it seems this varies from implementation to implementation. Many OAuth implementations give no special treatment to the parameters and simply percent-encode all of them, but other OAuth implementations give special treatment to the realm parameter and exclude it from percent-encoding.
What is the correct behavior for adding the realm parameter to the WWW-Authenticate header?
The WWW-Authenticate header and the realm parameter, in particular, are defined by rfc2617 and rfc7235, which do not say anything about encoding. rfc7235 shows an example where the spaces in "Login to \"apps\"" are not percent-encoded.
rfc2617 and rfc7235 are the authority on the WWW-Authenticate header and realm parameter while the OAuth1a spec is only the authority on the additional OAuth specific parameters. Therefore the realm parameter should not be percent-encoded and section 5.4.1 of the OAuth1a spec should be interpreted to only be talking about OAuth Protocol Parameters with regards to percent-encoding.

Is it possible to get profile information in an id_token from Google?

When using Google's OpenIDConnect authentication system, it's possible to specify email or profile or both in the scope parameter. If you request the email scope, the "email" and "email_verified" claims will be included in the id_token that gets returned as part of a successful OAuth2 authentication session.
Here's an example from Google's documentation:
An ID token's payload
An ID token is a JSON object containing a set of name/value pairs.
Here’s an example, formatted for readability:
{"iss":"accounts.google.com",
"at_hash":"HK6E_P6Dh8Y93mRNtsDB1Q",
"email_verified":"true",
"sub":"10769150350006150715113082367",
"azp":"1234987819200.apps.googleusercontent.com",
"email":"jsmith#example.com",
"aud":"1234987819200.apps.googleusercontent.com",
"iat":1353601026,
"exp":1353604926,
"hd":"example.com"
}
However, requesting the profile scope seems to have no effect whatsoever on the contents of the id_token. In order to retrieve the profile information, you have to make a separate HTTP request to a distinct endpoint (authenticated with the access_token you just received) to get a document that looks very similar, but with more information:
{
"kind": "plus#personOpenIdConnect",
"gender": string,
"sub": string,
"name": string,
"given_name": string,
"family_name": string,
"profile": string,
"picture": string,
"email": string,
"email_verified": "true",
"locale": string,
"hd": string
}
Ideally, I would prefer to get the profile information (just name, actually) included in the id_token JWT rather than having to make a separate call. Is there any way to specify additional fields and have them included as claims in the id_token? If not, why is email treated specially and returned in the id_token?
Starting today you will get profile information when exchanging the code at the token endpoint (i.e. using the "code flow").
How to use: add the profile scope to your request, and make sure you are using the OpenID Connect compliant endpoints (the ones listed in https://accounts.google.com/.well-known/openid-configuration).
Look for claims such as name and picture in these ID Token responses. As before, if the email scope is in your request, the ID Token will contain email related claims.
When you refresh your access token, every so often the ID Token that is returned with the fresh access token will also contain these additional claims. You can check these fields, and if present (and different to what you have stored), update your user's profile. This can be useful to detect name or email address changes.
When a request is made with response_type=id_token and profile in the scope like scope=openid+profile+email, the resulting id token should contain the profile claims directly in it.
This is per section 5.4 of the OpenID Connect spec, which says "... when no Access Token is issued (which is the case for the response_type value id_token), the resulting Claims are returned in the ID Token."
However, in a little testing I did with their OAuth 2 Playground, Google doesn't seem to put profile claims in the id token even when response_type=id_token and no access token is issued. I'd argue that this is an implementation defect on Google's part and, short of them fixing that (or adding support for the "claims" Request Parameter), there doesn't seem to be a way to accomplish what you're looking for.
Well, this is the right place to request. We are working to support this feature and should be rolling this out soon (in the next few weeks). I'll make an update to this response then.

What are the characteristics of an OAuth token?

How many characters long can an oauth access token and oauth access secret be and what are the allowed characters? I need to store them in a database.
I am not sure there are any explicit limits. The spec doesn't have any.
That said, OAuth tokens are often passed as url parameters and so have some of the same limitations. ie need to be properly encoded, etc.
OAuth doesn't specify the format or content of a token. We simply use encrypted name-value pairs as token. You can use any characters in token but it's much easier to handle if the token is URL-safe. We achieve this by encoding the ciphertext with an URL-safe Base64.
As most people already pointed out. The OAuth specification doesn't give you exact directions but they do say...
cited from: https://datatracker.ietf.org/doc/html/draft-hammer-oauth-10#section-4.9
"Servers should be careful to assign
shared-secrets which are long enough,
and random enough, to resist such
attacks for at least the length of
time that the shared-secrets are
valid."
"Of course, servers are urged to err
on the side of caution, and use the
longest secrets reasonable."
on the other hand, you should consider the maximum URL length of browsers:
see: http://www.boutell.com/newfaq/misc/urllength.html
If you read the spec, it says,
The authorization server issues the registered client a client
identifier - a unique string representing the registration
information provided by the client. The client identifier is not a
secret; it is exposed to the resource owner, and MUST NOT be used
alone for client authentication. The client identifier is unique to
the authorization server.
The client identifier string size is left undefined by this
specification. The client should avoid making assumptions about the
identifier size. The authorization server SHOULD document the size
of any identifier it issues.
Second, Access Token should be sent as header, not as a URL param.
Authorization: Bearer < token>.
An OAuth token is conceptually an arbitrary-sized sequence of bytes, not characters. In URLs, it gets encoded using standard URL escaping mechanisms:
unreserved = ALPHA, DIGIT, '-', '.', '_', '~'
Everything not unreserved gets %-encoded.
I'm not sure whether you just talk about the oauth_token parameter that gets passed around. Usually, additional parameters need to be stored and transmitted as well, such as oauth_token_secret, oauth_signature, etc. Some of them have different data types, for example, oauth_timestamp is an integer representing seconds since 1970 (encoded in decimal ASCII digits).
Valid chars for OAuth token are limited by HTTP header value restrictions as OAuth token is frequently sent in HTTP header "Authorization".
Valid chars for HTTP headers are specified by https://www.rfc-editor.org/rfc/rfc7230#section-3.2.6. Alternatively you may check HTTP header validating code of some popular HTTP client libs, for example see Headers.checkNameAndValue() util of OkHttp framework: https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/Headers.java
And this is not all. I wouldn't include HTTP header separator (; and many others) and whitespace symbols (' ' and '\t') and double quote (") (see https://www.rfc-editor.org/rfc/rfc7230#section-3.2.6) as it would require to escape OAuth token before using in HTTP header. Frequently tokens are used by humans in curl test requests, and so good token generators don't add such characters. But you should check what characters may produce Oauth token generator with which your service is working before making any assumptions.
To be specific, even if Oauth spec doesn't say anything, if you are using java and mysql then it will be 16 characters as we generally generate the tokens using UUID and store it as BINARY(16) in the database. I know these details as I have recently done the development using OAuth.

Resources