I have previously written 2 posts about the generation and persistence of Refresh tokens: 1) Web API and OAuth: The persistence of both the access token, Mr He refresh token;2) ASP. OWIN Oauth:refresh Tokens.
We then realized the creation and persistence of the refresh token in Cnblogsrefreshtokenprovider:
Public classcnblogsrefreshtokenprovider:authenticationtokenprovider{PrivateIrefreshtokenservice _refreshtokenservice; PublicCnblogsrefreshtokenprovider (Irefreshtokenservice refreshtokenservice) {_refreshtokenservice=Refreshtokenservice; } Public Override AsyncTask Createasync (Authenticationtokencreatecontext context) {if(string. IsNullOrEmpty (context. Ticket.Identity.Name))return; varClientId = context. owincontext.get<string> ("as:client_id"); if(string. IsNullOrEmpty (CLIENTID))return; varRefreshtokenlifetime = context. owincontext.get<string> ("As:clientrefreshtokenlifetime"); if(string. IsNullOrEmpty (Refreshtokenlifetime))return; //Generate access tokenRandomNumberGenerator Cryptorandomdatagenerator =NewRNGCryptoServiceProvider (); byte[] buffer =New byte[ -]; Cryptorandomdatagenerator.getbytes (buffer); varRefreshtokenid = convert.tobase64string (buffer). TrimEnd ('='). Replace ('+','-'). Replace ('/','_'); varRefreshtoken =NewRefreshtoken () {Id=Refreshtokenid, ClientId=NewGuid (clientId), UserName=context. Ticket.Identity.Name, ISSUEDUTC=Datetime.utcnow, EXPIRESUTC=DateTime.UtcNow.AddSeconds (convert.todouble (Refreshtokenlifetime)), Protectedticket=context. Serializeticket (), IP=context. Request.getuserip ()}; Context. TICKET.PROPERTIES.ISSUEDUTC=REFRESHTOKEN.ISSUEDUTC; Context. TICKET.PROPERTIES.EXPIRESUTC=REFRESHTOKEN.EXPIRESUTC; if(await_refreshtokenservice.save (Refreshtoken)) {context. Settoken (Refreshtokenid); } } Public Override AsyncTask Receiveasync (Authenticationtokenreceivecontext context) {varRefreshtoken =await_refreshtokenservice.get (context. Token); if(Refreshtoken! =NULL) {context. Deserializeticket (Refreshtoken.protectedticket); varresult =await_refreshtokenservice.remove (context. Token); } }}
Cnblogsrefreshtokenprovider
Later, a problem was found (this is the 1th problem encountered), and when the user does not log in, the client credentials grant gets access token, it also generates a refresh token and saves it to the database. The refresh token is intended to solve the hassle of entering user names and passwords multiple times when obtaining access tokens in Resource owner password credentials grant mode. Therefore, for the client credentials grant scenario, it is not necessary to generate the refresh token.
So, you have to try to avoid this refresh token untimely situation. Later, the workaround was found, simply by adding the following code at the beginning of the overloaded method of Createasync:
Public class cnblogsrefreshtokenprovider:authenticationtokenprovider{ publicoverride Async Task createasync (authenticationtokencreatecontext context) { if ( Stringreturn; // ... }}
The 2nd problem encountered is that the client acquires the refresh token multiple times with resource owner password credentials grant, generates multiple refresh tokens, and stores multiple records in the database.
Typically, the client obtains the refresh token in the way resource owner password credentials grant, and it is saved. You need to update access token with this refresh token to update, the update will generate a new refresh token, and the original refresh token deleted. The corresponding implementation code is as follows:
Public Override Async Task Receiveasync (Authenticationtokenreceivecontext context) { varawait _ Refreshtokenservice.get (context. Token); if NULL ) { context. Deserializeticket (refreshtoken.protectedticket); var await _refreshtokenservice.remove (context. Token);} }
However, when the client acquires multiple refresh tokens more than once, only the refresh token that is used to flush access tokens is deleted, and the other refresh tokens are left in the database as unwanted trash. In order to care for the environment, do not litter, we have to solve this problem.
The idea is to delete all the refresh tokens that correspond to this user (resource owner) and the client before generating a new refresh token and saving it to the database. The deletion is based on the condition that ClientID and UserID, because the previous persisted refresh token only saved username, did not save the userid, so refreshtoken to add the UserID property. Then add the deletion method to the Irefreshtokenservice interface of the application layer:
Public Interface irefreshtokenservice{ //... task<bool> Remove (guid clientId, GUID userId);}
(The implementation of this method is omitted)
Then, before saving the refresh token in Cnblogsrefreshtokenprovider, call this method:
Public classcnblogsrefreshtokenprovider:authenticationtokenprovider{PrivateIrefreshtokenservice _refreshtokenservice; Public Override AsyncTask Createasync (Authenticationtokencreatecontext context) {varRefreshtoken =NewRefreshtoken () {//...UserId = (awaitUcenterservice.getuser (context. Ticket.Identity.Name)). UserID,//... }; await _refreshtokenservice.remove (Refreshtoken.clientid, Refreshtoken.userid); if(await_refreshtokenservice.save (Refreshtoken)) {context. Settoken (Refreshtokenid); } }}
This solves the 2nd problem.
ASP. OWIN OAuth: 2 Refresh token issues encountered