Dart Aqueduct server basic authorization - dart

I'm learning about how authentication works using the Aqueduct framework.
In my channel.dart file I have a route:
router
.route('/protected')
.link(() => Authorizer.basic(validator))
.link(() => ProtectedController());
But I don't know how to create the validator. In the docs I see that I can make a custom Authorizer without using an AuthServer. The code example is this:
class BasicValidator implements AuthValidator {
#override
FutureOr<Authorization> validate<T>(AuthorizationParser<T> parser, T authorizationData, {List<AuthScope> requiredScope}) {}
var user = await userForName(usernameAndPassword.username);
if (user.password == hash(usernameAndPassword.password, user.salt)) {
return Authorization(...);
}
return null;
}
}
I'd like to make a basic working example, but this is the closest that I could get:
class BasicValidator implements AuthValidator {
#override
FutureOr<Authorization> validate<T>(AuthorizationParser<T> parser, T authorizationData, {List<AuthScope> requiredScope}) {
final validUsername = 'bob';
final validPassword = 'password123';
// How do I get the parsed username?
// How do I get the parsed password?
if (parsedUsername == validUsername && parsedPassword == validPassword) {
// How do I create an Authorization?
return Authorization(...);
}
return null;
}
// What is this?
#override
List<APISecurityRequirement> documentRequirementsForAuthorizer(APIDocumentContext context, Authorizer authorizer, {List<AuthScope> scopes}) {
return null;
}
}
Could anyone show me a basic working example of Basic authorization validator?

authorizationData is an instance of AuthBasicCredentials when using Authorizer.basic. An object of this type has username and password fields derived from parsing the 'Authorization' header from a request.
An Authorization object is a container for data related to the authorized resource owner (such as their user ID). It is used by subsequent controllers to control authorization without having to look up the authorized user again; you should populate it with any authorization information you have available.
documentRequirementsForAuthorizer is used during OpenAPI document generation. An Authorizer that uses your validator will encode the returned security requirements into the OpenAPI operations being secured.
See also http://aqueduct.io/docs/auth/authorizer/#using-authorizers-without-authserver.
#override
FutureOr<Authorization> validate<T>(AuthorizationParser<T> parser, T authorizationData, {List<AuthScope> requiredScope}) {
final validUsername = 'bob';
final validPassword = 'password123';
final credentials = authorizationData as AuthBasicCredentials;
if (credentials.username == validUsername
&& credentials.password == validPassword) {
return Authorization(
null, // no client ID for basic auth
await getUserIDFromUsername(validUsername), // This is your problem to solve
this, // always this
credentials: credentials // if you want to pass this info along
);
}
return null;
}

Related

#FrameworkEndpoint to oAuth 2.0 revokeToken

I have a #FrameworkEndpoint based controller in order to implement DELETE of token as follows
#FrameworkEndpoint
public class RevokeTokenEndpoint {
#Resource(name = "tokenServices")
ConsumerTokenServices tokenServices;
#RequestMapping(method = RequestMethod.DELETE, value = "/oauth/token")
#ResponseBody
public void revokeToken(HttpServletRequest request) {
String authorization = request.getHeader("Authorization");
if (authorization != null && authorization.contains("Bearer")) {
String tokenId = authorization.substring("Bearer".length() + 1);
System.out.println("tokenId : " + tokenId);
tokenServices.revokeToken(tokenId);
//tokenStore.removeRefreshToken(token);
}
}
}
My request is a DELETE (http://localhost:8081/oauth/token request with Authorization Bearer ce8b914d-57db-4ad7-86d9-be2d7f47b203
The problem is that the end-point does not get hit at all and the message returned back is "unauthorized". When I fire the request with Authorization Basic with client_id and secret then it does hit the end-point. But then in that case I will be forced to pass another parameter or header to carry the token and change the code to get the value of token from this another parameter or header.
I believe, the ideal way would be tell spring security to allow unauthorized calls to remove the access token. Does this make sense ? and if yes then how ?
I have implemented like this
#PostMapping("/token/revoke")
public ResponseEntity<Boolean> revokeToken(
#RequestHeader(value = "Authorization") final String tokenId) {
try {
final String[] token = tokenId.split("\\s+");
tokenServices.revokeToken(token[1]);
return new ResponseEntity<>(true, HttpStatus.OK);
} catch (final Exception e) {
LOGGER.info("Exception while removing access token");
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
tokenId contains Bearer b133b6ee-59db-4809-b546-e47cb879bea1. In this way i have implemented and its working fine

How to check oauth credentials are valid or not in Netsuite?

I could get access token and secret, but they are not working some times . I am getting error like invalid login attempt. I don't know how to solve this issue.
I have all the credentials for oauth , these are
Consumer key
Consumer secret
Account id
Token id
Token Secret
Script id
Deployment id
I want to know my oauth credentials by group or individual are valid or not .
I think the only way is to hit SuiteTalk or RESTlet.
If you are using RESTlet, you must ensure that the user has access to RESTlet in the deployment options.
I have a working code using Java (using Scribe Java Library) which can be found here
MAIN Method:
OAuthConfig authConfig = new OAuthConfig("CONSUMER_KEY", "CONSUMER_SECRET", null, SignatureType.Header, null, null);
Token token = new Token("TOKEN_ID", "TOKEN_SECRET");
OAuth10aServiceImpl auth10aServiceImpl = new OAuth10aServiceImpl(new NetSuiteApi(), authConfig);
OAuthRequest request = new OAuthRequest(Verb.GET, "RESTLET_URL");
request.setRealm("NS_ACCOUNT_ID");
auth10aServiceImpl.signRequest(token, request);
Response response = request.send();
Also you will need to write NetSuiteApi Class extending DefaultApi10a
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.Token;
public class NetSuiteApi extends DefaultApi10a {
#Override
public String getAccessTokenEndpoint() {
// TODO Auto-generated method stub
return null;
}
#Override
public String getAuthorizationUrl(Token arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public String getRequestTokenEndpoint() {
// TODO Auto-generated method stub
return null;
}
}

Limit user authorization to my Google domain

It should be possible to limit Google API OAuth2 requests to a specific google domain. It used to be possible by hacking on the end &hd=mydomain.com to the request. Using the new MVC auth stuff it seems no longer possible. Any ideas how?
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new AppGoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "***.apps.googleusercontent.com",
ClientSecret = "******"
},
Scopes = new[] { DriveService.Scope.Drive },
DataStore = new FileDataStore(HttpContext.Current.Server.MapPath("~/App_Data"), true) ,
});
public override string GetUserId(Controller controller)
{
// In this sample we use the session to store the user identifiers.
// That's not the best practice, because you should have a logic to identify
// a user. You might want to use "OpenID Connect".
// You can read more about the protocol in the following link:
// https://developers.google.com/accounts/docs/OAuth2Login.
var user = controller.Session["user"];
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}
public class AppGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
public AppGoogleAuthorizationCodeFlow(GoogleAuthorizationCodeFlow.Initializer initializer) : base(initializer) { }
public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(String redirectUri)
{
var authorizeUri = new Uri(AuthorizationServerUrl).AddQuery("hd", "ourgoogledomain.com"); //is not in the request
var authUrl = new GoogleAuthorizationCodeRequestUrl(authorizeUri)
{
ClientId = ClientSecrets.ClientId,
Scope = string.Join(" ", Scopes),
RedirectUri = redirectUri,
//AccessType = "offline",
// ApprovalPrompt = "force"
};
return authUrl;
}
}
Passing a hd parameter is indeed the correct way to limit users to your domain. However, it is important that you verify that the user does actually belong to that hosted domain. I see in your answer that you figured out how to add this parameter back in to your request, so I will address the second part of this.
The issue is that the user can actually modify the requested URL in their client and remove the hd parameter! So while it's good to pass this parameter for the best UI for your users, you need to also verify that authenticated users do actually belong to that domain.
To see which hosted Google Apps for Work domain (if any) the user belongs to, you must include email in the list of scopes that you authorize. Then, do one of the following:
Option 1. Verify the ID Token.
When you exchange your code for an access token, the token endpoint will also return an ID Token in the id_token param (assuming you include an identity scope in your request such as email). If the user is part of a hosted domain, a hd claim will be present, you should check that it is present, and matches what you expect.
You can read more about ID tokens on Google's OpenID Connect docs (including some links to sample code and libraries to help you decode them). This tool can decode ID Tokens during testing.
Option 2. Call UserInfo
Once you have the OAuth Access Token, perform a GET request to https://www.googleapis.com/plus/v1/people/me/openIdConnect with the Access Token in the header. It will return a JSON dictionary of claims about the user. If the user is part of a hosted domain, a hd claim will be present, you should check that it is present, and matches what you expect.
Read more in the documentation for Google's UserInfo endpoint.
The main difference between Option 1 and Option 2 is that with the ID Token, you avoid another HTTP round-trip to the server making it faster, and less error-prone. You can try out both these options interactively using the OAuth2 Playground.
With the updated for .NET core package previous answers are no longer suitable. Fortunately in the new implementation there is a way to hook into authentication events to perform such task.
You will need a class that will handle 2 events - the one that fired before you go to Google and the one for when coming back. In first you limit which domain can be used to sign-in and in the second you ensure that the email with the right domain was in fact used for signin:
internal class GoogleAuthEvents : OAuthEvents
{
private string _domainName;
public GoogleAuthEvents(string domainName)
{
this._domainName = domainName?.ToLower() ?? throw new ArgumentNullException(nameof(domainName));
}
public override Task RedirectToAuthorizationEndpoint(OAuthRedirectToAuthorizationContext context)
{
return base.RedirectToAuthorizationEndpoint(new OAuthRedirectToAuthorizationContext(
context.HttpContext,
context.Options,
context.Properties,
$"{context.RedirectUri}&hd={_domainName}"));
}
public override Task TicketReceived(TicketReceivedContext context)
{
var emailClaim = context.Ticket.Principal.Claims.FirstOrDefault(
c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
if (emailClaim == null || !emailClaim.Value.ToLower().EndsWith(_domainName))
{
context.Response.StatusCode = 403; // or redirect somewhere
context.HandleResponse();
}
return base.TicketReceived(context);
}
}
and then you need to pass this "events handler" to the middleware via GoogleOptions class:
app.UseGoogleAuthentication(new GoogleOptions
{
Events = new GoogleAuthEvents(Configuration["Authentication:Google:LimitToDomain"])
})
#AMH, to do in simplest way you should create your own Google Provider, override method ApplyRedirect and append additional parameter like hd to address which will be using to redirect to a new google auth page:
public class GoogleAuthProvider : GoogleOAuth2AuthenticationProvider
{
public override void ApplyRedirect(GoogleOAuth2ApplyRedirectContext context)
{
var newRedirectUri = context.RedirectUri;
newRedirectUri += string.Format("&hd={0}", "your_domain.com");
context.Response.Redirect(newRedirectUri);
}
}
After that just link new provider to your options:
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "your id",
ClientSecret = "your secret",
Provider = new GoogleAuthProvider(),
});
Having downloaded the source, I was able to see it is easy to subclass the request object, and add custom parameters:
public class GoogleDomainAuthorizationCodeRequestUrl : GoogleAuthorizationCodeRequestUrl
{
/// <summary>
/// Gets or sets the hosted domain.
/// When you want to limit authorizing users from a specific domain
/// </summary>
[Google.Apis.Util.RequestParameterAttribute("hd", Google.Apis.Util.RequestParameterType.Query)]
public string Hd { get; set; }
public GoogleDomainAuthorizationCodeRequestUrl(Uri authorizationServerUrl) : base(authorizationServerUrl)
{
}
}
public class AppGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
public AppGoogleAuthorizationCodeFlow(GoogleAuthorizationCodeFlow.Initializer initializer) : base(initializer) { }
public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(String redirectUri)
{
var authUrl = new GoogleDomainAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl))
{
Hd = "mydomain.com",
ClientId = ClientSecrets.ClientId,
Scope = string.Join(" ", Scopes),
RedirectUri = redirectUri
};
return authUrl;
}
}
I found this post when searching for a solution to specify the hosted domain with OpenID Connect integration to Google. I was able to get it working using the Google.Apis.Auth.AspNetCore package and the following code.
In Startup.cs
services.AddGoogleOpenIdConnect(options =>
{
options.ClientId = "*****";
options.ClientSecret = "*****";
options.SaveTokens = true;
options.EventsType = typeof(GoogleAuthenticationEvents);
});
services.AddTransient(provider => new GoogleAuthenticationEvents("example.com"));
Don't forget app.UseAuthentication(); in the Configure() method of Startup.cs.
Then the authentication events class
public class GoogleAuthenticationEvents : OpenIdConnectEvents
{
private readonly string _hostedDomain;
public GoogleAuthenticationEvents(string hostedDomain)
{
_hostedDomain = hostedDomain;
}
public override Task RedirectToIdentityProvider(RedirectContext context)
{
context.ProtocolMessage.Parameters.Add("hd", _hostedDomain);
return base.RedirectToIdentityProvider(context);
}
public override Task TicketReceived(TicketReceivedContext context)
{
var email = context.Principal.FindFirstValue(ClaimTypes.Email);
if (email == null || !email.ToLower().EndsWith(_hostedDomain))
{
context.Response.StatusCode = 403;
context.HandleResponse();
}
return base.TicketReceived(context);
}
}

ServiceStack authentication request fails

I am trying to set up authentication with my ServiceStack service by following this tutorial.
My service is decorated with the [Authenticate] attribute.
My AppHost looks like this:
public class TestAppHost : AppHostHttpListenerBase
{
public TestAppHost() : base("TestService", typeof(TestService).Assembly) { }
public static void ConfigureAppHost(IAppHost host, Container container)
{
try
{
// Set JSON web services to return idiomatic JSON camelCase properties.
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
// Configure the IOC container
IoC.Configure(container);
// Configure ServiceStack authentication to use our custom authentication providers.
var appSettings = new AppSettings();
host.Plugins.Add(new AuthFeature(() =>
new AuthUserSession(), // use ServiceStack's session class but fill it with our own data using our own auth service provider
new IAuthProvider[] {
new UserCredentialsAuthProvider(appSettings)
}));
}
}
where UserCredentialsAuthProvider is my custom credentials provider:
public class UserCredentialsAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
try
{
// Authenticate the user.
var userRepo = authService.TryResolve<IUserRepository>();
var user = userRepo.Authenticate(userName, password);
// Populate session properties.
var session = authService.GetSession();
session.IsAuthenticated = true;
session.CreatedAt = DateTime.UtcNow;
session.DisplayName = user.FullName;
session.UserAuthName = session.UserName = user.Username;
session.UserAuthId = user.ID.ToString();
}
catch (Exception ex)
{
// ... Log exception ...
return false;
}
return true;
}
}
In my user tests I initialize and start my TestAppHost on http://127.0.0.1:8888, then use JsonServiceClient to authenticate itself to the service like so:
var client = new JsonServiceClient("http://127.0.0.1:8888/")
var response = client.Send<AuthResponse>(new Auth
{
provider = UserCredentialsAuthProvider.Name,
UserName = username,
Password = password,
RememberMe = true
});
But getting the following exception:
The remote server returned an error: (400) Bad Request.
at System.Net.HttpWebRequest.GetResponse()
at ServiceStack.ServiceClient.Web.ServiceClientBase.Send[TResponse](Object request)...
The ServiceStack.ServiceInterface.Auth.Auth request contains the correct username and passsword, and the request is being posted to:
http://127.0.0.1:8888/json/syncreply/Auth
I am not sure why the URL is not /json/auth/credentials or what I might be doing wrong. Any suggestions?
UPDATE
Tracing the chain of events up the stack I found the following:
JsonDataContractSerializer.SerializeToStream correctly serializes the Auth request into Json. However, the System.Net.HttpRequestStream passed to JsonDataContractDeserializer by EndpointHandlerBase has a stream of the correct length that is filled with nulls (zero bytes). As a result, the request object passed to CredentialsAuthProvider.Authenticate has nulls in all its properties.
How can the HTTP stream get stripped of its data?
Got it!!!
The problem was the following pre-request filter that I added for logging purposes in TestAppHost.Configure:
PreRequestFilters.Add((httpReq, httpRes) =>
{
LastRequestBody = httpReq.GetRawBody();
});
as seen here.
When the GetRawBody() method reads the request InputStream it leaves it in the EOS state, and all subsequent read attempts return nothing.
So obviously GetRawBody() can only be safely used with buffered streams, but unfortunately it quietly causes a very nasty bug instead of throwing an exception when used with a non-buffered stream.

Authenticating users with auth token in query string with ASP.NET MVC

[This question relates to ASP.NET MVC4, and it is about best-practice approach - so please, don't suggest hacks.]
I want to authenticate users using an auth token sent in the request URL. It works similarly to a password reset token, except in this case it does not go to a reset page but instead grants access to some portion of the site. The idea is to send the URL with the auth token to a verified email address of the user. Users can click the link and perform some actions without typing their password.
Out-of-the-box, ASP.NET has the [Authorize] attribute and the SimpleMembershipProvider - these seem to work great, but they do some voodoo magic under the hood (like auto-generating database tables), so I don't know how to extend them to add this link-based auth token.
I don't expect an exact answer, but please do point me to the right direction.
Thanks!
Uf, broad question. But I will try at least to direct you to a right direction.
So first if suggest that you use Forms Authentication as a base, but you will have to customize using of it. And I presume that you do not want to use cookies for the authentication as this is native behaviour of the Forms Authentication.
The most important point you should consider to have it you custom query string token based authentication.
Create a login action and in this action you will authorize the user, if he have granted access you ask FormsAuthentication to create AuthCookie. For the further on you just take the httpCookie.Value as your auth token that you will carry in query string.
You need to implement the Application_BeginRequest in the Global.asax that will handle this query string tokens and translate it into the cookie. With this approach you can leverage all the ASP.NET Forms Authentication infrastructure.
This is quite high level picture w/o code. If you need more detail help I can also provide it to you.
You should just use a regular Action that accepts HttpGet.
Upon receiving the token, immediately invalid it so it can't be used again.
Also, only accept tokens that are within your pre-defined range of time period, like 24 or 72 hours.
Thank you Peter for idea.
If smb need to create JWT token authorization for old ASP.NET MVC5.I wrote small example. I don't serialize cookie to JWT. I create a JWT and after I am checking it in the BeginRequest. If everything is ok, I create a cookie and set it to the httpContext.Request. I used authentication mode="Forms" for application and it require cookies.
For create JWT token:
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
[AllowAnonymous]
[HttpPost]
public ActionResult LoginJWT(LoginViewModel model)
{
ActionResult response = null;
if (ModelState.IsValid)
{
if (true) //todo: check user login&password
{
var payload = new Dictionary<string, object>
{
{ "iss", "subject" },
{ "sub", "api" },
{ "exp", DateTimeOffset.UtcNow.AddHours(2).ToUnixTimeSeconds()},
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds()},
{ "jti", Guid.NewGuid() },
{ "uid", "64" } //custom field for identificate user
};
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
var token = encoder.Encode(payload, secret);
response = Content(token);
}
else
{
response = new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Login or password are not found");
}
}
else
{
response = new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Errors in Model");
}
return response;
}
For check JWT token in Global.asax:
public override void Init()
{
this.BeginRequest += this.BeginRequestHandler;
base.Init();
}
private void BeginRequestHandler(object sender, EventArgs e)
{
var bearerToken = this.Context.Request.Headers["Authorization"];
if (bearerToken != null)
{
var token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
int userId = 0;
try
{
IJsonSerializer serializer = new JsonNetSerializer();
var provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
var json = decoder.DecodeToObject<IDictionary<string, string>>(token, secret, verify: true);
if (json.TryGetValue("uid", out var uid))
{
userId = Convert.ToInt32(uid);
}
}
catch (TokenExpiredException)
{
Console.WriteLine("Token has expired");
}
catch (SignatureVerificationException)
{
Console.WriteLine("Token has invalid signature");
}
if (userId != 0)
{
// check user by id, if found create cookie.
}
}
}
I used:
jwt-dotnet/jwt library 7.2.1

Resources