Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 1E83C200BF7 for ; Mon, 9 Jan 2017 16:01:39 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 1CF96160B3E; Mon, 9 Jan 2017 15:01:39 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 4A5FE160B3B for ; Mon, 9 Jan 2017 16:01:37 +0100 (CET) Received: (qmail 65146 invoked by uid 500); 9 Jan 2017 15:01:36 -0000 Mailing-List: contact user-help@shiro.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: user@shiro.apache.org Delivered-To: mailing list user@shiro.apache.org Received: (qmail 65136 invoked by uid 99); 9 Jan 2017 15:01:36 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd3-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 09 Jan 2017 15:01:36 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd3-us-west.apache.org (ASF Mail Server at spamd3-us-west.apache.org) with ESMTP id 7801E180140 for ; Mon, 9 Jan 2017 15:01:35 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd3-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 2.879 X-Spam-Level: ** X-Spam-Status: No, score=2.879 tagged_above=-999 required=6.31 tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, HTML_MESSAGE=2, KAM_LINEPADDING=1.2, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RCVD_IN_SORBS_SPAM=0.5, SPF_PASS=-0.001] autolearn=disabled Authentication-Results: spamd3-us-west.apache.org (amavisd-new); dkim=pass (2048-bit key) header.d=gmail.com Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd3-us-west.apache.org [10.40.0.10]) (amavisd-new, port 10024) with ESMTP id fcpKtgEOjvzY for ; Mon, 9 Jan 2017 15:01:31 +0000 (UTC) Received: from mail-it0-f54.google.com (mail-it0-f54.google.com [209.85.214.54]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with ESMTPS id EBAD65FC16 for ; Mon, 9 Jan 2017 15:01:25 +0000 (UTC) Received: by mail-it0-f54.google.com with SMTP id c20so55449139itb.0 for ; Mon, 09 Jan 2017 07:01:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:in-reply-to:references:from:date:message-id:subject:to; bh=NoeIA+vDVXk5Ix5WXqFVicM0WqS/7IiI4929kp+TTIQ=; b=OFbjqf90kF8vlewIZL4YjKt9WlvUgo/2k7FzqN5enlGBZmUXKa4NwDhkTUCd8JPGLB fc2bU8kuF7E3FORLDPGlcGKnh3DUFzXeBBdVAM3yRTWtyt2kmcyxe+pPL5T4O/JmkAqq jx7HcfJ2eEndC8zlBDqGwt8sJijRmBojEbYCdwqMYZ4fcNjdK/GDpEtikqmetX+Ix8wf q2hslMB5SqRNH9oyemdJ63a9hGRL13Kqs0m2uuc4VB/8S0hS7xCNh5HQXmWqi6tmvqJG JoOBGcwcUl69zSQc2Lw2ycL8Xu2/rcSzkaiMRyrODFuMf8xYWSG13KrCyUV4d2HIoqZU u19A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to; bh=NoeIA+vDVXk5Ix5WXqFVicM0WqS/7IiI4929kp+TTIQ=; b=VO+9lWZ3RaaetRuG4SJayaKumMc8B4aCKVyUWxO3CEnqIBHok/O/UwtfVfk/zljhA+ dZ6v7NJvghnUT0bcCVCzL2Ugex68Lpj1spRDI9u66HQ1AphUGRuHyBsBWFRwifqK0Wil wGVJj595ncSJG5QMbibpqLieErJh1fTYfVftNgnzL7UN5PXuNubXIE1lU1w8gQD1SDaG hWZ6KKZFWeJwQVesGDiiJRt4YlP5YUkImftJIWZUFopmjG/rG1S0MyT2hk4X4i4gUYqB 55EFEP0BemAfIueDHcArKNdLIUu2PYvPsDRaFks6a2rZI0Koa3RlnCcHEme6BCh3zffV NapA== X-Gm-Message-State: AIkVDXLLvlVatuVC1nvlK8GjzAMSVO7fxYFpDysnaoG1/xXNNUwpO4KNysXoxBmXDDpgSexQOCA7cri+weacog== X-Received: by 10.36.25.143 with SMTP id b137mr1928970itb.54.1483974082430; Mon, 09 Jan 2017 07:01:22 -0800 (PST) MIME-Version: 1.0 Received: by 10.107.19.106 with HTTP; Mon, 9 Jan 2017 07:01:21 -0800 (PST) In-Reply-To: References: From: Brian Demers Date: Mon, 9 Jan 2017 10:01:21 -0500 Message-ID: Subject: Re: MFA - Possible Solution? To: "user@shiro.apache.org" Content-Type: multipart/alternative; boundary=001a1144d1c80a0d080545aaa494 archived-at: Mon, 09 Jan 2017 15:01:39 -0000 --001a1144d1c80a0d080545aaa494 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable This is a great thread, thanks Bj=C3=B6rn and Richard! These two approaches are very similar. The AllSuccessfulStrategy (or a custom one) would be important here if you want to ensure both realms have been queried, but I'm assuming this will fail the initial authentication request due to the 2nd factor not being present (Bj=C3=B6rn,= it sounds like you have something working, so please correct me if i'm wrong). Checking to see if the 2nd factor was checked 'isMfaOk()' could also be an Authorization check (a permission check of 'user:2factor', or something) Your 2nd realm would still need to verify the AuthC for the token. But your application's resources could require the 2nd factor or not by using: subject.checkPermission("user:2factor"), or equivalent annotation. On Mon, Jan 9, 2017 at 6:40 AM, Richard Wheeldon < richard.wheeldon@voxsmart.com> wrote: > So I=E2=80=99m guessing in your example the auths are wired more like thi= s: > > /password-login =3D passwordFormAuth > > /token-login =3D passwordFormAuth, tokenFormAuth > > /** =3D passwordFormAuth, tokenFormAuth > > > > Is that right? If so, it makes sense and is probably neater that my > solution. It might also give a point for wiring in IP address checking. e= .g. > > /** =3D ipRangeAuth, passwordFormAuth, tokenFormAuth > > > > Slightly OT but here=E2=80=99s my take on SMS auth in general: > > - It has a weakness because if you have the ability to reset the > password via e-mail and an end-user has inappropriately secured e-mail= on > their phone (pretty common) then the possession of the phone becomes a > single factor for authentication. If you don=E2=80=99t allow password = resets via > e-mail you wind up with another usability issue. > - I agree with your points on the reliability and 3rd party concerns. > - NIST have deprecated it largely because of the VOIP concerns. In our > scenario we can be almost certain that the number in question will > represent a physical mobile phone. This is not true for many use-cases= so > there=E2=80=99s another potential weakness. However, imperfect SMS-bas= ed 2FA is > still better than nothing. > - It=E2=80=99s better for usability purely because a user already know= s how to > receive a text message 99% of the time and they tend to carry phones a= round > with them already. Hence if you=E2=80=99ve got a lot of users who use = the site / > app rarely it=E2=80=99s a simpler option. > - If you have a lot of users but access is rare, SMS is much cheaper > than hard-token. If you have a few users making regular access hard-to= ken > is much cheaper than SMS. > > > > From a Shiro point of view: > > - It=E2=80=99s largely irrelevant as the workflow for a soft-token lik= e Google > authenticator would (AIUI) work in pretty much the same way. > - Using JdbcRealm for obtaining an SMS token is a bit dubious because > the realm only returns one token and it=E2=80=99s quite likely if ther= e is an SMS > delay that the user will retry and we=E2=80=99re then left with two to= kens > generated and the user may try either. It wasn=E2=80=99t something I t= hought of > when I wrote the original mail so I think I=E2=80=99ll have to have to= consider > this carefully. > > > > Thanks for the U2F link and I=E2=80=99ll be sure to take a look, > > > > Richard > > > > *From:* Bj=C3=B6rn Raupach [mailto:raupach@me.com] > *Sent:* Monday, January 9, 2017 7:20 AM > > *To:* user@shiro.apache.org > *Subject:* Re: MFA - Possible Solution? > > > > Interesting. We were looking into SMS, too but I don=E2=80=99t find it us= er > friendly at all. You rely on a third party. SMS often don=E2=80=99t arriv= e within > seconds and there is an additional cost in every sms you send. > > > > I don=E2=80=99t get your difference in workflows. AllSuccessfulStrategy s= ays all > realms have to authenticate. Thats why we needed to create a > AuthenticationToken that works on both realms. It has the username and > password and the challenge from the token generator. First page collects > username and password, second page creates token. Then submit them all. Y= ou > can=E2=80=99t generate the token in advance because you need to find the = matching > user first. > > > > Have a look at U2F! It is a rather cheap hardware token generator. You ca= n > get one for less than 20 EUR. Use it on many sites and there are no > additional license costs. Only downside is it works currently only Chrome > and Firefox (with an extension). > > > > https://www.yubico.com/about/background/fido/ > > http://www.howtogeek.com/232314/u2f-explained-how- > google-microsoft-and-others-are-creating-universal-two- > factor-authentication-tokens/ > > https://fidoalliance.org/specifications/overview/ > > > > > > > > On 9 Jan 2017, at 01:43, Richard Wheeldon > wrote: > > > > I think your idea seems equally reasonable. I considered something > similar. However, we=E2=80=99re using SMS as the 2nd factor, which is imp= erfect > from a security point of view but much better from a distribution and > usability point of view. I=E2=80=99m guessing you have some sort of token= generator > for the 2nd factor. The key point being that there=E2=80=99s a difference= in > workflow: > > User supplies username + password -> System sends token t= o > user -> User inputs token > > vs. > > User generates token -> User supplies username, password > and token > > > > Richard > > > > *From:* Bj=C3=B6rn Raupach [mailto:raupach@me.com ] > *Sent:* Sunday, January 8, 2017 12:02 PM > *To:* user@shiro.apache.org > *Subject:* Re: MFA - Possible Solution? > > > > Hi Richard, > > > > I am currently working on an implementation for U2F with Apache Shiro. I= t > is still not officially > > rolled out but it works and I am also not sure if I did it correct. > > > > Like you I created two realms. Then however I changed the authentication > strategy in the > > security manager. > > > > authcStrategy =3D org.apache.shiro.authc.pam.AllSuccessfulStrategy > > securityManager.authenticator.authenticationStrategy =3D $authcStrategy > > securityManager.realms =3D $jdbcRealm, $u2fRealm > > > > I used U2FAuthenticationToken that extends UsernamePasswordToken > > that works on both realms. The custom U2FRealm overrides supports and > checks that the token > > is an instance of U2FAuthenticationToken. > > > > > > On 6 Jan 2017, at 19:27, Richard Wheeldon > wrote: > > > > Hi, > > > > As a few of you may know, I posted many, many months ago about trying to > get Multi Factor Authentication working on a Shiro-based app. I think I > have a plan that isn=E2=80=99t totally crazy and doesn=E2=80=99t involve = stupid levels of > custom code or changing any of the core Shiro libs. However, I=E2=80=99d = like it > run it by you guys to see what you think and poke holes in it before I tu= rn > it into prod-ready code. Maybe there=E2=80=99s something here that folks = can adapt > in the future if they need to do something similar. > > > > I started off with a pre-existing config with a DefaultWebSessionManager > and a JDBC realm reading username / password and role info from a PG DB. > Apart from a non-standard session manager and a non-standard cookie name, > it=E2=80=99s fairly bog-standard stuff. > > > > What I=E2=80=99m doing now is to split the JDBC logic into two realms. Th= e first > is the same password stuff: > > > > passwordJdbcRealm =3D org.apache.shiro.realm.jdbc.JdbcRealm > > passwordJdbcRealm.permissionsLookupEnabled =3D true > > passwordJdbcRealm.authenticationQuery =3D select password from users wher= e =E2=80=A6 > > passwordJdbcRealm.userRolesQuery =3D select =E2=80=A6 > > passwordJdbcRealm.dataSource =3D $dataSource > > passwordJdbcRealm.credentialsMatcher =3D $passwordMatcher > > passwordJdbcRealm.permissionsQuery =3D select =E2=80=A6 > > > > The second is for accessing one-time authentication tokens: > > > > tokenJdbcRealm =3D org.apache.shiro.realm.jdbc.JdbcRealm > > tokenJdbcRealm.permissionsLookupEnabled =3D false > > tokenJdbcRealm.authenticationQuery =3D select token from auth_tokens wher= e =E2=80=A6 > > tokenJdbcRealm.userRolesQuery =3D select null where ? is not null and fal= se > (must be a neater way just to disable role lookups but this works) > > tokenJdbcRealm.dataSource =3D $dataSource > > tokenJdbcRealm.credentialsMatcher =3D $tokenMatcher > > > > Both of these are now bound to the same old security manager: > > > > securityManager.realms =3D $passwordJdbcRealm, $tokenJdbcRealm > > > > I=E2=80=99ve now also got two form filters - one for the password and a n= ew one > for the one-time token: > > passwordFormAuth =3Dcom.voxsmart.PasswordFormAuthenticationFilter > > passwordFormAuth.loginUrl =3D /login.jsp > > > > tokenFormAuth =3D com.voxsmart=E2=80=A6.TokenFormAuthenticationFilter > > tokenFormAuth.loginUrl =3D /login2.jsp > > > > login2.jsp redirects to login.jsp if the user isn=E2=80=99t authenticated= . > > > > These form filters are wired up like so: > > > > /login.jsp =3D passwordFormAuth > > /login2.jsp =3D tokenFormAuth > > /logout =3D logout > > /** =3D tokenFormAuth > > > > The first is basic except for sending out a token if the login succeeds: > > @Override > > public boolean onLoginSuccess(AuthenticationToken token, Subject subject, > ServletRequest request, ServletResponse response) throws Exception { > > // Generate a one-time token, write it to > the DB and send it to the user via SMS / e-mail / carrier pigeon > > } > > > > The second checks that an MFA attribute is set on the session in order to > allow access and sets it if / only if the token match is found: > > > > private boolean isMfaOk(Object mfaStatus) { > > return "ALLOWED".equals(mfaStatus) || > "NOT_NEEDED".equals(mfaStatus); > > } > > > > @Override > > public boolean isAccessAllowed(ServletRequest request, ServletResponse > response, Object mappedValue) { > > Object mfaStatus =3D subject.getSession().getAttribute("MFA-STATUS"); > > return (subject.isAuthenticated() && isMfaOk(mfaStatus)) || // Additional > check for MFA > > (!isLoginRequest(request, response) && isPermissive(mappedValue)); // > Copy-n-paste from AuthenticatingFilter > > } > > > > @Override > > public boolean onLoginSuccess(AuthenticationToken token, > Subject subject, ServletRequest request, ServletResponse response) throws > Exception { > > boolean ret =3D super.onLoginSuccess(token, subject, request, response); > > subject.getSession().setAttribute("MFA-STATUS", "ALLOWED"); > > // write some audit log entries and other fluff > > return ret; > > } > > > > Does this seem sane? Is there a better way to do it? Is there something > I=E2=80=99m missing or forgotten that will cause this to blow up in my fa= ce at some > later point in time? Is there something that could be cribbed and added t= o > Shiro to make it easier in the future? > > > > Regards, > > > > Richard > > > --001a1144d1c80a0d080545aaa494 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
This is a great thread, thanks=C2=A0Bj=C3=B6rn and Richard= !

These two approaches are very similar. The=C2=A0AllSuccessfulStrategy (o= r a custom one) would be important here if you want to ensure both realms h= ave been queried, but I'm assuming this will fail the initial authentic= ation=C2=A0request due to the 2nd factor not being present (B= j=C3=B6rn, it sounds like you have something working, so please correct me = if i'm wrong).=

Checking t= o see if the 2nd factor was checked 'isMfaOk()' could also be= an Authorization check (a permission check of 'user:2factor', or s= omething)
Your 2nd realm would still need t= o verify the AuthC for the token.=C2=A0 But your application's resource= s could require the 2nd factor or not by using: subject.checkPermission(&qu= ot;user:2factor"), or equivalent annotation.


On Mon, Jan 9, 2017 at 6:40 AM, Richard Wheeldon <richard.wheeldon@voxsmart.com> wrote:

So I=E2=80=99m guessing in your example the auths a= re wired more like this:

/password= -login =3D passwordFormAuth

/token-lo= gin =3D passwordFormAuth, tokenFormAuth

/** =3D p= asswordFormAuth, tokenFormAuth

=C2=A0

Is that right? If so, it makes sense and is probabl= y neater that my solution. It might also give a point for wiring in IP addr= ess checking. e.g.

/** =3D i= pRangeAuth, passwordFormAuth, tokenFormAuth

=C2=A0

Slightly OT but here=E2=80=99s my take on SMS auth = in general:

  • It has a weakness becaus= e if you have the ability to reset the password via e-mail and an end-user = has inappropriately secured e-mail on their phone (pretty common) then the possession of the phone bec= omes a single factor for authentication. If you don=E2=80=99t allow passwor= d resets via e-mail you wind up with another usability issue.=
  • I agree wi= th your points on the reliability and 3rd party concerns.=
  • NIST = have deprecated it largely because of the VOIP concerns. In our scenario we= can be almost certain that the number in question will represent a physical mobile phone. This is not true for many use-case= s so there=E2=80=99s another potential weakness. However, imperfect SMS-bas= ed 2FA is still better than nothing.
  • It=E2=80=99s better for usability pur= ely because a user already knows how to receive a text message 99% of the t= ime and they tend to carry phones around with them already. Hence if you=E2=80=99ve got a lot o= f users who use the site / app rarely it=E2=80=99s a simpler option.=
  • If yo= u have a lot of users but access is rare, SMS is much cheaper than hard-tok= en. If you have a few users making regular access hard-token is much cheaper than SMS.

=C2=A0

From a Shiro point of view:

  • It=E2=80=99s largely irrelevant as the workflow for a soft-token like G= oogle authenticator would (AIUI) work in pretty much the same way.
  • Using JdbcRealm for obtaining an SMS token is a bit dubious= because the realm only returns one token and it=E2=80=99s quite likely if there is an SMS delay that the user will retry and we=E2=80=99re then l= eft with two tokens generated and the user may try either. It wasn=E2=80=99= t something I thought of when I wrote the original mail so I think I=E2=80= =99ll have to have to consider this carefully.
  • =C2=A0

    Thanks for the U2F link and I=E2=80=99ll be sure to= take a look,

    =C2=A0

    Richard

    =C2=A0

    From: Bj=C3=B6rn Raupach [mailto:raupach@me.com]
    Sent: Monday, January 9, 2017 7:20 AM


    To: user@= shiro.apache.org
    Subject: Re: MFA - Possible Solution?

    =C2=A0

    Interesting. We were looking into SMS, too but I don= =E2=80=99t find it user friendly at all. You rely on a third party. SMS oft= en don=E2=80=99t arrive within seconds and there is an additional cost in e= very sms you send.

    =C2=A0

    I don=E2=80=99t get your difference in workflows. Al= lSuccessfulStrategy says all realms have to authenticate. Thats why we need= ed to create a AuthenticationToken that works on both realms. It has the us= ername and password and the challenge from the token generator. First page collects username and password, second pag= e creates token. Then submit them all. You can=E2=80=99t generate the token= in advance because you need to find the matching user first.=

    =C2=A0

    Have a look at U2F! It is a rather cheap hardware to= ken generator. You can get one for less than 20 EUR. Use it on many sites a= nd there are no additional license costs. Only downside is it works current= ly only Chrome and Firefox (with an extension).

    =C2=A0

    =C2=A0

    =C2=A0

    =C2=A0

    On 9 Jan 2017, at 01:43, Richard Wheeldon <richard.wheeld= on@voxsmart.com> wrote:

    =C2=A0

    I think your idea seems equally reasonable. I consi= dered something similar. However, we=E2=80=99re using SMS as the 2nd=C2=A0factor, which is imperfect from a security point of view but much better from a di= stribution and usability point of view. I=E2=80=99m guessing you have some = sort of token generator for the 2nd=C2=A0factor. The key point being that there=E2=80=99s a difference in workflow:

    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 User supplies username + password -= > System sends token to user -> User inputs token

    vs.

    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 User generates token -> User sup= plies username, password and token

    =C2=A0

    Richard

    =C2=A0

    From:=C2=A0Bj=C3=B6rn Raupach [mailt= o:raupach@me.com]=C2=A0
    Sent:=C2= =A0Sunday, January 8, 2017 12:02 PM
    To:=C2=A0=
    u= ser@shiro.apache.org
    Subject:= =C2=A0Re: MFA - Possible Solution?

    =C2=A0

    Hi Richard,

    =C2=A0

    I am currently working on an implementation for U2F = =C2=A0with Apache Shiro. It is still not officially

    rolled out but it works and I am also not sure if I = did it correct.

    =C2=A0

    Like you I created two realms. Then however I change= d the authentication strategy in the=C2=A0

    security manager.

    =C2=A0

    authcStrategy =3D org.apache.shiro.authc.pam.Al= lSuccessfulStrategy

    securityManager.authenticator.authenticationStr= ategy =3D $authcStrategy

    securityManager.realms =3D $jdbcRealm, $u2fRealm<= /u>

    =C2=A0

    I used U2FAuthenticationToken that extends UsernameP= asswordToken=C2=A0

    that works on both realms. The custom U2FRealm overr= ides supports and checks that the token

    is an instance of U2FAuthenticationToken.<= /u>

    =C2=A0

    =C2=A0

    On 6 Jan 2017, at 19:27, Richard Wheeldon <richard.wheeldon@voxsmart.com> wrote:<= /u>

    =C2=A0

    Hi,

    =C2=A0

    As a few of you may know, I posted m= any, many months ago about trying to get Multi Factor Authentication workin= g on a Shiro-based app. I think I have a plan that isn=E2=80=99t totally crazy and doesn=E2=80=99t involve stupid levels of c= ustom code or changing any of the core Shiro libs. However, I=E2=80=99d lik= e it run it by you guys to see what you think and poke holes in it before I= turn it into prod-ready code. Maybe there=E2=80=99s something here that folks can adapt in the future if they need to do something similar.

    =C2=A0

    I started off with a pre-existing co= nfig with a DefaultWebSessionManager and a JDBC realm reading username / pa= ssword and role info from a PG DB. Apart from a non-standard session manager and a non-standard cookie name, it=E2=80=99s = fairly bog-standard stuff.

    =C2=A0

    What I=E2=80=99m doing now is to spl= it the JDBC logic into two realms. The first is the same password stuff:

    =C2=A0

    passwordJ= dbcRealm =3D org.apache.shiro.realm.jdbc.JdbcRealm

    passwordJ= dbcRealm.permissionsLookupEnabled =3D true

    passwordJ= dbcRealm.authenticationQuery =3D select password from users where =E2= =80=A6

    passwordJ= dbcRealm.userRolesQuery =3D select =E2=80=A6

    passwordJ= dbcRealm.dataSource =3D $dataSource

    passwordJ= dbcRealm.credentialsMatcher =3D $passwordMatcher<= /p>

    passwordJ= dbcRealm.permissionsQuery =3D select =E2=80=A6

    =C2=A0

    The second is for accessing one-time= authentication tokens:

    =C2=A0

    tokenJdbc= Realm =3D org.apache.shiro.realm.jdbc.JdbcRealm

    tokenJdbc= Realm.permissionsLookupEnabled =3D false

    tokenJdbc= Realm.authenticationQuery =3D select token from auth_tokens where =E2= =80=A6

    tokenJdbc= Realm.userRolesQuery =3D select null where ? is not null and false (must be= a neater way just to disable role lookups but this works)

    tokenJdbc= Realm.dataSource =3D $dataSource

    tokenJdbc= Realm.credentialsMatcher =3D $tokenMatcher

    =C2=A0

    Both of these are now bound to the s= ame old security manager:

    =C2=A0

    securityM= anager.realms =3D $passwordJdbcRealm, $tokenJdbcRealm<= /p>

    =C2=A0

    I=E2=80=99ve now also got two form f= ilters - one for the password and a new one for the one-time token:<= u>

    passwordF= ormAuth =3Dcom.voxsmart.PasswordFormAuthenticationFilter

    passwordF= ormAuth.loginUrl =3D /login.jsp

    =C2=A0

    tokenForm= Auth =3D com.voxsmart=E2=80=A6.TokenFormAuthenticationFilter=

    tokenForm= Auth.loginUrl =3D /login2.jsp

    =C2=A0

    login2.jsp redirects to login.jsp if= the user isn=E2=80=99t authenticated.

    =C2=A0

    These form filters are wired up like= so:

    =C2=A0

    /login.js= p =3D passwordFormAuth

    /login2.j= sp =3D tokenFormAuth

    /logout = =3D logout

    /** =3D t= okenFormAuth

    =C2=A0

    The first is basic except for sendin= g out a token if the login succeeds:

    @Override=

    public bo= olean onLoginSuccess(AuthenticationToken token, Subject subject, Servl= etRequest request, ServletResponse response) throws Exception {

    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0 // Generate a one-time token, write it to the DB and send it to the = user via SMS / e-mail / carrier pigeon

    }<= u>

    =C2=A0=C2=A0=C2=A0=C2=A0=

    The second checks that an MFA attrib= ute is set on the session in order to allow access and sets it if / only if= the token match is found:

    =C2=A0

    private b= oolean isMfaOk(Object mfaStatus) {

    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return &= quot;ALLOWED".equals(mfaStatus) || "NOT_NEEDED".equals(mfaSt= atus);

    =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<= /p>

    =C2=A0

    @Override=

    public bo= olean isAccessAllowed(ServletRequest request, ServletResponse response, Obj= ect mappedValue) {

    Object mf= aStatus =3D subject.getSession().getAttribute("MFA-STATUS");=

    return (s= ubject.isAuthenticated() && isMfaOk(mfaStatus)) || // Additional ch= eck for MFA

    (!isLogin= Request(request, response) && isPermissive(mappedValue)); // Copy-n= -paste from AuthenticatingFilter

    =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<= /p>

    =C2=A0

    @Override=

    =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 public boolean onLoginS= uccess(AuthenticationToken token, Subject subject, ServletRequest requ= est, ServletResponse response) throws Exception {

    boolean r= et =3D super.onLoginSuccess(token, subject, request, response);

    subject.g= etSession().setAttribute("MFA-STATUS", "ALLOWED");=

    // write = some audit log entries and other fluff

    return re= t;

    }=C2=A0= =C2=A0=C2=A0=C2= =A0

    =C2=A0

    Does this seem sane? Is there a bett= er way to do it? Is there something I=E2=80=99m missing or forgotten that w= ill cause this to blow up in my face at some later point in time? Is there something that could be cribbed and added to Shiro to ma= ke it easier in the future?

    =C2=A0

    Regards,

    =C2=A0

    Richard

    =C2=A0


--001a1144d1c80a0d080545aaa494--