Could anyone help me?
When i read cookie in live server, cookie return encrypted data in live server. But in local cookie return decrypted data.
Cookie config is as the following
$this->Cookie->name = 'rememberMe';
$this->Cookie->secure = false;
$this->Cookie->key = 'key';
$this->Cookie->httpOnly = true;
$this->Cookie->type('rijndael');
i want cookie return decrypted data when read cookie.
anyone can help me?
Related
I'm trying to create a local Java-based client that interacts with the SurveyMonkey API.
SurveyMonkey requires a long-lived access token using OAuth 2.0, which I'm not very familiar with.
I've been googling this for hours, and I think the answer is no, but I just want to be sure:
Is it possible for me to write a simple Java client that interacts with the SurveyMonkey, without setting up my own redirect server in some cloud?
I feel like having my own online service is mandatory to be able to receive the bearer tokens generated by OAuth 2.0. Is it possible that I can't have SurveyMonkey send bearer tokens directly to my client?
And if I were to set up my own custom Servlet somewhere, and use it as a redirect_uri, then the correct flow would be as follows:
Java-client request bearer token from SurveyMonkey, with
redirect_uri being my own custom servlet URL.
SurveyMonkey sends token to my custom servlet URL.
Java-client polls custom servlet URL until a token is available?
Is this correct?
Yes, it is possible to use OAuth2 without a callback URL.
The RFC6749 introduces several flows. The Implicit and Authorization Code grant types require a redirect URI. However the Resource Owner Password Credentials grant type does not.
Since RFC6749, other specifications have been issued that do not require any redirect URI:
RFC7522: Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants
RFC7523: JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants
RFC8628: OAuth 2.0 Device Authorization Grant
In any case, if the grant types above do not fit on your needs, nothing prevent you from creating a custom grant type.
Not exactly, the whole point of the OAuth flow is that the user (the client you're accessing the data on behalf of) needs to give you permission to access their data.
See the authentication instructions. You need to send the user to the OAuth authorize page:
https://api.surveymonkey.net/oauth/authorize?api_key<your_key>&client_id=<your_client_id>&response_type=code&redirect_uri=<your_redirect_uri>
This will show a page to the user telling them which parts of their account you are requesting access to (ex. see their surveys, see their responses, etc). Once the user approves that by clicking "Authorize" on that page, SurveyMonkey will automatically go to whatever you set as your redirect URI (make sure the one from the url above matches with what you set in the settings for your app) with the code.
So if your redirect URL was https://example.com/surveymonkey/oauth, SurveyMonkey will redirect the user to that URL with a code:
https://example.com/surveymonkey/oauth?code=<auth_code>
You need to take that code and then exchange it for an access token by doing a POST request to https://api.surveymonkey.net/oauth/token?api_key=<your_api_key> with the following post params:
client_secret=<your_secret>
code=<auth_code_you_just_got>
redirect_uri=<same_redirect_uri_as_before>
grant_type=authorization_code
This will return an access token, you can then use that access token to access data on the user's account. You don't give the access token to the user it's for you to use to access the user's account. No need for polling or anything.
If you're just accessing your own account, you can use the access token provided in the settings page of your app. Otherwise there's no way to get an access token for a user without setting up your own redirect server (unless all the users are in the same group as you, i.e. multiple users under the same account; but I won't get into that). SurveyMonkey needs a place to send you the code once the user authorizes, you can't just request one.
You do need to implement something that will act as the redirect_uri, which does not necessarily need to be hosted somewhere else than your client (as you say, in some cloud).
I am not very familiar with Java and Servelets, but if I assume correctly, it would be something that could handle http://localhost:some_port. In that case, the flow that you describe is correct.
I implemented the same flow successfully in C#. Here is the class that implements that flow. I hope it helps.
class OAuth2Negotiator
{
private HttpListener _listener = null;
private string _accessToken = null;
private string _errorResult = null;
private string _apiKey = null;
private string _clientSecret = null;
private string _redirectUri = null;
public OAuth2Negotiator(string apiKey, string address, string clientSecret)
{
_apiKey = apiKey;
_redirectUri = address.TrimEnd('/');
_clientSecret = clientSecret;
_listener = new HttpListener();
_listener.Prefixes.Add(address + "/");
_listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
}
public string GetToken()
{
var url = string.Format(#"https://api.surveymonkey.net/oauth/authorize?redirect_uri={0}&client_id=sm_sunsoftdemo&response_type=code&api_key=svtx8maxmjmqavpavdd5sg5p",
HttpUtility.UrlEncode(#"http://localhost:60403"));
System.Diagnostics.Process.Start(url);
_listener.Start();
AsyncContext.Run(() => ListenLoop(_listener));
_listener.Stop();
if (!string.IsNullOrEmpty(_errorResult))
throw new Exception(_errorResult);
return _accessToken;
}
private async void ListenLoop(HttpListener listener)
{
while (true)
{
var context = await listener.GetContextAsync();
var query = context.Request.QueryString;
if (context.Request.Url.ToString().EndsWith("favicon.ico"))
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.Close();
}
else if (query != null && query.Count > 0)
{
if (!string.IsNullOrEmpty(query["code"]))
{
_accessToken = await SendCodeAsync(query["code"]);
break;
}
else if (!string.IsNullOrEmpty(query["error"]))
{
_errorResult = string.Format("{0}: {1}", query["error"], query["error_description"]);
break;
}
}
}
}
private async Task<string> SendCodeAsync(string code)
{
var GrantType = "authorization_code";
//client_secret, code, redirect_uri and grant_type. The grant type must be set to “authorization_code”
var client = new HttpClient();
client.BaseAddress = new Uri("https://api.surveymonkey.net");
var request = new HttpRequestMessage(HttpMethod.Post, string.Format("/oauth/token?api_key={0}", _apiKey));
var formData = new List<KeyValuePair<string, string>>();
formData.Add(new KeyValuePair<string, string>("client_secret", _clientSecret));
formData.Add(new KeyValuePair<string, string>("code", code));
formData.Add(new KeyValuePair<string, string>("redirect_uri", _redirectUri));
formData.Add(new KeyValuePair<string, string>("grant_type", GrantType));
formData.Add(new KeyValuePair<string, string>("client_id", "sm_sunsoftdemo"));
request.Content = new FormUrlEncodedContent(formData);
var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
_errorResult = string.Format("Status {0}: {1}", response.StatusCode.ToString(), response.ReasonPhrase.ToString());
return null;
}
var data = await response.Content.ReadAsStringAsync();
if (data == null)
return null;
Dictionary<string, string> tokenInfo = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
return(tokenInfo["access_token"]);
}
}
Currently I am working in dev env on my local machine where I am storing passwords in plain text using MongoDB. I am using express-jwt along with jsonwebtoken for passing user data and authentication. I've researched bcryptjs and bcrypt and I would like to implement whichever is best for React, and Express, for passing hashed passwords to the database from the client. I have found resources for server side, but nothing for client side.
My question is then, what is the methodology for properly saving encrypted passwords on my server when they are passed from a client? How do I encrypt passwords client side then authenticate server side?
I have read some articles saying there is no need to encrypt client side due to ssl, but others say there is an absolute need to encrypt client side. What is the correct way, and how can I implement it on my React application?
Using Bcryptjs, Express and MongoDB:
There is no need to encrpyt client side, you can pass the password as plain text to the server using a post request (through a form usually).
Assuming you have a 'user' schema which looks similar to this:
const userSchema = new mongoose.Schema({
email:{type:String,required:true,unique:true},
password:{type:String,required:true}
},{collection:'users'}
const User= mongoose.model("User",userSchema);
)
On register/sign up in the server, where you handle the request you would hash the user's password like so:
app.post('/signup',async (req,res)=>{
// geting our data from frontend
const {email,password:plainTextPassword}=req.body;
// encrypting our password to store in database
const password = await bcrypt.hash(plainTextPassword,salt);
try {
// storing our user data into database
const response = await User.create({
email,
password
})
return res.redirect('/');
} catch (error) {
console.log(JSON.stringify(error));
if(error.code === 11000){
return res.send({status:'error',error:'email already exists'})
}
throw error
}
})
4.Upon login request (which will also be a post through a form o the client), you will compare the passwords using bcrpyt.compare() function, and if successful, assign a JWT to the user like so, this method assumes the token will be stored in the Cookies.
const verifyUserLogin = async (email,password)=>{
try {
const user = await User.findOne({email}).lean()
if(!user){
return {status:'error',error:'user not found'}
}
if(await bcrypt.compare(password,user.password)){
// creating a JWT token
token = jwt.sign({id:user._id,username:user.email,type:'user'},JWT_SECRET,{ expiresIn: '2h'})
return {status:'ok',data:token}
}
return {status:'error',error:'invalid password'}
} catch (error) {
console.log(error);
return {status:'error',error:'timed out'}
}
}
// login
app.post('/login',async(req,res)=>{
const {email,password}=req.body;
// we made a function to verify our user login
const response = await verifyUserLogin(email,password);
if(response.status==='ok'){
// storing our JWT web token as a cookie in our browser
res.cookie('token',token,{ maxAge: 2 * 60 * 60 * 1000, httpOnly: true }); // maxAge: 2 hours
res.redirect('/');
}else{
res.json(response);
}
})
I didn't address the front end, because it only includes basic POST request forms in react, there is no need for any special methods or processing on the client-side.
hope it helps.
edit, hashing client-side:
There is a debate about this, and in some protocols, it's even required to hash passwords on the client-side, in short, because SSL already encrypts everything that moves from client to server hashing on the client-side is pretty pointless and is not widely accepted today, even # big companies. Bottom line, the added security is neglectable, and is not worth the trouble and exposing hashing logic to the client side
You don't decrypt passwords. You ask the user for the password, then you hash it and compare it to the stored hash one you saved. If they're the same, then (assuming you have a secure hashing algorithm) the unencrypted versions must be the same also.
Using Identity Server 4 server.
In client’s Startup.Auth.cs :
private static void ConfigureAuth(IAppBuilder app)
{
ISettingsReader settingsReader = Services.Resolve<ISettingsReader>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
ExpireTimeSpan = new TimeSpan(1, 0, 0),
CookieSecure = CookieSecureOption.Always,
CookieHttpOnly = true,
SlidingExpiration = true
});
var platformUri = settingsReader.GetSetting("PlatformUri")?.TrimEnd('/');
var platformApiKey = settingsReader.GetSetting("PlatformApiKey");
var deploymentURL = settingsReader.GetSetting("deploymentURL")?.TrimEnd('/');
var authority = $"{platformUri}/identity";
string clientSecret;
string clientId = SplitApiKey(platformApiKey, out clientSecret);
var options = new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
ClientSecret = clientSecret,
Authority = authority,
RedirectUri = $"{deploymentURL}/signin/callback",
ResponseType = "id_token token",
Scope = "platform openid",
UseTokenLifetime = false,
SignInAsAuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = SecurityTokenValidatedHandler,
RedirectToIdentityProvider = RedirectToIdentityProviderHandler,
}
};
app.UseOpenIdConnectAuthentication(options);
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
}
Note – the cookie’s expire timespan has been set to 5 mins for debugging, normally would be set to an hour.
And then stash the access_token in the validated handler (as per several articles) so that we can use it later for api calls:
private static async Task SecurityTokenValidatedHandler(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var jwtDetails = JsonWebToken.Parse(notification.ProtocolMessage.AccessToken);
notification.AuthenticationTicket.Identity.AddClaims(
jwtDetails.Claims.Where(c => DesiredAccessTokenClaims.Contains(c.Type)));
notification.AuthenticationTicket.Identity.AddClaim(new Claim("access_token", notification.ProtocolMessage.AccessToken));
notification.AuthenticationTicket.Identity.AddClaim(new Claim("id_token", notification.ProtocolMessage.IdToken));
}
This works fine. However, although the cookies do auto refresh themselves, with sliding expiration (typically after 2-3 minutes, rather than the full 5), there doesn’t seem to be a way to refresh the access token which is held in the claim, so although the user will remain logged in, the access token will be useless after it expires.
Is this the right way to be going about this? And if so, is there a way to update the access token in the claim, in the background without disturbing the user? It seems that the ideal solution would be to have the cookie refresh also trigger the SecurityTokenValidatedHandler so that a renewed claim can be added into the new cookie, although despite looking in CookieManager, etc. there doesn't seem to be an event which is triggered when the cookie sliding refreshes itself. Does anyone know a way to do this?
Many thanks for your time!
If anyone comes across this issue, the answer was to change from implicit to hybrid. Hybrid allows refresh tokens, whereas implicit, not so much. In our case we needed Hybrid and Client Creds
I'm currently working on a Angular (Front End) - Laravel (API) application. I need to know whether I can make a hybrid log in.
Currently I'm using a stored procedure for the log in. But then if the login was succesful I want to send a JWT token to the client side. Is this possible without calling the JWT attempt method? Does JWT has something like a JWT::getToken($mail); method? If I can't do this, are any other work arounds?
$mail = $request['email'];
$password = $request['password'];
$query = new Query("CALL SP_USER_LOG_IN(?,?, #outputMessage)");
$query->addParameter(1, $mail);
$query->addParameter(2, $password);
$outputMessage = DB::executeQuery($query);
if($outputMessage === null)
{
//It means the login was successful
$token = JWT::getToken($mail);
return ["error" => NULL, "token" => $token];
}
else
{
return ["error" => $outputMessage];
}
NOTE
I just found out about JWT and it looks like a really good solution since I'm working with a light angular application on the front end. But since I must use a stored procedure, I don't how to proceed.
Once you've verified the user, grab them from the database then create the token fromUser($user).
if($outputMessage === null)
{
//It means the login was successful
$user = User::where('email', $mail)->first();
$token = JWT::fromUser($user);
return ["error" => NULL, "token" => $token];
}
i am building a SPA using Angular JS and web API2, use Oauth2 for authentication. My issue, token'expiration is fixed, such as 20 minutes. So how can we redirect to logion page if user does not have any request in 20 minutes?
Refresh token does not work because system will auto refresh token although user does not have any action in valid time.
Cheers,
You don't need to control timeout in client app.
When the client do a request to the resource server, the resource server validates the access token and if it's expired returns a 401 - Unauthorized response.
When the client gets the 401 from the resource server, needs to obtain a new access token from the authorization server, either using the resource owner credentials or the refresh token.
This is the behaviour specified by the OAuth 2.0 protocol.
Please let me know if you need a deeper explanation.
I use an AuthorizeAttribute and override OnAuthorization
public override void OnAuthorization(HttpActionContext actionContext)
{
string token = string.Empty;
AuthenticationTicket ticket;
//retrieve the token the client sent in the request...
token = (actionContext.Request.Headers.Any(x => x.Key == "Authorization")) ? actionContext.Request.Headers.Where(x => x.Key == "Authorization").FirstOrDefault().Value.SingleOrDefault().Replace("Bearer ", "") : "";
//Your OAuth Startup class may be called differently...
ticket = Startup.OAuthBearerOptions.AccessTokenFormat.Unprotect(token);
//verification using the ticket's properties. When it was set to expire (ExpiresUtc) or whatever other properties you may have appended to it's dictionnary.
//if verification fails..
//actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "Verification failed.");
//return;
//Otherwise, send a new token with an extended expiration date...
AuthenticationProperties refreshTokenProperties = new AuthenticationProperties(ticket.Properties.Dictionary)
{
IssuedUtc = ticket.Properties.IssuedUtc,
ExpiresUtc = DateTime.UtcNow.AddMinutes(20)
};
AuthenticationTicket newToken = new AuthenticationTicket(ticket.Identity, refreshTokenProperties);
string newTokenHash = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(newToken);
//add the new token to request properties. Can't add it to the header here, because creating response/response headers here will prevent process from proceeding to called controller method.
actionContext.Request.Properties.Add(new KeyValuePair<string, object>("Token", newTokenHash));
}
Then chain it with an ActionFilterAttribute filter:
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Response == null)
return;
var objectContent = actionExecutedContext.Response.Content as ObjectContent;
//the token we put in the filter above...
string tokenHash = (actionExecutedContext.Request.Properties.Any(x => x.Key == "Token")) ? (string)actionExecutedContext.Request.Properties.Where(x => x.Key == "Token").FirstOrDefault().Value : "";
}
You can either append a new header to the response, put in the JSON payload response or add it as a response cookie. Then you make your client use this new hash when requesting any other resource, that way the expiration will slide an extra 20 mins everytime.
You can register these filter attributes globally in App_Start/WebApiConfig.cs
config.Filters.Add(new ClassExtendingAuthorizeAttribute());
config.Filters.Add(new ClassExtendingActionFilterAttribute());
But as mentioned by jumuro, you could have your client simply use the refresh token. Depends if you want to your back-end or front-end to do most of the leg work.
Hope it helps.