forked from jwt-scala/jwt-scala
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
31 changed files
with
750 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
.history | ||
target | ||
project/project | ||
project/target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
logs | ||
project/project | ||
project/target | ||
target | ||
tmp | ||
.history | ||
dist | ||
/.idea | ||
/*.iml | ||
/out | ||
/.idea_modules | ||
/.classpath | ||
/.project | ||
/RUNNING_PID | ||
/.settings |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Example: Play Framework + AngularJS |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package controllers | ||
|
||
import play.api._ | ||
import play.api.mvc._ | ||
import play.api.libs.json._ | ||
import play.api.libs.functional.syntax._ | ||
|
||
import pdi.scala.jwt._ | ||
|
||
import models.User | ||
|
||
object Application extends Controller with Secured { | ||
val passwords = Seq("red", "blue", "green", "purple", "totoro") | ||
|
||
def index = Action { | ||
Ok(views.html.index()) | ||
} | ||
|
||
private val loginForm: Reads[(String, String)] = | ||
(JsPath \ "username").read[String] and | ||
(JsPath \ "password").read[String] tupled | ||
|
||
def login = Action(parse.json) { implicit request => | ||
request.body.validate(loginForm).fold( | ||
errors => { | ||
BadRequest(JsError.toFlatJson(errors)) | ||
}, | ||
form => { | ||
if (passwords.contains(form._2)) { | ||
Ok.addingToJwtSession("user", User(form._1)) | ||
} else { | ||
Unauthorized | ||
} | ||
} | ||
) | ||
} | ||
|
||
def publicApi = Action { | ||
Ok("That was easy!") | ||
} | ||
|
||
def privateApi = Authenticated { | ||
Ok("Only the best can see that.") | ||
} | ||
|
||
def adminApi = Admin { | ||
Ok("Top secret data. Hopefully, nobody will ever access it.") | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package controllers | ||
|
||
import scala.concurrent.Future | ||
import play.api.libs.concurrent.Execution.Implicits._ | ||
|
||
import play.api.mvc._ | ||
import play.api.mvc.Results._ | ||
import play.api.libs.json._ | ||
|
||
import pdi.scala.jwt._ | ||
|
||
import models.User | ||
|
||
class AuthenticatedRequest[A](val user: User, request: Request[A]) extends WrappedRequest[A](request) | ||
|
||
trait Secured { | ||
def Authenticated = AuthenticatedAction | ||
def Admin = AdminAction | ||
} | ||
|
||
object AuthenticatedAction extends ActionBuilder[AuthenticatedRequest] { | ||
def invokeBlock[A](request: Request[A], block: AuthenticatedRequest[A] => Future[SimpleResult]) = | ||
request.jwtSession(request).getAs[User]("user") match { | ||
case Some(user) => block(new AuthenticatedRequest(user, request)).map(_.refreshJwtSession(request)) | ||
case _ => Future.successful(Unauthorized) | ||
} | ||
} | ||
|
||
object AdminAction extends ActionBuilder[AuthenticatedRequest] { | ||
def invokeBlock[A](request: Request[A], block: AuthenticatedRequest[A] => Future[SimpleResult]) = | ||
request.jwtSession(request).getAs[User]("user") match { | ||
case Some(user) if user.isAdmin => block(new AuthenticatedRequest(user, request)).map(_.refreshJwtSession(request)) | ||
case Some(_) => Future.successful(Forbidden.refreshJwtSession(request)) | ||
case _ => Future.successful(Unauthorized) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package models | ||
|
||
import play.api.libs.json._ | ||
import play.api.libs.functional.syntax._ | ||
|
||
case class User(name: String) { | ||
def isAdmin: Boolean = (name.toLowerCase == "admin") | ||
} | ||
|
||
object User { | ||
implicit val userFormat = Json.format[User] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
@() | ||
|
||
<!DOCTYPE html> | ||
|
||
<html ng-app="app"> | ||
<head> | ||
<title>JWT - Play Angular</title> | ||
<link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")"> | ||
<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/app.css")"> | ||
</head> | ||
<body ng-controller="HomeCtrl as ctrl"> | ||
<header> | ||
|
||
</header> | ||
|
||
<section ng-hide="authenticated"> | ||
<form ng-submit="ctrl.login()"> | ||
<input type="text" placeholder="Username" ng-model="ctrl.loginForm.username"> | ||
<input type="password" placeholder="Password ('red' or 'blue' or 'green')" ng-model="ctrl.loginForm.password"> | ||
<button class="btn btn-login" type="submit">Login</button> | ||
</form> | ||
</section> | ||
|
||
<section ng-show="authenticated"> | ||
<div>Hello there! How are you today <strong>{{user().name}}</strong>?</div> | ||
<button type="button" class="btn btn-logout" ng-click="ctrl.logout()">Logout</button> | ||
</section> | ||
|
||
<section> | ||
<button class="btn btn-public" type="button" ng-click="ctrl.publicCall()">Public API</button> | ||
<button class="btn btn-private" type="button" ng-click="ctrl.privateCall()">Private API</button> | ||
<button class="btn btn-admin" type="button" ng-click="ctrl.adminCall()">Admin API</button> | ||
</section> | ||
|
||
<section ng-if="notification" class="fade-if" ng-click="closeNotification()"> | ||
<div class="notification" ng-class="'' + notification.severity"> | ||
{{notification.message}} | ||
</div> | ||
</section> | ||
|
||
<script src="//rawgit.com/pauldijou/jwt-client/master/jwt-client.js" type="text/javascript"></script> | ||
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.0/angular.js"></script> | ||
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.0/angular-animate.js"></script> | ||
<script src="@routes.Assets.at("javascripts/app.js")" type="text/javascript"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# This is the main configuration file for the application. | ||
# ~~~~~ | ||
|
||
# Secret key | ||
# ~~~~~ | ||
# The secret key is used to secure cryptographics functions. | ||
# | ||
# This must be changed for production, but we recommend not changing it in this file. | ||
# | ||
# See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. | ||
application.secret="?`wdc6<s]M=Z4VG][lSu1d48pxr;`FvQOl4H^XfvAJX5G<9W`jmSi/?XaFvT9hGb" | ||
|
||
# The application languages | ||
# ~~~~~ | ||
application.langs="en" | ||
|
||
# Global object class | ||
# ~~~~~ | ||
# Define the Global object class for this application. | ||
# Default to Global in the root package. | ||
# application.global=Global | ||
|
||
# Router | ||
# ~~~~~ | ||
# Define the Router object to use for this application. | ||
# This router will be looked up first when the application is starting up, | ||
# so make sure this is the entry point. | ||
# Furthermore, it's assumed your route file is named properly. | ||
# So for an application router like `my.application.Router`, | ||
# you may need to define a router file `conf/my.application.routes`. | ||
# Default to Routes in the root package (and conf/routes) | ||
# application.router=my.application.Routes | ||
|
||
# Database configuration | ||
# ~~~~~ | ||
# You can declare as many datasources as you want. | ||
# By convention, the default datasource is named `default` | ||
# | ||
# db.default.driver=org.h2.Driver | ||
# db.default.url="jdbc:h2:mem:play" | ||
# db.default.user=sa | ||
# db.default.password="" | ||
|
||
# Evolutions | ||
# ~~~~~ | ||
# You can disable evolutions if needed | ||
# evolutionplugin=disabled | ||
|
||
# Logger | ||
# ~~~~~ | ||
# You can also configure logback (http://logback.qos.ch/), | ||
# by providing an application-logger.xml file in the conf directory. | ||
|
||
# Root logger: | ||
logger.root=ERROR | ||
|
||
# Logger used by the framework: | ||
logger.play=INFO | ||
|
||
# Logger provided to your application: | ||
logger.application=DEBUG | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Routes | ||
# This file defines all application routes (Higher priority routes first) | ||
# ~~~~ | ||
|
||
# Home page | ||
GET / controllers.Application.index | ||
|
||
POST /api/login controllers.Application.login | ||
GET /api/public controllers.Application.publicApi | ||
GET /api/private controllers.Application.privateApi | ||
GET /api/admin controllers.Application.adminApi | ||
|
||
# Map static resources from the /public folder to the /assets URL path | ||
GET /assets/*file controllers.Assets.at(path="/public", file) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
angular.module('app', ['ngAnimate']) | ||
|
||
.config(['$provide', '$httpProvider', function ($provide, $httpProvider) { | ||
$provide.factory('AuthenticationInterceptor', ['$q', '$rootScope', function ($q, $rootScope) { | ||
return { | ||
'request': function (config) { | ||
config.headers['Authorization'] = JWT.get(); | ||
return config; | ||
}, | ||
|
||
'responseError': function (rejection) { | ||
console.log(rejection); | ||
|
||
if (rejection.status === 401) { | ||
// User isn't authenticated | ||
$rootScope.$emit('notification', 'warning', 'You need to be authenticated to access such API.') | ||
} else if (rejection.status === 403) { | ||
// User is authenticated but do not have permission to access this API | ||
$rootScope.$emit('notification', 'warning', 'Sorry, you do not have access to this API... Maybe if your username was "admin"... who knows...') | ||
} | ||
|
||
return $q.reject(rejection); | ||
} | ||
} | ||
}]); | ||
|
||
$httpProvider.interceptors.push('AuthenticationInterceptor'); | ||
}]) | ||
|
||
.run(['$rootScope', 'Authenticated', function ($rootScope, Authenticated) { | ||
$rootScope.$on('notification', function (e, severity, message) { | ||
$rootScope.notification = {severity: severity, message: message}; | ||
}); | ||
|
||
$rootScope.closeNotification = function closeNotification() { | ||
$rootScope.notification = null; | ||
}; | ||
|
||
$rootScope.user = Authenticated.current | ||
}]) | ||
|
||
.factory('Authenticated', ['$http', '$rootScope', function ($http, $rootScope) { | ||
var user = null; | ||
sync(); | ||
|
||
function sync() { | ||
var session = JWT.remember(); | ||
user = session && session.claim && session.claim.user; | ||
$rootScope.authenticated = !!user; | ||
} | ||
|
||
function login (data) { | ||
return $http.post('/api/login', data).then(function (response) { | ||
var token = response.headers("Authorization"); | ||
var session = JWT.read(token); | ||
|
||
console.log(token); | ||
console.log(session); | ||
|
||
if (JWT.validate(session)) { | ||
console.log('valid'); | ||
JWT.keep(session); | ||
sync(); | ||
} else { | ||
logout(); | ||
} | ||
}); | ||
} | ||
|
||
function logout() { | ||
JWT.forget(); | ||
sync(); | ||
} | ||
|
||
function isAuthenticated() { | ||
return !!user; | ||
} | ||
|
||
function current() { | ||
return user; | ||
} | ||
|
||
return { | ||
login: login, | ||
logout: logout, | ||
isAuthenticated: isAuthenticated, | ||
current: current | ||
} | ||
}]) | ||
|
||
.controller('HomeCtrl', ['$scope', '$http', 'Authenticated', function ($scope, $http, Authenticated) { | ||
var ctrl = this; | ||
ctrl.notification = null; | ||
|
||
ctrl.test = "test"; | ||
|
||
ctrl.loginForm = {}; | ||
|
||
ctrl.login = function login() { | ||
console.log('login', ctrl.loginForm); | ||
Authenticated.login(ctrl.loginForm).then(function () { | ||
ctrl.loginForm = {}; | ||
}, function (error) { | ||
ctrl.notif('error', 'Invalid username or password!'); | ||
}); | ||
}; | ||
|
||
ctrl.logout = function logout() { | ||
Authenticated.logout(); | ||
}; | ||
|
||
function get(endpoint) { | ||
return $http.get(endpoint).then(function (response) { | ||
ctrl.notif('success', response.data); | ||
}, function (error) { | ||
// 401 and 403 errors are already handled by the interceptor | ||
}); | ||
} | ||
|
||
ctrl.publicCall = function publicCall() { | ||
get('/api/public'); | ||
}; | ||
|
||
ctrl.privateCall = function privateCall() { | ||
get('/api/private'); | ||
}; | ||
|
||
ctrl.adminCall = function privateCall() { | ||
get('/api/admin'); | ||
}; | ||
|
||
ctrl.notif = function notif(severity, message) { | ||
$scope.$emit('notification', severity, message); | ||
}; | ||
}]); |
Oops, something went wrong.