tafax / angular-digest-auth Goto Github PK
View Code? Open in Web Editor NEWAngularJS module to manage HTTP Digest Authentication
License: MIT License
AngularJS module to manage HTTP Digest Authentication
License: MIT License
Hello, how can use $scope in success or any callback, when i try to inject fails.
thanks.
How can we support logout of a user and then optionally logging as a different user?
What I see now is that credentials of the first successfully logged user are used always including after logout in case of access to protected URL.
Hi, good morning.
I found this module fantastic and I'm really excited to see it working, but until now I could not configure and use it properly.
Can you provide a simple web-app as a sample?
My problem is when I try to load the function $scope.submit
nothing happens.
Anyway, here is what I'm doing:
<?php
header('Access-Control-Allow-Origin: *');
$realm = 'Restricted area';
//user => password
$users = array('admin' => 'mypass', 'guest' => 'guest');
if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
die('Text to send if user hits Cancel button');
}
// analyze the PHP_AUTH_DIGEST variable
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($users[$data['username']]))
die('Wrong Credentials!');
// generate the valid response
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
if ($data['response'] != $valid_response)
die('Wrong Credentials!');
// ok, valid username & password
//echo 'You are logged in as: ' . $data['username'];
echo json_encode(array('user' => 'admin', 'role' => array('VIEW_ALL', 'CREATE_ALL')));
// function to parse the http auth header
function http_digest_parse($txt)
{
// protect against missing data
$needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
$data = array();
$keys = implode('|', array_keys($needed_parts));
preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);
foreach ($matches as $m) {
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($needed_parts[$m[1]]);
}
return $needed_parts ? false : $data;
}
?>
var app = angular.module('app', ['dgAuth']);
app.config(['$routeProvider', 'dgAuthServiceProvider',
function($routeProvider, dgAuthServiceProvider) {
dgAuthServiceProvider.setHeader('PHP_AUTH_DIGEST');
dgAuthServiceProvider.setConfig({
login: {
method: 'POST',
url: 'http://localhost/digest/digest-auth.php'
},
logout: {
method: 'POST',
url: '/signout'
//Other HTTP configurations.
}
});
dgAuthServiceProvider.callbacks.login.push(['', function()
{
return {
successful: function(response)
{
console.log("Worked man: " + response);
},
error: function(response)
{
console.log("Ops, did not work: " + response);
},
}
}]);
$routeProvider.
when('/', {
templateUrl: 'partials/login/login.html',
controller: 'loginController'
}).
when('/main', {
templateUrl: 'partials/main/main.html',
resolve: {
auth: ['dgAuthService', '$q', '$location', function (dgAuthService, $q, $location)
{
var deferred = $q.defer();
dgAuthService.isAuthorized().then(function (authorized)
{
if (authorized)
deferred.resolve();
else
deferred.reject();
$location.path('/');
},
function (authorized)
{
deferred.reject();
});
return deferred.promise;
}]
}
}).
when('/logout', {
templateUrl: 'partials/logout/logout.html'
}).
otherwise({
redirectTo: '/'
});
}]);
app.run(['dgAuthService', function(dgAuthService)
{
dgAuthService.start();
}]);
app.controller('loginController', function ($scope, dgAuthService){
$scope.submit = function() {
console.log($scope.username,$scope.password);
dgAuthService.setCredentials($scope.username,$scope.password);
dgAuthService.signin();
}
});
<div class="loginContainer center-block">
<div class="panel panel-primary">
<div class="panel-heading">User Information</div>
<div class="panel-body">
<form data-ng-submit="submit()">
<div class="form-group">
<label for="usename">User Name</label>
<input class="form-control col-sm-1 col-md-1" id="usename" type="text" placeholder="Username" data-ng-model="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control col-sm-1 col-md-1" id="password" placeholder="Password" data-ng-model="password">
</div>
<input type="submit" class="btn btn-lg btn-primary btn-block col-sm-1 col-md-1" value="Sign in"/>
</form>
</div>
</div>
</div>
Hello.
When the melhod "dgAuthService.start()" is called in my app.js, the following error is showed on console.
http://errors.angularjs.org/1.2.23/$injector/unpr?p0=serviceInjectProvider%20%3C-%20serviceInject
at VALIDITY_STATE_PROPERTY (http://localhost:63342/myproject-web/app/bower_components/angular/angular.js:78:12)
at http://localhost:63342/myproject-web/app/bower_components/angular/angular.js:3802:19
at Object.getService as get
at http://localhost:63342/myproject-web/app/bower_components/angular/angular.js:3807:45
at getService (http://localhost:63342/myproject-web/app/bower_components/angular/angular.js:3930:39)
at Object.invoke (http://localhost:63342/myproject-web/app/bower_components/angular/angular.js:3957:13)
at b.getCallbacks (http://localhost:63342/myproject-web/app/bower_components/angular-digest-auth/dist/angular-digest-auth.min.js:8:7035)
at c.a.config.loggedIn.action (http://localhost:63342/myproject-web/app/bower_components/angular-digest-auth/dist/angular-digest-auth.min.js:8:2618)
at Object.invoke (http://localhost:63342/myproject-web/app/bower_components/angular/angular.js:3966:17)
at c.send (http://localhost:63342/myproject-web/app/bower_components/angular-state-machine/dist/angular-state-machine.min.js:8:2700)
(anonymous function) angular.js:10061
$get angular.js:7357
deferred.promise.then.wrappedCallback angular.js:11564
(anonymous function) angular.js:11647
$get.Scope.$eval angular.js:12673
$get.Scope.$digest angular.js:12485
$get.Scope.$apply angular.js:12777
done angular.js:8371
completeRequest angular.js:8585
xhr.onreadystatechange angular.js:8524
After calling dgAuthService.start();
I always get the error on console header.split is not a function
.
Tracking down this error on the source code, I came to this line. I added console.info(header);
right before it to check it's content, and the contents of this variable is not a String
as probably expect, but an Object
with the following structure:
Object {content-type: "text/html; charset=utf-8"}
I'm on Angular 1.4.6. How can I fix this bug? Thanks.
This is a hard issue to describe in a way you can replicate, without sending you my entire application. If I get time later I may attempt to construct a simple example. Nevertheless, for now, this is what I know.
The symptom: authIdentity.get() sometimes does not return user identity information obtained from the signin
URL configured via dgAuthServiceProvider.setConfig()
, as I believe it is meant to. Instead it contains the response to another URL, which varies depending on the circumstance.
This manifests in my app as being shown as logged in by no-one, or some nonsensical user depending on the URL data we actually get. In my app this is repeatable by logging out (which calls the dgAuthService.signout()
method), reloading the browser page and then logging in again.
The cause. Inspecting the code and debugging, what seems to be happening is that the signin
url is never actually requested, because within the authRequests.signin()
function, there is a cached request to another URL obtained at line 1172. The response from this is what gets set into the authIdentity
object.
The cached request is made by the application following an event broadcast by the successful
callback bound to dgAuthServiceProvider.callbacks.login
. It is intended to update my application's data structures. Apparently it may get invoked before authRequsts.signin()
does.
In other words, what I think is happening is that angular-auth-digest is caching the failed API calls meant to update my application's data structures, and then when the user has entered their login, it replays them instead of calling the configured signin
URL, and the authIdentity object is set by the first result to come back.
The sequence of events I see in my console log output is as follows:
authRequests.signin()
called: (angular-digest-auth.js line 1168)authRequests.signin()
called: (angular-digest-auth.js line 1168)loggedIn
action sets authIdentity to FOO (angular-digest-auth.js line 230)loggedIn
action also sets credentials with { username="Controllis", password="Controllis" (angular-digest-auth.js line 235)loggedIn
action calls login.successful callback with FOO (angular-digest-auth.js line 240)The docs mention authIdentity
but not authService.getCredentials()
, thus I assume authService.getCredentials()
is internal. However it does seem to be more reliable, in the current scheme, so I am using this as an alternative to authIdentity.get()
for now. More explanation of authIdentity
and its purpose would be welcome!
Thanks.
In my project I am using angular-digest-auth to talk to a Restlet 2.3 based webserver (Java 7).
The authentication would not work correctly. It would always be rejected, even with the correct credentials.
I tracked this down and discovered a discarded exception in the Restlet server. This showed that the reason was that the response to the challenge sent by angular-digest-auth was incorrect - the server expected a nonce which matched the one it sent, and was a valid base64 encoding (which means it must be the correct length).
Checking the angular-digest-auth code, I noticed that the parseHeader()
function's regex did not permit =
characters in the values, which is what base64 uses to pad an encoded string to the required length. In fact the regex discarded the trailing =
characters silently.
An example of the challenge sent by Restlet:
Digest realm="redacted realm", domain="/", nonce="MTQ0NzE3NDIzNjA2ODpmMzIxZTgzyzczYjq5NjU2ZDcyZGY3OTYxMjhiNGViYQ==", algorithm=MD5, qop="auth"
A trivial fix is possible just by permitting this character, but inspecting the regex I see it would not cope with other legal challenges. In particular, any empty parameter value would not get parsed correctly, nor any unquoted values, nor quoted values containing a comma or a quote character. Not to mention multiple challenges embedded in one header.
See https://tools.ietf.org/html/rfc2617#section-1.2 and https://tools.ietf.org/html/rfc2616#section-2.1
Hi, I think I don't really understand the way angular-digest-auth works I have an API that auths via URL "/api/authenticate" that expects a POST with JSON body with username and password, I configured the angular-digest-auth to that URL but on my angular app.run() when I do a dgAuthSercive.start() it calls my API URL via POST but without pass any body or any username and password via another way. Is there a example app configured so I can figure out how it really works?
Using npm to install could be supported fairly trivially, and npm is sometimes preferable to using bower.
For example, the 0.4.3 tagged version of this module can be installed using npm using:
npm install 'git+https://[email protected]/tafax/angular-digest-auth.git#v0.4.3' --save
The main problem I can see here is that although the bower.json and README.md file lists dependencies on angular, angular-state-machine and angular-md5, the package.json file doesn't.
Consequentially, installing using npm like this doesn't get these required dependencies.
Adding support would be simple, the bower.json dependencies attribute can almost be used literally, except that angular-state-machine is not available on npm, so unless you publish that it must also be referenced via git:
"dependencies": {
"angular-cookies": "*",
"angular-md5": "*",
"angular-state-machine": "git+https://[email protected]/tafax/angular-state-machine.git",
"angular": "*"
},
Although of course, constraints to ensure obtaining a working version number as per the (docs)[https://docs.npmjs.com/files/package.json#dependencies] is good practice. Publishing this package to the npm manager allows other to do the same with this module.
Oh, I also notice that your bower.json states a dependency on angular-cookies, but your README.md does not.
Cheers!
Can't seem to get it working. It sends the login request, but that seems to be it. No callbacks, no signin, nothing. So I wanted to ask whether any help is available or whether I should look somewhere else?
I'm attempting to submit a patch for the WWW-Authenticate parser function. As part of that I'd like to add a test. However, the tests don't seem to run for me.
There are no instructions and I'm a little new to the grunt/bower/npm toolchain so there's some guesswork involved. But, on the current master branch (i.e. no changes added by me):
grunt karma
Fails because:
Specifically:
ERROR [launcher]: Cannot start Opera
Can not find the binary opera
Please set env variable OPERA_BIN
Removed 'Opera' from browsers
list in karma.conf.js for now.
bower install
, ran that manually for now.e.g.
Firefox 42.0.0 (Ubuntu) ERROR
ReferenceError: angular is not defined
at src/angular-digest-auth.js:7
Specifically:
Firefox 42.0.0 (Ubuntu) angular-digest-auth tests with no server info or credentials stored should ask for the info and credentials and then sign in FAILED
ReferenceError: SyncStrategy is not defined
StateMachineProvider/this.$get<@/home/nick/tmp/angular-digest-auth/lib/angular-state-machine/src/services/state-machine.js:121:9
e@/home/nick/tmp/angular-digest-auth/lib/angular/angular.min.js:39:191
[...]
Likewise for npm test
, which runs grunt travis --verbose
. This does not seem to be a problem
Additionally, the pull requests I submitted fail to build; this may be because of a different reason, the error seems to be:
Fatal error: Arguments to path.join must be strings
npm ERR! Test failed. See above for more details.
npm ERR! not ok code 0
(See here; Googling finds this which suggests grunt-bower needs to be updated)
Anyway, advice welcome.
Thanks
I'd like to know if there is any example app using the angular-digest-auth module. It would help a lot :)
The README says:
You should define a custom header in the server side in order to avoid the browser form and use your custom login form.
This seems to imply that is the WWW-Authenticate header is present, the usual browser prompt will appear? However, if the server does not use WWW-Authenticate then arguably it isn't using standard HTTP-Digest authentication, but a non-standard variation of it.
Also, I'm not clear if this is required just for the sign-in URL, or all URLs (I would assume the latter, but that makes the whole API exposed by the server non-standard, not just one URL, which would allow the rest of the API to stick to the standards and thereby interoperate with other clients besides the AngularJS UI using this module).
Can you clarify: can this module be used with standard http-digest with WWW-Authenticate, or does some or all occurrences of the WWW-Authenticate header need to be prevented for it to work?
Thanks!
var _valuePattern = /([a-zA-Z]+)="?([a-zA-Z0-9/\s]+)"?/; is wrong as nonce can have '=' sign.
Do you have a demo site where one can see your library in action? I can't seem to get it to work with my own code and I am wondering if I am misinterpreting your setup instructions somehow.
Why is there a signin/signout URL? HttpDigest requires that the auth details be sent with every request to the server in the protected area. My REST API expects this to pass the credentials with every request for data and to evaluate it's authenticity with every request as the HttpDigest protocol. Is there a way to use this with a proper HttpDigest setup?
Hi
I notice that when calling dgAuthService.start(); a post request is made to the login
login: {
method: 'Post',
url: '/api/signin'
}
I had expected this call would only be made on signin.submit();
Further I was wondering if you had a sample angularJS webapp that I could use to get started.
Thanks for your help
Andrew
I was wondering why one needs cookies for anything HTTP-Digest related, is this dependency actually required?
So I looked and could not see any use of $cookies. The last use of it is in commit d66924, so I think this is no longer a dependency.
There are 2 local sites that are running on different ports one is for server and one is UI. The server has enabled the CORS using the following settings.
1. <add name="Access-Control-Allow-Origin" value="http://localhost" />
2. <add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE,PATCH,OPTIONS" />
3. <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept, Authorization" />
4. <add name="Access-Control-Allow-Credentials" value="true" />
and also enabled the digest authentication in the IIS.
Now coming to the UI, i created the angular module as per the guidelines provided in the Readme file. Following is my code.
var app = angular.module("poc", ["dgAuth"]);
app.config(['dgAuthServiceProvider', function (dgAuthServiceProvider) {
dgAuthServiceProvider.setHeader('WWW-Authenticate');
dgAuthServiceProvider.setLimit('inf');
dgAuthServiceProvider.setConfig({
login: {
method: 'GET',
url: 'http://localhost:1111/api/values',
withCredentials: true,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
},
logout: {
method: 'GET',
url: 'http://localhost:1111/api/values'
}
});
dgAuthServiceProvider.callbacks.login.push(function (AuthenticationSvc) {
return {
successful: function (response) {
console.log(response);
},
error: function (response) {
console.log(response);
},
required: function (response) {
console.log(response);
},
limit: function (response) {
console.log(response);
}
};
});
dgAuthServiceProvider.callbacks.logout.push(function (AuthenticationSvc) {
return {
successful: function (response) {
console.log(response);
},
error: function (response) {
console.log(response);
}
};
});
// dgAuthServiceProvider.setStorage(localStorage);
}]);
app.controller("first", function ($scope, dgAuthService, $http) {
$scope.userName = "";
$scope.password = "";
$scope.login = function () {
dgAuthService.setCredentials($scope.userName, $scope.password);
dgAuthService.signin();
dgAuthService.isAuthorized().then(function (response) {
$http({
url: 'http://localhost:1111/api/values/get',
method: "GET",
withCredentials: true,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
}).then(
function (response) {
console.log(response)
},
function (response) {
console.log(response)
}
)
});
}
});
app.run(['dgAuthService', function (dgAuthService) {
/**
* It tries to sign in. If the service doesn't find
* the credentials stored or the user is not signed in yet,
* the service executes the required function.
*/
dgAuthService.start();
}]);
I am getting status as -1 in all the request errors. Getting unauthorized when checked in the browser console.
Highly appreciable if any one provide the solution ASAP.
Note : I downloaded the reference files directly from the bower.
After weeks of having to do something else for work I have returned to this. I am still trying to get a hang of Angular and while I thought I had it set up correctly when I add the callbacks I get errors in the console. A full example instead of segments would be helpful. I've added my code maybe you can tell me what I am doing wrong.
(function($){
/**
* example of http digest injection.
*/
app = angular.module('authMain', ['dgAuth']);
app.config(['dgAuthServiceProvider', function(dgAuthServiceProvider)
{
dgAuthServiceProvider.setConfig({
login: {
method: 'POST',
url: '/api/user/authenticate'
},
logout: {
method: 'POST',
url: '/api/user/deauthenticate'
}
});
// dgAuthServiceProvider.setHeader('');
dgAuthServiceProvider.setLimit(5);
dgAuthServiceProvider.setStorage(localStorage);
/**
* You can add the callbacks to manage what happens after
* successful of the login.
*/
dgAuthServiceProvider.callbacks.login.push(['serviceInject', function(serviceInject)
{
return {
successful: function(response)
{
console.write('login success');
},
error: function(response)
{
console.write('login error');
},
required: function(response)
{
console.write('login requried');
},
limit: function(response)
{
console.write('login limit');
}
};
}]);
//This is the same for the logout.
/**
* You can add the callbacks to manage what happens after
* successful of the logout.
*/
dgAuthServiceProvider.callbacks.logout.push(['serviceInject', function(serviceInject)
{
return {
successful: function(response)
{
console.write('logout success');
},
error: function(response)
{
console.write('logout fail');
}
};
}]);
}]);
app.run(['dgAuthService', function(dgAuthService)
{
/**
* It tries to sign in. If the service doesn't find
* the credentials stored or the user is not signed in yet,
* the service executes the required function.
*/
dgAuthService.start();
}]);
app.controller("LoginController", ['dgAuthService', function(dgAuthService){
this.user = {};
this.doLogin = function()
{
console.log(this.user);
dgAuthService.setCredentials(this.user.emailAddress, this.user.password);
dgAuthService.signin();
}
}]);
})(jQuery);
I was reading through the code and noticed that you are storing in plain text the username and password in local storage. That's not secure. Instead you should be storing the ha1 hash in local storage, and storing the ha1 hash in your password database that way you aren't storing plain text passwords in your database. Yes it means a username change would result in having to ask for the password and rebuild the password hash but how often to people change usernames.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.