If you look at the OAuth type authorization code stream , then yes, there are two actuaries:
1. <user_session_id, client_id> => authorization_code 2. <client_id, redirect_uri, authorization_code, client_secret> => access_token, refresh_token
In step 1: the user tells the OAuth server that "I want to authorize this client environment (client_id) to access my resource. Here is my authentication (user_session_id or whatever)"
In step 2: the client (client_id) tells the OAuth server that "I have a user authorization (authorization code), please give me an access token for future access. And this is my authentication (client_id & client_secret)"
You see, if we skip step 2, then there is no guarantee for client authentication. Any client can call step1 with a different client_id and get an access token for that client_id instead of its own. That is why we need step 2.
If you really want to combine step1 and step2, you can do something like this:
<client_id, redirect_uri, client_secret> => access_token, refresh_token
We use this approach in our Open Api platform, and so far have not found any security issues.
By the way, there is actually a type of implicit grant , that is:
<client_id, redirect_uri> => access_token, refresh_token
As a rule, it is applicable only to client applications that do not have a server part. In this case, the OAuth server must make sure that the redirect URI belongs to this client (for example, the same with the redirect_uri register)