OA4MP supports the scope parameter for servers. Unfortunately, scope has been completely overused and has acquired several meanings. To be completely clear, scopes are requests that ask for certain claims to be asserted in various tokens.
Remember that, unhelpfully, if you send in a scope request, the resulting claim is asserted using the scope tag again. This completely confuses many people since the request and response look the same. It's the specification though, so we get to live with it. A fuller discussion about scopes generally to be found here. This blurb is concerned with configuring a server or client to use scopes.
The id token is a JWT (JSON Web Token) which has a header (signing information), payload (a base 64 encoded JSON object), and signature, plus a system for verifying the signature. Historically, since this could be trusted by checking with the server if it had been altered, people started using it as a type of access token. That's both a good and bad idea, since it is not really designed for granting access to things, but, yes, it can be verified. At this point, all the tokens an OAuth 2 server could create (authorization, access, refresh) started to mutate into JWTs. This is a good thing. OA4MP lets you do this, it also will issue old style opaque tokens.
A note about native tokens in OA4MP. These are unforgeable, self-describing and unique (unlike many OAuth servers where they are indeed just random strings). Here is an example of one:
https://cilogon.org/oauth2/6c41b109f62d6776976bc23816e97f5a?type=refreshToken&ts=1683061597117&version=v2.0&lifetime=500000000
If you do not want/need JWTs for your service, you will get these. If you do get JWTs, these native tokens will be used as the jti unique identifier.
This sure is messy you think. Is there another way to request scopes? Sort of. Another attempts was to create essentially an entire request language per claim in the claim parameter, but that turned into such a mess and most servers don't support that. OA4MP most certainly does not mostly since the requests are even hard to write. I am relating this history because people who are new to this often have a devil of a time figuring out why the scope parameter does what it does.
Nota Bene: Clients on the server (as opposed to free standing clients) by default are configured with the strict_scopes set to true. What that means is that any scope not in its configured scope list will raise an error. This is done since clients that typically need a handful of specific scopes should be alerted if an unknown scope is requested. The spec says that unknown scopes may be ignored and that is common enough for most OAuth 2 servers, but our experience is that the vast majority of times there is a typo in the scope (so requesting emaile not email) and it is far better that the request fail initially so it can be fixed rather than have a mystery that the email claim is not asserted.
If you need a lot of flexibility in setting claims (such as for SciTokens or WLCG token requests that are asserted in the access token), set the strict_scopes to false. You can pass in anything at that point and the more standard OAuth 2 behvaior of just ignoring anything unknown will be in effect.
There is a handler attribute in the scopes tag. This allows you to specify a claim source implementation that will be invoked automatically for every phase, adding the resulting claims to the set a class that has a no-argument. While there are claim sources that are included in the standard OA4MP distribution, (such as for an LDAP claim source), the aim is that you can write your own and have it run. Generally this should not be used, except as a very specific custom extension to OA4MP. In particular, this contract has no way to pass along any configuration to the handler.
If you want a claim source to be run automatically, the easiest way is in a QDL script to add it (with the ~ operator) to the claim_sources. So a typical use in the pre or post_auth phase in your QDL script would be:
cfg. := claims#new_template('file'); cfg.'claim_key' := 'eppn'; // configures it as per here claim_sources. := claim_sources. ~ [claims#create_source(cfg.)]; // automate getting claims by the system
This creates a file claim source and adds it to the list of claim sources the server runs. You need to add this once and it will be managed ever thereafter. The downsides to this approach are that you no longer have control over it and it may be invoked a great deal more that you would want or need. Generally, adding to the list of claim sources is discouraged. It is simply better the get the claims you want and add them where you want them, e.g. from the NCSA client script:
cfg. := claims#new_template('ncsa'); cfg. := claims#create_source(cfg.); claims. := claims. ~ claims#get_claims(cfg., claims.'uid');
Which creates a claim source, gets the claim claims and adds them to the existing claims.
The configuration allows you to set statically which scopes are requested as follows. This is in both the client configuration, where it is used to construct the request with the given scopes, and the server configuration, where it sets a fixed list of scopes that will be strictly honored (by default). The top-level tag is the scopes tag and that in turn contains scope tags. These scope tags contain a single scope and supports a single attribute:
Name | Required | Default | Description |
enabled | N | true | Enable or disable this scope. |
<config> <client name="my-cfg"> <scopes> <scope>custom.scope</scope> </scopes> <!-- other stuff.. --> <client> </config>
The client will include custom.scope in requests to the server in addition to the standard scopes. The default OA4MP client behavior is to request all the standard scopes (exception offline_access). Additions to the scopes element are therefore additive. To disallow a scope, disable it:
<config> <client name="my-cfg"> <scopes> <scope enabled="false">edu.uiuc.ncsa.myproxy.getcert</scope> </scopes> <!-- other stuff.. --> <client> </config>
This would omit the getcert scope, but request all the other scopes.
<config> <service name="my-server"> <scopes> <scope enabled="false">edu.uiuc.ncsa.myproxy.getcert</scope> <scope>my.custom.scope</scope> </scopes> <!-- other stuff.. --> </service> </config>
In this case, in the registration page, the custom scope of my.custom.scope would be presented as an option, but the getcert scope would not. These scopes would then be added to the client's configuration so that requests with my.custom.scope would be accepted. Of course, some form of processing to do somethign with that scope is needed, e.g. QDL scripting.
The standard OA4MP client sends along only the scope parameter in the initial request. You may also send along scopes in the token, refresh and exchange phases but you need to customize to do that.