On Twitter, you can only make posts that fit in 140 characters. That includes the handles you are tweeting at: #handle. This creates a problem when attempting to tweet at a larger group. In an attempt to create a workaround for my basketball team, I am attempting to create a twitter bot that when tweeted at, it take the text from the tweet and then send a series of tweets # each person's handle on the team.
I started with code from this tutorial. Then I edited out the Wolfram Alpha stuff and came up with this code to start with: (The key and secret arent really xxxxx)
/** ScotsTeamRetweeter **/
/** ======================================= **/
/** Written by John Holland
/** Taken from: Amit Agarwal #labnol on 10/09/2013 **/
/** Tutorial link: http://www.labnol.org/?p=27902 **/
function start() {
Logger.log("Did actually start");
// REPLACE THESE DUMMY VALUES
var TWITTER_CONSUMER_KEY = "XXXXXX";
var TWITTER_CONSUMER_SECRET = "XXXXXX";
var TWITTER_HANDLE = "ScotsTeam";
// Store variables
ScriptProperties.setProperty("TWITTER_CONSUMER_KEY", TWITTER_CONSUMER_KEY);
ScriptProperties.setProperty("TWITTER_CONSUMER_SECRET", TWITTER_CONSUMER_SECRET);
ScriptProperties.setProperty("TWITTER_HANDLE", TWITTER_HANDLE);
ScriptProperties.setProperty("MAX_TWITTER_ID", 0);
// Delete exiting triggers, if any
var triggers = ScriptApp.getScriptTriggers();
for(var i=0; i < triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
// Setup trigger to read Tweets every five minutes
ScriptApp.newTrigger("fetchTweets")
.timeBased()
.everyMinutes(1)
.create();
}
function oAuth() {
var oauthConfig = UrlFetchApp.addOAuthService("twitter");
oauthConfig.setAccessTokenUrl("https://api.twitter.com/oauth/access_token");
oauthConfig.setRequestTokenUrl("https://api.twitter.com/oauth/request_token");
oauthConfig.setAuthorizationUrl("https://api.twitter.com/oauth/authorize");
oauthConfig.setConsumerKey(ScriptProperties.getProperty("TWITTER_CONSUMER_KEY"));
oauthConfig.setConsumerSecret(ScriptProperties.getProperty("TWITTER_CONSUMER_SECRET"));
}
function fetchTweets() {
oAuth();
var twitter_handle = ScriptProperties.getProperty("TWITTER_HANDLE");
var phrase = "lang:en+to:" + twitter_handle;
var search = "https://api.twitter.com/1.1/search/tweets.json?count=5&include_entities=false&result_type=recent&q=";
search = search + encodeString(phrase) + "&since_id=" + ScriptProperties.getProperty("MAX_TWITTER_ID");
var options =
{
"method": "get",
"oAuthServiceName":"twitter",
"oAuthUseToken":"always"
};
try {
var result = UrlFetchApp.fetch(search, options);
if (result.getResponseCode() === 200) {
var data = Utilities.jsonParse(result.getContentText());
if (data) {
var tweets = data.statuses;
for (var i=tweets.length-1; i>=0; i--) {
var question = tweets[i].text.replace(new RegExp("\#" + twitter_handle, "ig"), "");
var answer = "Looks Like it worked"
sendTweet(tweets[i].user.screen_name, tweets[i].id_str, answer);
Logger.log("Tweet should have sent");
}
}
}
} catch (e) {
Logger.log(e.toString());
}
}
function sendTweet(user, reply_id, tweet) {
var options =
{
"method": "POST",
"oAuthServiceName":"twitter",
"oAuthUseToken":"always"
};
var status = "https://api.twitter.com/1.1/statuses/update.json";
status = status + "?status=" + encodeString("#" + user + " " + tweet);
status = status + "&in_reply_to_status_id=" + reply_id;
try {
var result = UrlFetchApp.fetch(status, options);
ScriptProperties.setProperty("MAX_TWITTER_ID", reply_id);
Logger.log(result.getContentText());
}
catch (e) {
Logger.log(e.toString());
}
}
function encodeString (q) {
// Update: 09/06/2013
// Google Apps Script is having issues storing oAuth tokens with the Twitter API 1.1 due to some encoding issues.
// Hence this workaround to remove all the problematic characters from the status message.
var str = q.replace(/\(/g,'{').replace(/\)/g,'}').replace(/\[/g,'{').replace(/\]/g,'}').replace(/\!/g, '|').replace(/\*/g, 'x').replace(/\'/g, '');
return encodeURIComponent(str);
// var str = encodeURIComponent(q);
// str = str.replace(/!/g,'%21');
// str = str.replace(/\*/g,'%2A');
// str = str.replace(/\(/g,'%28');
// str = str.replace(/\)/g,'%29');
// str = str.replace(/'/g,'%27');
// return str;
}
(My understanding is) This code should just cause the twitter bot to post a tweet that says "looks like it worked" when tweeted at.
However I seem to have an authorization problem that I dont understand. I recieve this email most nights:
Your script, ScotsTeamRetweeter, has recently failed to finish successfully. A summary of the failure(s) is shown below. To configure the triggers for this script, or change your setting for receiving future failure notifications, click here.
Summary:
Error Message Count
Authorization is required to perform that action. 18
Details:
Start Function Error Message Trigger End
10/9/13 9:11 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:11 PM
10/9/13 9:12 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:12 PM
10/9/13 9:13 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:13 PM
10/9/13 9:14 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:14 PM
10/9/13 9:15 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:15 PM
10/9/13 9:16 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:16 PM
10/9/13 9:17 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:17 PM
10/9/13 9:18 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:18 PM
10/9/13 9:19 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:19 PM
10/9/13 9:20 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:20 PM
10/9/13 9:21 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:21 PM
10/9/13 9:22 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:22 PM
10/9/13 9:23 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:23 PM
10/9/13 9:24 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:24 PM
10/9/13 9:25 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:25 PM
10/9/13 9:26 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:26 PM
10/9/13 9:27 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:27 PM
10/9/13 9:28 PM fetchTweets Authorization is required to perform that action. time-based 10/9/13 9:28 PM
What should I do to fix my bot?
You need to go into your script editor and call your function directly. (Play button).
After that it will display an authorization screen to you and after that everything will be ok.
Related
I'm testing a .Net Core 2.2 web app using SpecFlow, Selenium, and ChromeDriver and am befuddled by something I've encountered.
I'm using TempData to pass data for an alert message between a page that posts data and a page that displays data. When deployed to IIS locally for debugging, etc, I see the TempData until it is consumed and displayed on a page as formatted message. However, I have a step in a SpecFlow scenario that fails when the alert isn't displayed because the TempData is empty. The SpecFlow scenario follows the same steps that are taken during manual testing.
I am testing a .Net Core 2.2 web project from a referenced XUnit/Specflow test project. The following relevant dependencies are used:
Microsoft.NetCore.App v2.2.0
SpecFlow v3.0.225
SpecFlow.xUnit v3.0.225
Selenium.WebDriver v3.141.0
Selenium.WebDriver.ChromeDriver v76.0.3809.12600
I have confirmed that TempData is populated as expected with the message data when debugging the application locally. Debugging the test shows that TempData is populated upon successful posting of form data but is empty when the resulting OnGet is processed during the redirect.
Here is the post handler for adding an account:
public async Task<IActionResult> OnPostAsync()
{
if (ModelState.IsValid)
{
var user = new IdentityUser { UserName = Account.UserName, Email = Account.EmailAddress };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
Dictionary<string, string> SuccessAlert = new Dictionary<string, string>()
{
{ "Type", "success" },
{ "Title", "Account Added" },
{ "Text", "Account for user " + Account.UserName + " was added successfully."}
};
TempData["Alert"] = SuccessAlert;
_logger.LogInformation($"account added successfully. Alert title: {SuccessAlert["Title"]}");
return RedirectToPage("/Accounts/Index", new
{
area = "Administration"
});
}
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
}
Dictionary<string, string> ErrorAlert = new Dictionary<string, string>()
{
{ "Type", "error" },
{ "Title", "Uh-oh!" },
{ "Text", "Account for user " + Account.UserName + " was not added. See errors below."}
};
TempData["Alert"] = ErrorAlert;
return Page();
}
And the get handler for the /Accounts/Index page that the above redirects to upon successful completion (pretty basic):
public void OnGet()
{
Accounts = _accountService.FindAll();
_logger.LogInformation("AccountsIndexModel.OnGet");
_logger.LogInformation($"Items in TempData: {TempData.Count}");
}
The following are snippets from log files corresponding to the logging in the code above.
Logs for manually adding an account:
2019-08-28 16:18:53.917 -04:00 First.Web.Areas.Administration.Accounts.AddAccountModel [Information] account added successfully. Alert title: Account Added
2019-08-28 16:19:08.587 -04:00 First.Web.Areas.Administration.Accounts.AccountsIndexModel [Information] AccountsIndexModel.OnGet
2019-08-28 16:19:08.588 -04:00 First.Web.Areas.Administration.Accounts.AccountsIndexModel [Information] Items in TempData: 1
And for the SpecFlow automated test:
2019-08-28 17:19:02.014 -04:00 First.Web.Areas.Administration.Accounts.AddAccountModel [Information] account added successfully. Alert title: Account Added
2019-08-28 17:19:02.105 -04:00 First.Web.Areas.Administration.Accounts.AccountsIndexModel [Information] AccountsIndexModel.OnGet
2019-08-28 17:19:02.105 -04:00 First.Web.Areas.Administration.Accounts.AccountsIndexModel [Information] Items in TempData: 0
Here's the SpecFlow scenario:
Scenario: Add new account succeeds
Given I have access to the "Administration" module
When I navigate to the "Add Account" page
And I submit an account with values
| accountname | emailaddress |
| mcullen | mcullen#mnhockey.com |
Then I should be on the "Accounts" page
And I should see a success message with
| title | description |
| Account Added | Account for user |
And the step definition for the account submission step:
[When(#"I submit an account with values")]
public void WhenISubmitAnAccountWithValues(Table table)
{
var accountName = "";
var emailAddress = "";
var row = table.Rows[0];
row.TryGetValue("accountname", out accountName);
row.TryGetValue("emailaddress", out emailAddress);
_accountsAddPage.AccountNameField.SendKeys(accountName);
_accountsAddPage.EmailAddressField.SendKeys(emailAddress);
_accountsIndexPage = _accountsAddPage.Submit();
_context.Set<string>("Accounts", "currentpage");
}
While troubleshooting my issue more, I observed that the browser opened by the ChromeDriver was not being sent the .AspNetCore.Mvc.CookieTempDataProvider cookie in the response header.
To resolve this, I added the following configuration to Startup.cs:
services.Configure<CookieTempDataProviderOptions>(options =>
{
options.Cookie.IsEssential = true;
});
Or alternatively, one could omit the above code and change CheckConsentNeeded for non-essential cookies to false:
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
This is based on the documentation found here. Because my project is for an intranet site, I do not use the CookiePolicyMiddleware and the above solution is appropriate.
I was trying to do local receipt validation and doing testing at present.
But from apple receipt response, I was getting wrong time related response so it become difficult for me to test application at present.
Here is log information:
Here is source code that I used for receipt validation:
public void CheckIfSubscriptionIsActive(bool validateReceipt)
{
ConfigurationBuilder builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
IAppleConfiguration appleConfig = builder.Configure<IAppleConfiguration>();
bool isSubscriptionRunning = false;
if (!string.IsNullOrEmpty(appleConfig.appReceipt))
{
if (validateReceipt)
{
// local receipt verification
var receiptData = System.Convert.FromBase64String(appleConfig.appReceipt);
AppleReceipt receipt = new AppleValidator(AppleTangle.Data()).Validate(receiptData);
foreach (AppleInAppPurchaseReceipt productReceipt in receipt.inAppPurchaseReceipts)
{
Debug.Log("# server date: " + GameManager.Instance.ServerDate + " expire date: " + productReceipt.subscriptionExpirationDate);
int result = DateTime.Compare(GameManager.Instance.ServerDate, productReceipt.subscriptionExpirationDate);
if (result <= 0)
{
isSubscriptionRunning = true;
Debug.Log(" === ==== === Subscription Running: curr date: " + GameManager.Instance.ServerDate + " expire date: " + productReceipt.subscriptionExpirationDate);
}
//Debug.Log("PRODUCTID: " + productReceipt.productID);
//Debug.Log("PURCHASE DATE: " + productReceipt.purchaseDate);
//Debug.Log("EXPIRATION DATE: " + productReceipt.subscriptionExpirationDate);
//Debug.Log("CANCELDATE DATE: " + productReceipt.cancellationDate);
}
if (isSubscriptionRunning)
SubscriptionActivated();
// hide loading...
showLoader = false;
Camera.main.SendMessage("ActivateLoadingDialog", false, SendMessageOptions.DontRequireReceiver);
}
else
{
// server side receipt vecification
appReceipt = appleConfig.appReceipt;
StartCoroutine(CheckSubscriptionStatusServerSideVerification());
}
}
}
Usually Weekly subscription has 3 mins time duration so if I am not getting exact time then how can I do proper testing!!
In above image, I have mention both times, my local time is 15:01:04 and apple receipt response time is 09:37:18.
Using local device receipt validation at present I can't able to test the work. so what way exist for me for testing??
Basically in above problem, I was getting major time difference in Apple receipt response but now I have concluded the solution for this.
local time is 15:01:04
apple receipt response time is 09:37:18
Main reason for this is GMT time difference.
I am living in India so we have 5:30 time difference running.
So in Apple receipt time, I require to add this GMT time difference to get exact time values and after this we can complete our rest coding.
I will post link here with my Unity Support discussion if anybody required that.
I read the full documentation https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/AppleMusicWebServicesReference/SetUpWebServices.html#//apple_ref/doc/uid/TP40017625-CH2-SW1.
I generated JWT with kid,iss , iat exp. But when I hit apple music api
with
"Bearer eyJhbGciOiJFUzI1NiIsImtpZCI6Iko4UTJNTFA5RjMifQ.eyJpYXQiOjE1MTE0Mzc5ODgsImV4cCI6MTUxNjcwODM4OCwiaXNzIjoiN0Y0Ujk4WDRNVCJ9.iPFxiHc475spn2AVmO8-Q2bGOM_Rz5OqhAb-sMQ2cXQlGcbx6pfZaTpjI0r6vAaQnqeoFb8fNvVobjtiC2eyzw" header authorization.
API is https://api.music.apple.com/v1/catalog/us/search?term=khalid&types=albums
I am getting the following response header
{ URL: https://api.music.apple.com/v1/catalog/us/search?term=khalid&types=albums } { status code: 401, headers {
Connection = close;
"Content-Type" = "text/plain; charset=UTF-8";
Date = "Fri, 24 Nov 2017 09:21:29 GMT";
} })
Please help.
Thanks in advance.
I have been trying to send Push Notification to IOS device using Bluemix, In dashboard, It is giving successful response that Notification sent but I didnt receive the same in any of my device.
Note: Getting an response "UserId not set". Please do the needful.
Response:
Device is already registered. Return the device Id - Response is: httpStatus: 200
responseHeaders: {
"Cache-Control" = "no-cache=\"set-cookie, set-cookie2\"";
Connection = "Keep-Alive";
"Content-Type" = "application/json";
Date = "Fri, 31 Jul 2015 02:23:43 GMT";
Expires = "Thu, 01 Dec 1994 16:00:00 GMT";
"Set-Cookie" = "LtpaToken2=OwuD5qhTnGA48q9L5dQ+U2niyL+3R52+0ckK09o1Q2dPfq6ZJeIqXtvlb3mdwCP68o+LrjLdHcZOIqTwnQrgM7E3GPOde80ENO5T73uNbi/nUx1G358YpBu/D7TXO/gN+TT6zv/ShGI+ECDQwQVjkk5o7e8PRNje7QwGTe6ls/KASQ3PnPrt5HaKmdhaCtGqDm/dfbhIdF04uXWsAfLUpBqzrzt3lHd1nxQkOfCHyKp7xl2Rp3lh4x6oIDNMHph04zgukwVOEHftKXN4TgAj8Xd0ug9cGxuKthfPCr2gsoKc0KrOABSODHUhcHjW010YnU0Eam60Yr+iR5axhzs1M8FY83VOXC+CcrQINBfCakvArP0PecwVTK2qfSQSnfPBD+wokCwMX78YMukIXs2fprsFwvNE/ZE9tKIfiz9C9ruVe0c6h79fbUx9C0yuugZMDaOheiEXfgAUL6QVu4cWQG1Ul1HV+8SVq9VKh1GjKd4=; Path=/; HttpOnly";
"Transfer-Encoding" = Identity;
"X-Backside-Transport" = "OK OK";
"X-Cf-Requestid" = "bbb1720e-9d41-44d8-4151-871119ed5a68";
"X-Client-IP" = "60.51.27.236";
"X-Global-Transaction-ID" = 987171295;
"X-Powered-By" = "Servlet/3.0";
}
responseJson: {
createdTime = "2015-07-31T02:23:06Z";
deviceId = "84278A9D-FB54-4E71-ASS1-F5F62113C813";
lastUpdatedTime = "2015-07-31T02:23:06Z";
token = 281e351bfa3350e9e2ca96ec95dbf635363e30c708a14903awew7c670eba06ac;
userId = "UserId not set";
}
Response text: {"deviceId":"84278A9D-FB54-4E71-ASS1-F5F62113C813","token":"281e351bfa3350e9e2ca96ec95dbf635363e30c708a1490xxx7c670eba06ac","userId":"UserId not set","createdTime":"2015-07-31T02:23:06Z","lastUpdatedTime":"2015-07-31T02:23:06Z"}
Can you please check to make sure your push certificate that you have uploaded is not expired. You can find this in the Push Dashboard under Push->Configuration
When you send the push notification is the application in the background? Unless you have code written to specifically handle when a push notification is received and the application is in the foreground you will not see a push notification. You can write some code in the "didReceiveRemoteNotification" function, such as showing an alert for a quick test.
I'm trying to set up Thinktecture's Identity Server 3, but I can't seem to get it to return a refresh token when exchanging an authorization code (or when using the ResourceOwner flow, but I'm going to focus on the authorization code as it's more important to me right now). I get back access tokens and can use them to authenticate just fine, but it doesn't seem to even be generating the refresh tokens that I'm expecting to get back. Is there anything special that I need to do to get Identity Server to return refresh tokens?
I've looked through the documentation, but haven't seen anything that I've set up wrong, and the only thing on their page on refresh tokens that I'm not doing is explicitly requesting the "offline_access" scope when sending the user there for authentication, because whenever I try I get an "invalid scope" error. Therefore, I'm taking Thinktecture's phrasing of "Request the offline_access scope (via code or resource owner flow)" to mean that the offline_access scope is something automatically requested based on the flow you're using.
I've been trying to follow their sample applications (And the source code for the existing Owin Middleware from the Katana Project) as best I can, and my setup is as follows:
I've created a client using their client class, manually specifying the following:
var client = new Client()
{
ClientId = "SomeId",
ClientName = "Client with Authentication Code Flow",
RequireConsent = false, //Setting this to true didn't help
Flow = Flows.AuthorizationCode,
ClientSecrets = new List() {
new ClientSecret("secret")
},
RedirectUris = new List()
{
"localhost:/specific-redirect-path"
}
};
I'm making a call to the Authorization endpoint as follows:
var authorizationEndpoint =
AuthorizationEndpointBase +
"?client_id=" + Uri.EscapeDataString(Options.ClientId) +
"&scope=Default" +
"&response_type=code" +
"&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
"&state=" + Uri.EscapeDataString(state);
Response.Redirect(authorizationEndpoint);
where "Default" is a scope I created.
In my callback, I call the token endpoint as follows:
IReadableStringCollection query = Request.Query;
string code = getValueFromQueryString("code", query);
var tokenRequestParameters = new List>()
{
new KeyValuePair("client_id", Options.ClientId),
new KeyValuePair("redirect_uri", GenerateRedirectUri()),
new KeyValuePair("client_secret", Options.ClientSecret),
new KeyValuePair("code", code),
new KeyValuePair("grant_type", "authorization_code"),
};
var requestContent = new FormUrlEncodedContent(tokenRequestParameters);
HttpResponseMessage response = await _httpClient.PostAsync(TokenEndpoint, requestContent, Request.CallCancelled);
response.EnsureSuccessStatusCode();
string oauthTokenResponse = await response.Content.ReadAsStringAsync();
When I make the call to the token endpoint, my logging on Identity Server displays the following (after the validation of the authorization code):
iisexpress.exe Information: 0 : [Thinktecture.IdentityServer.Core.Validation.TokenRequestValidator]: 7/13/2015 1:44:07 PM +00:00 -- Token request validation success
{
"ClientId": "SomeId",
"ClientName": "Client with Authentication Code Flow",
"GrantType": "authorization_code",
"AuthorizationCode": "f8f795e649044067ebd96a341c5af8c3"
}
iisexpress.exe Information: 0 : [Thinktecture.IdentityServer.Core.ResponseHandling.TokenResponseGenerator]: 7/13/2015 1:44:07 PM +00:00 -- Creating token response
iisexpress.exe Information: 0 : [Thinktecture.IdentityServer.Core.ResponseHandling.TokenResponseGenerator]: 7/13/2015 1:44:07 PM +00:00 -- Processing authorization code request
Debug: [Thinktecture.IdentityServer.Core.Services.Default.DefaultTokenService]: 7/13/2015 1:44:07 PM +00:00 -- Creating access token
Debug: [Thinktecture.IdentityServer.Core.Services.Default.DefaultTokenService]: 7/13/2015 1:44:07 PM +00:00 -- Creating reference access token
iisexpress.exe Information: 0 : [Thinktecture.IdentityServer.Core.Endpoints.TokenEndpointController]: 7/13/2015 1:44:07 PM +00:00 -- End token request
iisexpress.exe Information: 0 : [Thinktecture.IdentityServer.Core.Results.TokenResult]: 7/13/2015 1:44:07 PM +00:00 -- Returning token response.
I'm not sure what else would be pertinent, so I'll provide more information as needed.
You do have to explicitly ask for 'offline_access' in your request. Separate the other scopes you are requesting with a space. (In my examples below I am replacing 'Default' with 'MyApi' to be clear that we are talking about a scope defined by your app.)
&scope=MyApi offline_access
However, you must also grant that client the right to get refresh tokens, it doesn't just happen based on the flow you pick:
var client = new Client()
{
... //All the stuff you were doing before
ScopeRestrictions = new List<string>
{
"MyApi",
StandardScopes.OfflineAccess.Name, //"offline_access" -for refresh tokens
//Other commonly requested scopes:
//StandardScopes.OpenId.Name, //"openid"
//StandardScopes.Email.Name, //"email"
},
}
You may need to add 'offline_access' to your scope store as well. The scope store is the list of scopes that Identity Server knows about. Your question doesn't mention how your scope store is set up in your project, so you may already have it. But if the above doesn't immediately work for you, you may want to look around for code like this in the example you're working from and add OfflineAccess.
var scopeStore = new InMemoryScopeStore(new Scope[]{
StandardScopes.OpenId,
StandardScopes.Profile,
StandardScopes.Email,
StandardScopes.OfflineAccess, //<--- ensure this is here to allow refresh tokens
new Scope{
Enabled = true,
Name = "MyApi"
},
}
Add offline_access value in scope while sending token request
new Client
{
ClientId = "ro.angular",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Address,
"api1",
IdentityServerConstants.StandardScopes.OfflineAccess
},
AllowOfflineAccess = true,
RefreshTokenUsage = TokenUsage.ReUse,
RefreshTokenExpiration = TokenExpiration.Sliding
}