I am setting-up a password reset functionality by sending an email with a reset link with asp.net core identity 3.
My first post generates the token and the email body and send the email.
1 [HttpPost("requestPasswordReset")]
2 [AllowAnonymous]
3 public async Task<IActionResult> requestPasswordReset(PasswordResetRequestDto passwordResetRequestDto)
4 {
5 var user = await _userManager.FindByNameAsync(passwordResetRequestDto.Username);
6
7 if (user == null)
8 return Unauthorized();
9
10 var code = await _userManager.GeneratePasswordResetTokenAsync(user);
11 code = System.Web.HttpUtility.UrlEncode(code);
12
13 // For test purpose: this works
14 var decodedCode = System.Web.HttpUtility.UrlDecode(code);
15 IdentityResult passwordChangeResult = await _userManager.ResetPasswordAsync(user, decodedCode, "password");
16 // End For test purpose
17
18 passwordResetRequestDto.EmailToSend.HtmlPart = passwordResetRequestDto.EmailToSend.HtmlPart.Replace("[resetLink]", passwordResetRequestDto.SpaUrl + "?token=" + code);
19
20 var jsonEmailToSend = JsonConvert.SerializeObject(passwordResetRequestDto.EmailToSend);
21 var data = new StringContent(jsonEmailToSend, Encoding.UTF8, "application/json");
22
23 var client = new HttpClient();
24
25 client.BaseAddress = new Uri(_config.GetSection("AppSettings:ApiUrl").Value + "email/sendmail");
26 var postEmail = client.PostAsync(client.BaseAddress, data);
27
28 return Ok();
29 }
Line 13 to 20 are for test only to check that the token can be used to reset the password.
Line 18 builds the reset link to the angular client SPA.
New password is entered in SPA and send to the next post to reset password
1 [HttpPost("resetPassword")]
2 [AllowAnonymous]
3 public async Task<IActionResult> resetPassword(UserDto userDto)
4 {
5 var user = await _userManager.FindByNameAsync(userDto.Username);
6 var password = userDto.Password;
7 var token = userDto.Token;
8 token = System.Web.HttpUtility.UrlDecode(token);
9
10 IdentityResult passwordChangeResult = await _userManager.ResetPasswordAsync(user, token, userDto.Password);
11
12 if (passwordChangeResult.Succeeded)
13 return Ok();
14
15 return BadRequest();
16 }
Line 10 returns 'invalid token'
I have checked that token in this line is the same that was generated by
var code = await _userManager.GeneratePasswordResetTokenAsync(user);
in first post.
Can someone help?
Calling .ResetPasswordAsync() will update user's SecurityStamp, meaning all tokens generated are now invalid.
For testing purpose, please remove the call .ResetPasswordAsync() and use the following code to verify token.
var isValidToken = await _userManager.VerifyUserTokenAsync(
user,
_userManager.Options.Tokens.PasswordResetTokenProvider,
UserManager<TUser>.ResetPasswordTokenPurpose,
code
);
Please note, <TUser> is your IdentityUser class.
Related
I am trying to integrated Google Calendar Integration to my web site. Everything run on local host is perfect. But when I publish it in to domain it give me some error.
This bt.mypage.kr page can’t be foundNo webpage was found for the web
address:
http://bt.mypage.kr/o/oauth2/v2/auth?scope=https://www.googleapis.com/auth/calendar+https://www.googleapis.com/auth/calendar.events&access_type=online&include_granted_scopes=true&response_type=code&state=state_parameter_passthrough_value&redirect_uri=http://bt.mypage.kr/Schedule/UI/oauth2callback&client_id=myclientid
My code some thing like this:
public ActionResult AuthRedirect()
{
var url = "http://" + Request.Host;
sting redirect_uri = url + "/Schedule/UI/oauth2callback"
GoogleParram gpr = new GoogleParram(url);
string rerectUrl = "https://accounts.google.com/o/oauth2/v2/auth?" +
"scope=https://www.googleapis.com/auth/calendar+https://www.googleapis.com/auth/calendar.events&" +
"access_type=online&" +
"include_granted_scopes=true&" +
"response_type=code&" +
"state=state_parameter_passthrough_value&" +
"redirect_uri=" + url + "/Schedule/UI/oauth2callback&" +
"client_id=" + gpr.client_id;
return Redirect(rerectUrl);
}
public void oauth2callback(string code, string error, string state)
{
if (string.IsNullOrWhiteSpace(error))
{
this.GetTokens(code);
}
}
public IActionResult GetTokens(string code)
{
var url = "http://" + Request.Host;
GoogleParram gpr = new GoogleParram(url);
var request = new RestRequest();
request.AddQueryParameter("client_id", gpr.client_id);
request.AddQueryParameter("client_secret", gpr.client_secret);
request.AddQueryParameter("code", code);
request.AddQueryParameter("grant_type", "authorization_code");
request.AddQueryParameter("redirect_uri", url + "/Schedule/UI/oauth2callback&");
RestClient restClient = new RestClient(gpr.token_uri);
var response = restClient.Post(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
gpr.WriteToken(response.Content);
return RedirectToAction("Calendar");
}
return RedirectToAction("Calendar");
}
My Credentials
I hope anybody knows how to solve. Bundle of Thanks in advance.
I am trying to use the Dynamics 365 Web API to GET records. The fetch query generated is too long to use a normal GET query so a workaround is to POST the request instead.
I have simplifed the fetch statement here for ease.
Ignore the lack of async/await and use of .Result, that can easily be sorted afterwards.
Code:
var clientcred = new ClientCredential(Config.ClientId, Config.ClientSecret);
var authenticationContext = new AuthenticationContext($"{Config.AadInstance}{Config.TenantId}");
var authenticationResult = authenticationContext.AcquireTokenAsync(Config.DynamicsUrl, clientcred).Result;
var token = authenticationResult.AccessToken;
var client = new HttpClient();
client.BaseAddress = new Uri("https://foobar.crm4.dynamics.com");
client.Timeout = new TimeSpan(0, 2, 0);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("OData-Version", "4.0");
client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
client.DefaultRequestHeaders.Add("Prefer", "odata.include-annotations=\"*\"");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var req = new HttpRequestMessage(System.Net.Http.HttpMethod.Post, $"/api/data/v9.1/$batch");
var content = "--batch_rob--\n" +
"Content-Type: application/http\n" +
"Content-Transfer-Encoding: binary\n" +
$"GET {Config.BaseUrl}contacts?fetchXml=<fetch count=\"10\" ><entity name=\"contact\" ><attribute name=\"fullname\" /></entity></fetch> HTTP/1.1\n" +
"OData-Version: 4.0\n" +
"OData-MaxVersion: 4.0\n" +
"--batch_rob--";
using (var content2 = new MultipartContent())
{
content2.Add(new StringContent(content));
content2.Headers.Remove("Content-Type");
content2.Headers.TryAddWithoutValidation("Content-Type", "multipart/mixed;boundary=batch_rob");
var request = new HttpRequestMessage(System.Net.Http.HttpMethod.Post, $"/api/data/v9.1/$batch")
{
Content = content2
};
var response = client.SendAsync(request).Result;
var outcome2 = response.Content.ReadAsStringAsync().Result;
}
This all compiles and appears to run fine. The response however does not contain the JSON I expect (the result of the GET query) but rather is just:
--batchresponse_20851dc6-4ff6-4914-a749-66f451985f67--
Any idea what I have missed?
This is based on the example demonstrated here:
https://dreamingincrm.com/2017/01/15/executing-large-fetchxml-with-webapi/
I'm trying to compile channel statistics for a list of channels and for the first page it works, but when I invoke the next page using the token it gives me an error that the URI string is too long.
I'm using .NET core 2.2 and Google.Apis.YouTube.v2 V1.39.0.1572. And the code I use is really simple:
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
ApiKey = Startup.Configuration["YTConfigurations:ApiKey"],
ApplicationName = this.GetType().ToString()
});
ChannelsResource.ListRequest channelsListRequest = youtubeService.Channels.List("snippet,statistics,brandingSettings,topicDetails");
channelsListRequest.Id = string.Join(",", channelEntities.Select(v => v.YTChannelId));
channelsListRequest.MaxResults = 50;
do
{
pageCounter++;
channelListResponse = channelsListRequest.Execute();//here is the error after page 1
foreach (var listResult in channelListResponse.Items)
{
channelEntity = channelEntities.FirstOrDefault(v => v.YTChannelId == listResult.Id);
channelEntity = Mapper.Map(listResult, channelEntity);
_repository.UpdateChannel(channelEntity);
}
if (channelListResponse.NextPageToken != null)
{
channelsListRequest.PageToken = channelListResponse.NextPageToken;
}
} while (channelListResponse.Items.Count == 50 && channelListResponse.NextPageToken != null);
When I execute this is what I get:
System.UriFormatException: Invalid URI: The Uri string is too long.
at System.UriHelper.EscapeString(String input, Int32 start, Int32 end, Char[] dest, Int32& destPos, Boolean isUriString, Char force1, Char force2, Char rsvd)
at System.Uri.EscapeDataString(String stringToEscape)
at Google.Apis.Requests.RequestBuilder.<>c.<BuildUri>b__25_0(KeyValuePair`2 x) in C:\Apiary\2019-05-01.11-08-18\Src\Support\Google.Apis.Core\Requests\RequestBuilder.cs:line 108
at System.Linq.Enumerable.SelectListIterator`2.ToArray()
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at Google.Apis.Requests.RequestBuilder.BuildUri() in C:\Apiary\2019-05-01.11-08-18\Src\Support\Google.Apis.Core\Requests\RequestBuilder.cs:line 107
at Google.Apis.Requests.RequestBuilder.CreateRequest() in C:\Apiary\2019-05-01.11-08-18\Src\Support\Google.Apis.Core\Requests\RequestBuilder.cs:line 332
at Google.Apis.Requests.ClientServiceRequest`1.CreateRequest(Nullable`1 overrideGZipEnabled) in C:\Apiary\2019-05-01.11-08-18\Src\Support\Google.Apis\Requests\ClientServiceRequest.cs:line 257
at Google.Apis.Requests.ClientServiceRequest`1.ExecuteUnparsedAsync(CancellationToken cancellationToken) in C:\Apiary\2019-05-01.11-08-18\Src\Support\Google.Apis\Requests\ClientServiceRequest.cs:line 229
at Google.Apis.Requests.ClientServiceRequest`1.Execute() in C:\Apiary\2019-05-01.11-08-18\Src\Support\Google.Apis\Requests\ClientServiceRequest.cs:line 167
at ChannelHarvester.Controllers.ChannelHarvester.extractStats(ICollection`1 channelEntities) in C:\Guayaba Projects\YT_ChannelHarvester\ChannelHarvester\Controllers\ChannelHarvester.cs:line 106
Am I doing something wrong? Please let me know if there is something I can fix on my end.
Thanks!
Well, I'm really sorry to have bothered you guys. I found what it was, in the following code I thought I was joining only 50 IDs, but apparently in some occasions I was sending a LOT more.
channelsListRequest.Id = string.Join(",", channelEntities.Select(v => v.YTChannelId));
So I guess we can close this
I tried to follow tutorial jersey 2, but I am falling into this exception that I cant shake off. The request has not reached the server as yet.
Exception
http://localhost:8080/TBOAirAPIEndPoint/air/Consolidation
Jul 09, 2015 6:59:42 PM org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor aroundWriteTo
SEVERE: MessageBodyWriter not found for media type=application/json, type=class com.mcruiseon.airapi.tunnel.common.UserNamePassword, genericType=class com.mcruiseon.airapi.tunnel.common.UserNamePassword.
#Test
public void test1PostSearch() throws Exception {
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient();
URI url = UriBuilder.fromUri(
"http://" + LOCALHOST + "/TBOAirAPIEndPoint/air/Consolidation")
.build();
WebTarget service = client.target(url);
Response response = (Response) service
.path("search")
.path("/BOM/BLR/" + FlightSearchType.OneWay.ordinal()
+ "/3/9/2015" + "/6/10/2015/"
+ CabinClass.Economy.ordinal() + "/1/1/1")
.request()
--exception--.post(Entity.entity(
new UserNamePassword("clientID", "password"),
MediaType.APPLICATION_JSON), Response.class);
assertNotNull(response);
}
Post Call
#POST
#Path("search/{origin}/{destination}/{flightSearchType}/{departureDayOfMonth}/{departureMonthOfYear}/{departureYear}/{returnDayOfMonth}/{returnMonthOfYear}/{returnYear}/{cabinClass}/{adultCount}/{childCount}/{infantCount}")
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public Response search(#PathParam("origin") String origin,
#PathParam("destination") String destination,
#PathParam("flightSearchType") int flightSearchType,
#PathParam("departureDayOfMonth") int departureDayOfMonth,
#PathParam("departureMonthOfYear") int departureMonthOfYear,
#PathParam("departureYear") int depatureYear,
#PathParam("returnDayOfMonth") int returnDayOfMonth,
#PathParam("returnMonthOfYear") int returnMonthOfYear,
#PathParam("returnYear") int returnYear,
#PathParam("cabinClass") int cabinClass,
#PathParam("adultCount") int adultCount,
#PathParam("childCount") int childCount,
#PathParam("infantCount") int infantCount,
JAXBElement<UserNamePassword> userNamePassword) {
Edit
ClientConfig config = new ClientConfig();
config.property(MessageProperties.LEGACY_WORKERS_ORDERING, true);
Client client = ClientBuilder.newClient();
URI url = UriBuilder.fromUri(
"http://" + LOCALHOST + "/TBOAirAPIEndPoint/air/Consolidation")
.build();
WebTarget service = client.target(url);
Response response = (Response) service
.path("search")
.path("/BOM/BLR/" + FlightSearchType.OneWay.ordinal()
+ "/3/9/2015" + "/6/10/2015/"
+ CabinClass.Economy.ordinal() + "/1/1/1")
.request()
.post(Entity.entity(
new UserNamePassword("clientID", "password"),
MediaType.APPLICATION_JSON), Response.class);
assertNotNull(response);
Edit
I found migration guide from 1.x to 2.x. Frankly I dont care, I just this to work so I am happy to set "jersey.config.workers.legacyOrdering") to true in ResourceConfig or ClientConfig properties.. But where and how do I set this variable to true. Where is the ClientConfig
I also modified the junit code to
ClientConfig config = new ClientConfig();
config.property("jersey.config.workers.legacyOrdering", true);
NOR did this work
ClientConfig config = new ClientConfig();
config.property(MessageProperties.LEGACY_WORKERS_ORDERING, true);
I downloaded Genson 1.3 for this to work. Version 0.94 does not work.
Here's a code snippet I found from the gdata sample codes. I tried incorporating this, but to no effect.
public ContactsExample(ContactsExampleParameters parameters)
throws MalformedURLException, AuthenticationException {
projection = parameters.getProjection();
String url = parameters.getBaseUrl() + (parameters.isGroupFeed() ? "groups/" : "contacts/") + parameters.getUserName() + "/" + projection;
feedUrl = new URL(url);
service = new ContactsService("MYAPP");
String userName = parameters.getUserName();
String password = parameters.getPassword();
if (userName == null || password == null) {
return;
}
service.setUserCredentials(userName, password);
}
I have these doubts:
1. Is the name specified as 'MYAPP' any random name, or does it have any significance?
2. Which is the userName and password supposed to be used here?