funcool / buddy-auth Goto Github PK
View Code? Open in Web Editor NEWAuthentication and Authorization facilities for ring and ring based web applications.
Home Page: https://funcool.github.io/buddy-auth/latest/
License: Apache License 2.0
Authentication and Authorization facilities for ring and ring based web applications.
Home Page: https://funcool.github.io/buddy-auth/latest/
License: Apache License 2.0
When I'm trying to access a restricted resource with a GET
request, Chrome first sends an OPTIONS
request. But it does not supply the Authorization
header. https://code.google.com/archive/p/twitter-api/issues/2273
How can I tell buddy-auth to just let OPTIONS
requests go unauthenticated?
Or is there a better way to solve this?
I'm using the jws
backend.
Hi,
Love this library.
I took a look at fn->multi
macro, and I don't think just wrapping respond
function around the body will make it able to handle async request.
Take wrap-authorization
middleware for example. Even when you wrap respond
around it, you still only pass 1 argument (request
) to the handler. For Ring handler that supports both sync and async, yes it will work, but that only means that it will always choose the sync implementation. For Ring handler that only supports async, it will just throw error saying not enough arguments.
So I tried to implement some buddy middlewares for async handlers and this is how I make them work.
Also, I noticed there is no tests for async handler. There is only test for the macro.
Upgraded from 0.7.1 to 0.8.1.
Running tests - Fail with above exception.
I did not see any docs regarding upgrade instructions. Maybe I missed it?
Thanks.
Hi,
More than an actual problem is an implementation detail question/observation. According to the description, buddy-auth
is:
Authentication and Authorization facilities for ring and ring based web applications
This means the request and response should meet the Ring SPEC. If you check the :headers
of the Request Map, you'll find that the keys of the map, must be downcased, which lead us to get a particular value with a simple key lookup, e.g. (get-in req [:headers "authorization"])
:headers
(Required, IPersistentMap)
A Clojure map of downcased header name Strings to corresponding header value
Strings.
The current implementation of header lookup is a case-insensitve search through the map keys using buddy.auth.http/-get-header
. Any thoughts on why is like this?
I want to unify the data structure of how the identity data structure looks like. With basic
and token
authorizations, the authfn
lets me hook in and choose what I want to return as an identity.
Can we also have this for all the other backends? JWT, JWS and Session?
Hi, I'm having a weird issue. Everything appears to be configured correctly. But when I send a request, i'm getting a 400 and the following response body.
{
"errors": {
"Authorization": "missing-required-key"
}
}
I've done some debugging, and I've verified that at in buddy.auth.middleware.wrap-authentication below, authdata actually has the data that were pulled from the token at the point of the assoc call. I just can't figure out where it's becoming unhappy.
(defn wrap-authentication
"Ring middleware that enables authentication for your ring
handler. When multiple `backends` are given each of them gets a
chance to authenticate the request."
[handler & backends]
(fn [request]
(if-let [authdata (authenticate-request request backends)]
(handler (assoc request :identity authdata))
(handler request))))
I'm using a luminus +auth-jwe +swagger setup. I've commented out most of the other middle ware handlers to verify that this is the source of the problem. Here's the full setup:
(ns gs-communication-ms.middleware
(:require [gs-communication-ms.env :refer [defaults]]
[clojure.tools.logging :as log]
[gs-communication-ms.layout :refer [*app-context* error-page]]
[ring.middleware.anti-forgery :refer [wrap-anti-forgery]]
[ring.middleware.webjars :refer [wrap-webjars]]
[ring.middleware.format :refer [wrap-restful-format]]
[gs-communication-ms.config :refer [env]]
[ring.middleware.flash :refer [wrap-flash]]
[immutant.web.middleware :refer [wrap-session]]
[ring.middleware.defaults :refer [site-defaults wrap-defaults]]
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]
[buddy.auth.accessrules :refer [restrict]]
[buddy.auth :refer [authenticated?]]
[buddy.auth.backends.token :refer [jwe-backend jws-backend]]
[buddy.sign.jwt :refer [encrypt]]
[buddy.core.nonce :refer [random-bytes]]
[buddy.core.keys :as ks]
[clojure.java.io :as io]
[clj-time.core :refer [plus now minutes]])
(:import [javax.servlet ServletContext]))
(defn wrap-context [handler]
(fn [request]
(binding [*app-context*
(if-let [context (:servlet-context request)]
;; If we're not inside a servlet environment
;; (for example when using mock requests), then
;; .getContextPath might not exist
(try (.getContextPath ^ServletContext context)
(catch IllegalArgumentException _ context))
;; if the context is not specified in the request
;; we check if one has been specified in the environment
;; instead
(:app-context env))]
(handler request))))
(defn wrap-internal-error [handler]
(fn [req]
(try
(handler req)
(catch Throwable t
(log/error t)
(error-page {:status 500
:title "Something very bad has happened!"
:message "We've dispatched a team of highly trained gnomes to take care of the problem."})))))
(defn wrap-csrf [handler]
(wrap-anti-forgery
handler
{:error-response
(error-page
{:status 403
:title "Invalid anti-forgery token"})}))
(defn wrap-formats [handler]
(let [wrapped (wrap-restful-format
handler
{:formats [:json-kw :transit-json :transit-msgpack]})]
(fn [request]
;; disable wrap-formats for websockets
;; since they're not compatible with this middleware
((if (:websocket? request) handler wrapped) request))))
(defn on-error [request response]
(error-page
{:status 403
:title (str "Access to " (:uri request) " is not authorized")}))
(defn wrap-restricted [handler]
(restrict handler {:handler authenticated?
:on-error on-error}))
(def secret (random-bytes 32))
;(def token-backend
; (jwe-backend {:secret secret
; :options {:alg :a256kw
; :enc :a128gcm}}))
(def token-backend
(jws-backend {:secret (ks/public-key (io/resource "pubkey.pem"))
:token-name "GS-Token"
:options {:alg :rs256}}))
(defn token [username]
(let [claims {:user (keyword username)
:exp (plus (now) (minutes 60))}]
(encrypt claims secret {:alg :a256kw :enc :a128gcm})))
(defn wrap-auth [handler]
(let [backend token-backend]
(-> handler
(wrap-authentication backend)
;(wrap-authorization backend)
)))
(defn wrap-base [handler]
(->
((:middleware defaults) handler)
wrap-auth
;wrap-webjars
;wrap-flash
;(wrap-session {:cookie-attrs {:http-only true}})
;(wrap-defaults
; (-> site-defaults
; (assoc-in [:security :anti-forgery] false)
; (dissoc :session)))
;wrap-context
;wrap-internal-error
))
my token data are pretty generic
{
"alg": "RS256",
"typ": "JWT"
}
{
"exp": 1487719225,
"user_name": "user",
"authorities": [
"ROLE_USER",
"ROLE_ACTUATOR"
],
"jti": "4c452287-25f0-4eaa-8179-b0652583e9bf",
"client_id": "demo",
"scope": [
"read",
"write"
]
}
But I don't see where it's expecting a key that might be 'missing', where the middleware decides to make the request return a 400, etc
Hi, I'm trying use the JWS backend for a service. Like so:
(def token-backend
(jws-backend {:secret (ks/public-key (io/resource "pubkey.pem"))
:token-name "Token"}))
The request processing is throwing the following error:
:cause No implementation of method: :-to-bytes of protocol: #'buddy.core.codecs/IByteArray found for class: org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
:via
[{:type java.lang.IllegalArgumentException
:message No implementation of method: :-to-bytes of protocol: #'buddy.core.codecs/IByteArray found for class: org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
:at [clojure.core$_cache_protocol_fn invokeStatic core_deftype.clj 568]}]
:trace
[[clojure.core$_cache_protocol_fn invokeStatic core_deftype.clj 568]
[clojure.core$_cache_protocol_fn invoke core_deftype.clj 560]
[buddy.core.codecs$eval19241$fn__19242$G__19232__19247 invoke codecs.clj 60]
[buddy.core.codecs$to_bytes invokeStatic codecs.clj 69]
[buddy.core.codecs$to_bytes invoke codecs.clj 66]
[buddy.core.mac$eval19698$fn__19699 invoke mac.clj 91]
[buddy.core.mac$eval19608$fn__19609$G__19599__19616 invoke mac.clj 44]
[buddy.core.mac$verify invokeStatic mac.clj 247]
[buddy.core.mac$verify invoke mac.clj 240]
[buddy.sign.jws$fn__20104 invokeStatic jws.clj 33]
[buddy.sign.jws$fn__20104 invoke jws.clj 30]
[buddy.sign.jws$verify_signature invokeStatic jws.clj 95]
[buddy.sign.jws$verify_signature invoke jws.clj 87]
[buddy.sign.jws$unsign invokeStatic jws.clj 133]
[buddy.sign.jws$unsign invoke jws.clj 127]
[buddy.sign.jwt$unsign invokeStatic jwt.clj 82]
[buddy.sign.jwt$unsign invoke jwt.clj 78]
[buddy.auth.backends.token$jws_backend$reify__20813 _authenticate token.clj 46]
I saw a reference to something similar in the buddy-sign issues (funcool/buddy-sign#34), but that was when the signing api was being used directly. I'm just setting up the backend with a public key and passing it to wrap-authentication.
I have tried to use jws_backend function and sometimes works fine but for some shared secrets does not seem to work. I wonder if has to do with the length of the shared secret or some lengths cannot be used? This is the full stacktrace when a request is made to the backend and tries to unsign the shared secret contained in the token:
java.lang.NullPointerException: null
at clojure.string$replace.invokeStatic(string.clj:101)
at clojure.string$replace.invoke(string.clj:75)
at buddy.core.codecs$safebase64__GT_bytes.invokeStatic(codecs.clj:116)
at buddy.core.codecs$safebase64__GT_bytes.invoke(codecs.clj:109)
at buddy.sign.jws$unsign.invokeStatic(jws.clj:176)
at buddy.sign.jws$unsign.invoke(jws.clj:168)
at buddy.auth.backends.token$jws_backend$reify__15853._authenticate(token.clj:53)
at buddy.auth.middleware$wrap_authentication$fn__15974$fn__15978.invoke(middleware.clj:36)
at buddy.auth.middleware$wrap_authentication$fn__15974.invoke(middleware.clj:31)
at compojure.core$routing$fn__10214.invoke(core.clj:144)
I am using version 0.9.0
it should be handy if rate limiting is provided by buddy who also hold all account info
For example, I wrote a test:
(testing "authenticated access with bad credentials"
(d/let-flow
[r (api/handler (-> (mock/request :post "/authenticate")
(mock/header "Authorization" "Token xyzzy")))]
(is (= (:status r) 401))))
However, instead I get a 500 with a weird random exception:
Error in handler-test
Uncaught exception, not in assertion.
expected: nil
actual: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('�' (code 65533 / 0xfffd)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: java.io.StringReader@5ccb2c27; line: 1, column: 2]
Hello,
I have a question/feature request. Say I have a rule defined like
this:
{:uri "/comments" :handler authenticated-user}
This would match both GET /comments
and POST /comments
. What
I really want is just match POST /comments
. I could do this
with the following rule:
{:uri "/comments" :handler {:or [get-request authenticated-user]}}}
It feels a bit strange to have to "unmatch" a rule when I want to
match on the request method as well. I think the common case is
that you have different rules depending on the request method.
Would you be open to add something like this to a rule:
{:uri "/comments" :methods #{:post}} :handler authenticated-user}
{:uri "/comments" :methods #{:delete}} :handler authenticated-user}
This would match only ŕequests with the given request
methods. Without the :methods keyword it would match everything
as is. I think this would make writing access rules a bit easier.
What do you think?
Roman
This allows secure and shorter signatures.
For:
I'm really enjoying using buddy-auth. Thanks much for this work!
I have a question about JWE (and this is probably more about JWE than buddy-auth, but hoping someone here might be able to shed some light).
The JWS and JWE examples are great, thanks for providing those.
The way I understand using JWS: 2 programs know a shared secret. One program uses shared secret to sign, and the other program uses the same secret to validate signature. If I understand that correctly, then that makes sense.
But I'm a little confused about how to take the JWE example and use it in the real world. If the program that encrypts the payload creates the nonce, then how to share that nonce with the program that needs to decrypt the payload?
Thanks for your help,
Dave
Hi, any plan to support async handlers? https://github.com/ring-clojure/ring/wiki/Concepts#handlers
Should be a matter of implementing middlewares with 3-arity instead of 1-arity.
Let's link back to the other buddy packages in the README
so things are a bit easier to navigate and find.
In 6.2. Rules Handlers
portion of the docs, an undefined function admin-user
is used. It wasn't immediately clear to me how I could define such a function for my own use and an example would be very helpful.
What does wrap-authorization
do?
When I have this handler for an access rule:
(defn authenticated-user
[request]
(if (authenticated? request)
true
(error "Only authenticated users are allowed")))
it seems that having wrap-access-rules
and wrap-authentication
is enough. What is the use-case for the wrap-authorization
middleware?
Why does error
return a status 400 here?
I'd assume that the wrap-authorization
middleware would check if authenticated?
is true
, and if not, return the string I supplied with error
together with status code 401
, and if it is true
, return the string I supplied with status code 403
. Am I wrong in expecting this behaviour? Because currently, using error
will result in status code 400
.
edit: I found out that, if I do add the wrap-authorization
middleware, and have my access-rule handler return false
instead of error
, the status codes are as expected: 401
when not authenticated?
, 403
otherwise. This is nice and all, but that means I cannot simply override the error message by using error
(because then I end up with 400
)
The README notes:
NOTE: this project is in maintencance mode, and looking for a new maintainer.
What is maintenance mode? What are the criteria for choosing a new maintainer? I may be happy to maintain the project, but know neither what qualifies someone to maintain the library nor what maintenance mode entails.
Hi,
According to RFC 2617, the authorization scheme should be case insensitive; thus, Basic
and basic
should both be valid.
Here's the relevant part:
HTTP provides a simple challenge-response authentication mechanism
that MAY be used by a server to challenge a client request and by a
client to provide authentication information. It uses an extensible,
case-insensitive token to identify the authentication scheme...
Thanks!
Just a nitpick, but from what I have seen on other sources, the default token name for JWS usually seems to be Bearer as opposed to Token. Should that be changed? One other advantage is that it might be easier to differentiate bt. normal token-based vs jwt based requests.
Due to how cljs is required in cuerda, any project using buddy-auth will rely on cljs.
https://github.com/funcool/cuerdas/blob/master/project.clj#L9
Perhaps referencing how test.check
includes its cljs dependencies will be transferrable here, unsure clojure/test.check@78d7df8
Not a major issue, but probably undesirable.
Thanks for your work on buddy et al!
I'd like to improve the English and make other changes as I read along.
I would like to use multiple backends. Would you take a PR that extends wrap-authentication
to work with multiple backends? Like this:
(wrap-authentication http-basic-backend jws-backend)
The idea is to try all passed backends in order. If one of them succeeds call the handler, otherwise return the failure response of the last backend?
Roman
For unit testing I've found it useful to simulate authenticated requests by simply adding an :identity
entry to my test ring requests. This allows me to create tests for authorization that don't tie themselves to any particular authentication method.
However, buddy-auth doesn't allow this out of the box. The problem is that middleware/authentication-request
will in effect (assoc :identity nil)
to any request that doesn't match a particular authentication backend, overwriting any existing value of :identity
. To get around this, I've created my own authentication backend that will retrieve this value if it already exists in the request:
(def map-entry-backend
(reify
buddy.auth.protocols/IAuthentication
(-parse [_ request]
(:identity request))
(-authenticate [_ request data]
data)))
Other users may find this functionality handy. It may be good to build in this functionality by either
middleware/authentication-request
to respect any existing values of :identity
map-entry-backend
as a build in back-end (possibly with a better name)I guess the main issue is any security risk - I'm assuming that it is not possible for any ring handler to insert an identity entry itself, but I don't know enough about ring to say for sure. If this is an issue, then option 2 might still be worthwhile provided that the documentation adequately warns not to use it in production.
Hello Andrey, I ran into the use case where they JWS my API receives might be signed by different public keys each time and I have to read the token's header claim kid (or jku) to figure out against which key I need to verify the signature (the :secret
param of the jws backend: https://github.com/funcool/buddy-auth/blob/master/src/buddy/auth/backends/token.clj#L36). I was wondering if it's a good idea to extend the jws backend implkeyementation to receive a function as the :secret
in which case the JWS header is passed to it and returns the appropriate secret.
A real example from my project:
When my web service starts I query my authentication service to receive all the public keys available to verify JWS' signatures:
(defstate keys
"Holds the public keys used for verifying JWS' signatures"
:start (...))
With the current implementation, when defining the middleware of my app the only choice I have is to pick one of the keys defined above:
defn base-middleware
"Application base middlewares."
[handler]
(let [secret (first keys)]
(-> handler
...
(wrap-authentication (app.auth/jws-backend secret))
...
)))
But what I would like is to be able to choose a key depending on the claims of the JWS itself, for example:
defn base-middleware
"Application base middlewares."
[handler]
(let [secret (fn [header]
(get-key-by-id keys (:kid header)))]
(-> handler
...
(wrap-authentication (app.auth/jws-backend secret))
...
)))
What do you think?
java.lang.NullPointerException: null
at buddy.auth.middleware$wrap_authorization$fn__14021.invoke(middleware.clj:81) ~[classes/:na]
commit 32d3b70 "Remove slingshot usage"
Missing throw argument
81 - (throw+)))))))
83 + (throw)))))))
I guess avobe changes "(throw e)" ?
I'm using Buddy with Basic Auth and the api-defaults
Ring middleware. The Unauthorized response gets a Content-Type
of octet-stream
. In a (Safari) browser, this results in an empty file being downloaded on Esc from the Basic Auth dialog.
I'm looking for a way to set Content-Type
to say text/plain
, but without having to re-create the same response as Buddy Auth. The only solution now seems to be to create a full response myself in the :on-error
function.
Sorry,
I am not able to any documentation to invalidate the issued token, is there any method do that.
If i completely depend on my jws token, how do i signout the user.
When trying to authenticate a user with a token generated by firebase it is not possible with this library directly.
According to the documentation (https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_a_third-party_jwt_library) the public key to verify the signature of the token has to be obtained from https://www.googleapis.com/robot/v1/metadata/x509/[email protected], which changes continuously. Furthermore, it is not an unique key, but there are many, and to choose which one, it is necessary to check one field from the header (:kid
).
There is another library (alekcz/charmander) that takes care of updating those keys and using them with buddy.sign.jwt
directly, but lacks the backend wrappers that buddy-auth offers.
It could be as easy as allowing the parameter secret
in method jws-backend
buddy-auth/src/buddy/auth/backends/token.clj
Lines 35 to 57 in ec454fe
ifn?
) then it could be called with data as parameter, allowing expandability for this and maybe other cases. (authfn (jwt/unsign data secret options))
(authfn (jwt/unsign data (if (ifn? secret) (secret data) secret) options))
RFC 7235 section 4.1. WWW-Authenticate specifies:
A server generating a 401 (Unauthorized) response MUST send a WWW-Authenticate header field containing at least one challenge.
However, buddy.auth.backends.token/handle-unauthorized-default sends a 401 status response without a WWW-Authenticate
header:
buddy-auth/src/buddy/auth/backends/token.clj
Lines 22 to 27 in 36a02a4
Discovered when trying to use buddy-auth access rules in a project with compojure route definitions that use compojure's context
. I haven't tested to see whether it's specific to compojure or whether it occurs without compojure or without the use of the context
fn.
The crux of the problem is the compiled :pattern
matcher, specifically that it matches against :path-info
if it exists, rather than always using the :uri
. Here's the relevant code:
;; rwilson- Excerpt from buddy.auth.accessrules/compile-access-rule, with comments added
(fn [request]
(let [pattern (:pattern accessrule)
;; rwilson- the inclusion of :path-info here breaks the ability to match the
;; whole uri, which is problematic because :path-info can be non-nil
;; and also irrelevant to the matching, as demonstrated below.
uri (or (:path-info request)
(:uri request))]
(when (and (matches-request-method request request-method)
(seq (re-matches pattern uri)))
{})))
Here's an example. Given the following:
(defroutes routes
(context "/account/:account-id[a-zA-Z0-9_]" [account-id]
(GET "/show" _ (account/show account-id))))
(def access-rules
{:rules [:pattern #"^/account/.*$"
:handler authenticated-user?]})
Say, then, that a request is made to "/account/12345/show"
. The problem is that the request will have a :path-info
value of "/show"
and a :uri
value of "/account/12345/show"
. Since the compiled matcher uses :path-info
over :uri
if it exists, the above access-rule will not match. But, it doesn't make terribly much sense to put an access rule on just "/show"
. After all, it's every URI with "/account/"
at the start that needs protecting, not URIs with "/show"
at the end.
Hi,
I'm trying out the multiple backends support. If you have two backends, with each being a jws-backend, then I've noticed that in the wrap-authentication function, an NPE occurs.
This is because (proto/authenticate current request rsq)
is potentially called when rsq
is null.
This happens because of the check inside of wrap-authentication: if (and (nil? rsq) last?)
. The null check only occurs if last?
is true; the backend is the last one. So for the first backend, if nothing is returned from (proto/parse current request)
, it will still attempt to perform (proto/authenticate current request rsq)
with a null rsq, which causes an NPE.
It is a pretty common expectation that JWT's can be passed using the Bearer token approach. It is also recommended and the main source of info for JWT:
It is a common compliant our customers give us that they need to use Token
instead of Bearer
when using our API. They need to write more code to support it etc...
Would you accept pull requests to all both to be specified?
I'm having difficulty figuring out how all the middleware and options work together.
There are three middleware: wrap-access-rules
, wrap-authentication
, wrap-authorization
. How do these work together and which are necessary? It seems like if I use wrap-access-rules
, I may omit wrap-authorization
.
What options are available for backends? I see :unauthorized-handler
, does a :unauthenticated-handler
exist?
What options are available for wrap-access-rules
? I see :rules
and :on-error
. Does/should :on-error
handle unauthenticated requests? Unauthorized requests? When should :on-error
be used instead of the :unauthorized-handler
option specified in the backend?
These concerns should be better documented.
The idea is support sign, and encrypt-then-sign (sign -> MAC or DS)
Security related details for implementation:
http://crypto.stackexchange.com/questions/202/should-we-mac-then-encrypt-or-encrypt-then-mac
There are a lot of wording, spelling, and grammar mistakes throughout the documentation. I understand English isn't everyone's first language; I just wanted to make a note of this for future contributors.
Buddy-auth should be added to jwt, to help advertise and show what is supported from the spec.
Hi,
I extended the example with dependencies here, should these kinds of full examples be included? I can rebase to current version. Perhaps should go into a separate repository and linked in a wiki or doc.
https://github.com/funcool/buddy-auth/tree/118fe135881ce52dfffe21d73df4fec664c987d4/examples/session
See also this implementation of token based security with buddy:
http://rundis.github.io/blog/2015/buddy_auth_part1.html
Another example:
https://adambard.com/blog/clojure-auth-with-buddy/
In the http-basic section it says to add (wrap-authentication)
, but it seems that (wrap-authorization)
middleware is also needed.
Without this middleware no 'basic auth' dialog is shown to enter the username/password.
This caught me out because it didn't even occur to me that I needed to add "authorization" to do "authentication".
Thanks!
With Ring 3-arity contract, exceptions thrown in the same thread bubble back to the middleware. However, if any non-blocking IO was to happen between wrap-authorization
and throw-unauthorized
, those exceptions would arrive via raise
callback. One such instance could be a OAuth2 authorization backend calling the token endpoint. A simplified example:
(defn handler [request respond raise]
(future ; simulates non-blocking IO switching threads
(try
(throw-unauthorized) ; simplified example
(catch Throwable t (raise t))))) ; raise any handler exceptions per Ring spec
(-> handler
(wrap-authorization nil) ; backend doesn't get called anyway
(apply {:request-method :get} println println nil)) ; run it
; => #ExceptionInfo
; expected: wrap-authorization to catch the raised exception
; and handle it with the backend
Possible fix
(defn wrap-authorization
(fn
; [...]
([request respond raise]
(try (handler request respond
(fn [e] (try (respond (authorization-error request e backend))
(catch Throwable t (raise t))))
(catch Exception e
(respond (authorization-error request e backend)))))))
https://github.com/funcool/buddy-auth/blob/master/src/clojure/buddy/auth.clj#L20
I notice you wrap the expression with boolean
and wonder if there's any particular reason you did this since anything not nil
is a truthy value anyways.
I started with wrap-authorization
with the httpbasic handler, which worked well as long as I peppered my controllers with authenticated?
checks. If unauthenticated, I was prompted for my httpbasic credentials, and only got an "unauthorized" error if I refused to provide them.
Now I'm trying to switch to wrap-access-rules
so I can protect URLs with a regex and DRY up my auth config. But now everything returns my on-error
response, and I can't figure out how to prompt for httpbasic credentials when I receive an authentication failure.
Does buddy-auth include a way to this? Can the docs please get updated with an example?
Since section 6. Access Rules
is part of the authorization process, perhaps it should be a subsection of 5. Authorization
.
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.