how to extract dynamic part in SIRD router (Simple Router) - playframework-2.6

I have created a SIRD router
class UserRouter #Inject()(controller:UserController) extends SimpleRouter {
override def routes:Routes = {
case GET (p"/signup/:token") =>{ //corresponds to email verification from user after sign up. This was the URL which the server had sent
println("add user request with token"); //TODOM - print token
controller.verifyUser(); //
}
}
}
The code in routes.conf to use the SIRD router is
-> /ws/users WSRouters.UserRouter #use UserRouter for /ws/users
The action is
def verifyUser(token:String) = Action.async{
implicit request => {
println("verifyUser action called with token: "+token) //TODOM - add proper handling and response
Future(Ok("user verified"))
}
}
how do I extract the dynamic part from the url (token) in case GET (p"/signup/:token") and pass it to verifyUser? In normal routes.conf. I can simply do something like GET /home/:id controllers.HomeController.index2(id) but when I do the same thing in SIRD router, I get error that token is not defined
case GET (p"/signup/:token") =>{ //corresponds to email verification from user after sign up. This was the URL which the server had sent
println("add user request with token"); //TODOM - print token
controller.verifyUser(token); //COMPILE ERROR - token is undefined.
}

Correct way is to use $token instead of :token
case GET (p"/signup/$token") =>{ //corresponds to email verification from user after sign up. This was the URL which the server had sent
println("add user request with token"+token); //TODOM - print token
controller.verifyUser(token); //
}

Related

Getting email id value null as response during apple-authentication

I'm implementing apple-authentication in react native using expo-apple-authentication package.
Below is the code which I'm calling on button's onPress.
async handleSocialLogin() {
const { mutate, BB, onSuccess, navigation } = this.props;
try {
const result = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
Alert.alert(JSON.stringify(result))
// signed in
} catch (e) {
Alert.alert(e)
if (e.code === 'ERR_CANCELED') {
// handle that the user canceled the sign-in flow
} else {
// handle other errors
}
}
}
It should return me authentication-token, Full_Name and Email which I requested in scope but It is giving me null for Full_Name and Email.
As per the documentation:
requestedScopes (AppleAuthenticationScope[]) (optional) - Array of user information scopes to which your app is requesting access. Note that the user can choose to deny your app access to any scope at the time of logging in. You will still need to handle null values for any scopes you request. Additionally, note that the requested scopes will only be provided to you the first time each user signs into your app; in subsequent requests they will be null.
You have probably already logged in once and didn't catch the logs. Subsequent log in will result in this data being null

Handle failed Silent Authentication in Open Id Connect

I have an ASP.NET site that uses Open Id Connect to authenticate with Identity Server.
When the authentication token is about to expire I have added a silent authentication(prompt=none) that will renew the token without showing any login dialog to the user.
This works fine as long as the user is still logged in to Identity Server.
If the user is no longer logged in, an "login_required" error is returned. I want to handle this error by just letting it fail silently and redirect the user back to the page where the authentication started.
When being returned with an error to the AuthenticationFailed notification, the RedirectUri dosen't seem to be available though. Are there any way to access the RedirectUri after getting the error?
My configuration looks something like this(abbreviated):
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions {
...
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
// if we failed to authenticate without prompt
if (context.ProtocolMessage.Error == "login_required")
{
context.HandleResponse();
//context.Response.Redirect("The url to the page where RedirectToIdentityProvider was triggered");
return Task.FromResult(0);
}
context.HandleResponse();
context.Response.Write(context.Exception.Message);
return Task.FromResult(0);
},
RedirectToIdentityProvider = async context =>
{
...
if (ShouldReAuthenticate())
{
context.ProtocolMessage.SetParameter("prompt", "none");
}
...
}
}
});
I managed to solve this by using the ISecureDataFormat.Unprotect() method to read the information in the state message.
It can probably could be done more elegantly, but something like this:
if (!string.IsNullOrEmpty(context.ProtocolMessage.State) ||
context.ProtocolMessage.State.StartsWith("OpenIdConnect.AuthenticationProperties="))
{
var authenticationPropertiesString = context.ProtocolMessage.State.Split('=')[1];
AuthenticationProperties authenticationProperties = context.Options.StateDataFormat.Unprotect(authenticationPropertiesString);
return authenticationProperties.RedirectUri;
}

User is always null when using AspNet.Security.OpenIdConnect.Server

I'm trying to generate access tokens for my aspnet core web app. I created the following provider:
public class CustomOpenIdConnectServerProvider : OpenIdConnectServerProvider
{
public override Task ValidateTokenRequest(ValidateTokenRequestContext context)
{
// Reject the token requests that don't use grant_type=password or grant_type=refresh_token.
if (!context.Request.IsPasswordGrantType() && !context.Request.IsRefreshTokenGrantType())
{
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
description: "Only the resource owner password credentials and refresh token " +
"grants are accepted by this authorization server");
return Task.FromResult(0);
}
// Since there's only one application and since it's a public client
// (i.e a client that cannot keep its credentials private), call Skip()
// to inform the server the request should be accepted without
// enforcing client authentication.
context.Skip();
return Task.FromResult(0);
}
public override async Task HandleTokenRequest(HandleTokenRequestContext context)
{
// Resolve ASP.NET Core Identity's user manager from the DI container.
var manager = context.HttpContext.RequestServices.GetRequiredService<UserManager<User>>();
// Only handle grant_type=password requests and let ASOS
// process grant_type=refresh_token requests automatically.
if (context.Request.IsPasswordGrantType())
{
var user = await manager.FindByNameAsync(context.Request.Username);
if (user == null)
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "Invalid credentials.");
return;
}
// Ensure the password is valid.
if (!await manager.CheckPasswordAsync(user, context.Request.Password))
{
if (manager.SupportsUserLockout)
{
await manager.AccessFailedAsync(user);
}
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "Invalid credentials.");
return;
}
if (manager.SupportsUserLockout)
{
await manager.ResetAccessFailedCountAsync(user);
}
var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);
// Note: the name identifier is always included in both identity and
// access tokens, even if an explicit destination is not specified.
identity.AddClaim(ClaimTypes.NameIdentifier, await manager.GetUserIdAsync(user));
identity.AddClaim(OpenIdConnectConstants.Claims.Subject, await manager.GetUserIdAsync(user));
// When adding custom claims, you MUST specify one or more destinations.
// Read "part 7" for more information about custom claims and scopes.
identity.AddClaim("username", await manager.GetUserNameAsync(user),
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
var claims = await manager.GetClaimsAsync(user);
foreach (var claim in claims)
{
identity.AddClaim(claim.Type, claim.Value, OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Create a new authentication ticket holding the user identity.
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
context.Options.AuthenticationScheme);
// Set the list of scopes granted to the client application.
ticket.SetScopes(
/* openid: */ OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.OfflineAccess,
/* email: */ OpenIdConnectConstants.Scopes.Email,
/* profile: */ OpenIdConnectConstants.Scopes.Profile);
// Set the resource servers the access token should be issued for.
ticket.SetResources("resource_server");
context.Validate(ticket);
}
}
This works just fine, I can get the access token and the users are authenticated successfully. The issue that I'm facing here is that in any authorized action method when I do this: var user = await _userManager.GetUserAsync(User); the value for user is always null! Of course, I'm passing the Authorization header with a valid access token and the request goes into actions annotated with Authorize without any problems. It's just the value of user is null. Can anybody tell me whats wrong with my code?
By default, UserManager.GetUserAsync(User) uses the ClaimTypes.NameIdentifier claim as the user identifier.
In your case, ClaimTypes.NameIdentifier - which is no longer considered by the OpenID Connect server middleware as a special claim in 1.0 - is not added to the access token because it doesn't have the appropriate destination. As a consequence, Identity is unable to extract the user identifier from the access token.
You have 3 options to fix that:
Replace the default user identifier claim used by Identity by calling services.Configure<IdentityOptions>(options => options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject); in your Startup.ConfigureServices() method.
Keep using the ClaimTypes.NameIdentifier claim but give it the right destination (OpenIdConnectConstants.Destinations.AccessToken).
Use UserManager.FindByIdAsync(User.FindFirstValue(OpenIdConnectConstants.Claims.Subject)) instead of UserManager.GetUserAsync(User).

How do I fetch profile info from facebook using OAuth plugin in Grails

I am using OAuth plugin for my Grails project for user to log in to my page. I am integrating facebook,google,and linkedIn to my web app. The OAuth plugin uses springSecurityOAuth plugin and respective OAuth plugins for facebook, google and linkedIn.
But the plugin is only fetching the userId from the social sites while I need to extract other profile info like firstname, lastname email etc. How can I achieve this?
I have already got required permissions for email and public_profile from facebook.
UPDATE:: I manually wrote code to get info such as firstname, lastname etc from providers. I am getting the required data from google but not from facebook. Am I doing any wrong here?
PS: I copied the same code from SpringSecurityOAuthService to get the info and made two for respective providers as shown:
def getUserDetailsGoogle(googleAccessToken){
if (provider=='google'){
def response = oauthService.getGoogleResource(googleAccessToken, 'https://www.googleapis.com/oauth2/v1/userinfo')
def googleResponse
try {
googleResponse = JSON.parse(response.body)
} catch (Exception e) {
log.error "Error parsing response from Google. Response:\n${response.body}"
throw new OAuthLoginException('Error parsing response from Google', e)
}
return googleResponse
}
}
def getUserDetailsFacebook(facebookAccessToken){
def response = oauthService.getFacebookResource(accessToken, 'https://graph.facebook.com/me')
def user
try {
facebookResponse = JSON.parse(response.getBody())
} catch (Exception e) {
log.error "Error parsing response from Facebook. Response:\n${response.body}"
throw new OAuthLoginException("Error parsing response from Facebook", e)
}
if (! facebookResponse?.id) {
log.error "No user id from Facebook. Response:\n${response.body}"
throw new OAuthLoginException("No user id from Facebook")
}
return facebookResponse
}
In my Grails 2.5.X app I use pac4j for authenticating with Facebook, Google, etc. by adding these dependencies to BuildConfig.groovy
dependencies {
compile 'org.pac4j:pac4j-core:1.6.0',
compile 'org.pac4j:pac4j-oauth:1.6.0'
}
The relevant controller class is shown below. If you want to look at the source of the OauthService it calls (or anything else), check out the GitHub repository I've linked to.
#Secured(['permitAll'])
class OauthController {
OauthService oauthService
GrailsApplication grailsApplication
SpringSecurityService springSecurityService
UserRegistrationService userRegistrationService
/**
* Starts the OAuth authentication flow, redirecting to the provider's Login URL. An optional callback parameter
* allows the frontend application to define the frontend callback URL on demand.
*/
def authenticate(String provider) {
BaseOAuthClient client = oauthService.getClient(provider)
WebContext context = new J2EContext(request, response)
RedirectAction redirectAction = client.getRedirectAction(context, true, false)
log.debug "Redirecting to ${redirectAction.location}"
redirect url: redirectAction.location
}
/**
* Handles the OAuth provider callback.
*/
def callback(String provider, String error) {
WebContext context = new J2EContext(request, response)
if (!error) {
try {
CommonProfile profile = oauthService.getUserProfile(provider, context)
User registeredUser = userRegistrationService.socialSignIn(profile, provider)
if (!registeredUser.isAttached()) {
// User is trying to register with an OAuth provider (e.g. Twitter, Yahoo), that doesn't provide their
// email address so they need to submit a form to supply us with their email
render view: '/register/confirmEmail', model: [user: registeredUser]
return
}
springSecurityService.reauthenticate(registeredUser.username)
flashHelper.info 'social.login.success': provider
redirect uri: '/'
return
} catch (ex) {
log.error "Error occurred during callback from OAuth2 provider '$provider'", ex
}
} else {
// Most likely explanation is that the user denied access on the consent screen which is not really an error
log.warn "Callback from OAuth2 provider '$provider' failed due to error: $error"
}
flashHelper.warn 'social.login.fail'
redirect uri: '/'
}
}

how to implement friendships/exists in oriceon/oauth-5-laravel twitter api?

iam using oriceon/oauth-5-laravel .help me to implement friendships/exists .I want to post a request to follow particular person.help me.tysm advance
Consider the situation that you want to follow NASA in twitter.NASA is the screen name of NASA.You should add screen name to the url as below.Add this method to your controller and do proper routing.
public function followWithTwitter(Request $request)
{
$token = $request->get('oauth_token');
$verify = $request->get('oauth_verifier');
$tw = \OAuth::consumer('Twitter');
if ( ! is_null($token) && ! is_null($verify))
{
// This was a callback request from twitter, get the token
$token = $tw->requestAccessToken($token, $verify);
// Send a request with it
$result = json_decode($tw->request('https://api.twitter.com/1.1/friendships/create.json?screen_name=NASA&follow=true','POST'), true);
if (!$result){
//do some tasks for calculating and database updation for following
return ("failed");
}
else{
//do some tasks for calculating and database updation for following
return ("success");
}
}
// if not ask for permission first
else
{
// get request token
$reqToken = $tw->requestRequestToken();
// get Authorization Uri sending the request token
$url = $tw->getAuthorizationUri(['oauth_token' => $reqToken->getRequestToken()]);
// return to twitter login url
return redirect((string)$url);
}
}

Resources