Do you Know?
Do you Have?
Are You?
password
answer to security question
stolen, weak, leaked, phished
token
RSA
PIN
hardware token, manual entry
the person you claim you are
biometrics (Apple touch)
retina scan
biometrics devices, availability
Combination of 2 such factors:
Password + token
Additional step in the process of fully authenticating a User
2FA can be annoying at times, however, only critical paths can be selectively chosen for 2FA. Example: during login or just before placing an order during check out
Spring Security
Spring Security REST
OAuth
pac4j
and more ...
2FA
in
Grails
&
Spring Boot ?
https://duo.com/
https://duo.com/
username
password
Login Success
username
password
Login Success
IFRAME provided by DUO Web
1
2
1. Primary login success, call sign_request() provided by SDK.
2. Secondary login success, upon DUO Push/SMS, call verify_response()
DUO Web SDK
Simple Java and JavaScript Client for web apps
Ad Hoc User Profile creation
Seamless Integration with Grails / Spring Boot
On the fly registration of users for DUO Push
DUO Admin API
Simple REST Api
Customized CRUD operations on User Profiles
Fine grain control of User profiles
Essential for app admins and User registrations
User profile creation in DUO can piggy back on app level registrations for User
// build.gradle
dependencies {
// Duo Web SDK
compile 'com.duosecurity:DuoWeb:1.2-SNAPSHOT'
// DUO Client required for ADMIN API
compile 'com.duosecurity:duo-client:0.2.1'
}
// assets
//= require duo/Duo-Web-v2.min.js
Spring Security Login Success Handler
grails.plugin.springsecurity.successHandler.defaultTargetUrl = '/duo/login'
Make sure above path is @Secured
DUO Login uses Duo Web SDK to get sig_request string
// DuoController.groovy
@Secured("isAuthenticated()")
def login() {
User user = springSecurityService.currentUser
DuoSignResponse duoDigResponse = duoService.signRequest(user.username)
[duoSigResponse: duoDigResponse]
}
// DuoService.groovy
import com.duosecurity.duoweb.DuoWeb
DuoSignResponse signRequest(String username) {
String sigRequest = DuoWeb.signRequest(
duoCredentials.ikey,
duoCredentials.skey,
duoCredentials.akey,
"${usernamePrefix}-$username"
)
return new DuoSignResponse(
duoCredentials.getHostname(),
sigRequest,
getPostCallbackUrl()
)
}
IFRAME uses hostname, sig_request & callback url
// /duo/login.gsp
<iframe
id="duo_iframe"
data-host="${duoSigResponse.hostname}"
data-sig-request="${duoSigResponse.sigRequest}"
data-post-action="${duoSigResponse.postCallbackUrl}"
width="100%"
height="420"
frameborder="0">
</iframe>
IFRAME attributes can be customized based on need and device
Push notification received,
approve login attempt from device
On successful authentication via DUO Push Notification or SMS, callback url is invoked by IFRAME.
In this case the callback is "/duo/verify" to verify the sig_response provided by DUO
// DuoService.groovy
import com.duosecurity.duoweb.DuoWeb
String verifyResponse(String sigResponse) {
String authUser = null
try {
authUser = DuoWeb.verifyResponse(
duoCredentials.ikey,
duoCredentials.skey,
duoCredentials.akey,
sigResponse
)
} catch (DuoWebException | NoSuchAlgorithmException | InvalidKeyException | IOException e) {
log.error("Exception from DUO", e)
}
return authUser
}
Based on the response of verify_response() from DUO, application can decide whether to redirect successfully or let user know that they are unauthorized to access
// DuoController.groovy
@Secured("isAuthenticated()")
def verify() {
// POST callback with request parameter sig_response
// which is a pipe delimited string.
String sigResponse = request.getParameter('sig_response')
String authUser = duoService.verifyResponse(sigResponse);
// Verify response returns back a valid String as authenticated User
// which signifies successful DUO authentication verification
// else, a null is returned for unsuccessful
// DUO authentication verification
if(authUser) {
redirect controller: 'sample'
} else {
render status: HttpStatus.UNAUTHORIZED,
text: "Failed DUO Authentication"
}
}
REST API to handle:
https://duo.com/docs/adminapi
https://duo.com/docs/adminapi
https://duo.com/docs/adminapi
// application.yml
duo:
enabled: true
env: dev
host: api-17XXXaf.duosecurity.com
websdk:
ikey: DIQ4FQ6XXXXXXO27YYYYY
skey: PlP9ZxQXtwXECfV5CRBsRgBndg2yLXXX
admin:
ikey: DIQ4FC7ZZZZZZZ4SQ3BGD
skey: P8kMx5M6XXXXKmGTlj3GXPhHaXmvKYZZ
https://duo.com/docs/adminapi
// DuoAdminService.yml
// Create User
import com.duosecurity.client.Http
import com.squareup.okhttp.Response
// Create Request
Http request = new Http(
HttpMethod.POST.name(),
duoAdminCredentials.hostname,
"/admin/v1/users"
)
//Add request params
request.addParam("username", "jdoe")
request.addParam("realname", "John Doe")
request.addParam("email", "jdoe@example.com")
request.addParam("status", "active")
// Make sure signRequest is always called just before execute because
// params and headers should all be set before calling signRequest
request.signRequest(duoAdminCredentials.ikey, duoAdminCredentials.skey)
// Execute HTTP Request
Response result = request.executeHttpRequest()
return result.isSuccessful()
https://duo.com/docs/adminapi
Adds the user to DUO Admin and sets status to active
- Leonardo da Vinci
SecureKey
Telesign
Encap
SyferLock
http://bit.ly/2uK2XfF
KAYAK
Safelite Auto Glass
University Of Michigan
Yelp
Federal and State Agencies
and many more ....
https://duo.com/docs/duoweb
https://duo.com/docs/adminapi
https://github.com/dmahapatro/grails-two-factor