Grafana: Automatic login by token url

Created on 15 Jan 2016  ·  95Comments  ·  Source: grafana/grafana

Should be nice to have an automatic login passing an user token thru url, this could be a partial solution to embed iframes on sites.

arebackenauth help wanted prioritimportant-longterm prioritunscheduled typfeature-request

Most helpful comment

This is pretty important for interoperability with SaaS.
We have our own authentication and dashboards, we want to provide the ability to pass a user from our dashboard across to Grafana - in a similar vein to how Heroku interoperates with New Relic and other services with zero-passwords.

Providing a url+token authentication method is par-for-the-course.
If someone then goes and embeds that on a public website, then no amount of security will save that person from themself. That's their fault.
Providing sufficient warnings in documentation / UI should be fine for this.

Without this functionality, users must encounter a second login screen, where they will be confused as to what username / password to enter.

This is a big blocker for us.

All 95 comments

would be pretty insecure as anyone can see the token, you can create a snapshot of the visible data and embedd that, it would be more secure

Yes but using snapshots you don't have the permission to edit it, or is there any way to edit dashboard thru snapshots ?

This is pretty important for interoperability with SaaS.
We have our own authentication and dashboards, we want to provide the ability to pass a user from our dashboard across to Grafana - in a similar vein to how Heroku interoperates with New Relic and other services with zero-passwords.

Providing a url+token authentication method is par-for-the-course.
If someone then goes and embeds that on a public website, then no amount of security will save that person from themself. That's their fault.
Providing sufficient warnings in documentation / UI should be fine for this.

Without this functionality, users must encounter a second login screen, where they will be confused as to what username / password to enter.

This is a big blocker for us.

Sounds like a good feature to have in Grafana. Would like help with PR as there is so much other stuff that is really high priority right now

Nice explanation @adamlwgriffiths the same thing happens to me.

Right now We solved this issue with a little hack, adding a javascript that automatic login to grafana, but is a temporal solution

+10 for this!!
To make this secure there should be a hash of the username + password + date, contained in the token. This could be used to expire the token after x number of hours to improve security.

If I get some time I'll seriously consider working on this

If you're storing the date of the token's creation so you can expire it, then you're also storing the hash and the associated account, so I don't think the hash needs to be generated from any particular data. It just needs to be a sufficiently large enough search space to make guessing infeasible.

@adamlwgriffiths, this should support some basic roles aswell.. such as viewer, editor.. etc, similar to what is done with the API keys.. Perhaps we could just extend that so we could pass an api key in via url?

I think - in the long term - the user / org / api token needs to be consolidated, rather than a new method added.
If the API tokens were expanded to allow use from the HTML interface then this wouldn't be a problem.
The API tokens already have roles, and can be revoked, so it just needs to be usable for the web view rather than pure API.

IMO API tokens should work the same as normal users. There's an arbitrary segregation that isn't documented and I find to be counter-intuitive. Enabling all the login checks to also check for API keys would be the best I think.

Has this feature been implemented? I was trying to have a user automatically get redirected to the grafana dashboard from my site..

@comcomservices As a follow-up on the token itself, you definitely could encrypt information inside the token with the user id, and the token expiring time. That way you can just read the token itself and get the information without requiring tokens to be stored in the db and running 'sweep and clear' over old tokens.

I am hesitant to recommend such a thing, I firmly believe that cryptography is hard, and without a full understanding of what you are doing, it's not a good idea to attempt to do it.

The other issue with encrypted tokens, is that they are rather large, and the more information the larger they become.
Where as a random string can be made to any length, as long as the space is large enough to make brute force infeasible.

@adamlwgriffiths as far as cryptography goes, I have a formal background in it and enjoy it but for this case I'm in full agreement that a token would be best.. Just trying to figure out this go stuff, been doing c++ and asm for way too long... I'll get it though and this is first on my list

I'm looking forward to this feature! Regarding @adamlwgriffiths comment above with respect to embedding the token on a site, I believe the way to mitigate that is associating that token with a domain that should be requesting it; that way that token only works when the origin matches. An example of this -- again, if I understand the issue correctly -- is Sentry's raven-js; it will make a public URL and then restrict it by origin (see below). Would this mitigate the security concern?

screen shot 2016-03-24 at 10 57 18 am

@Germanaz0 is your auto-login JS publicly available? I'm looking to implement something similar against the master branch and it appears that the backend redirects to the login screen prior to hitting the dashboard page. I'm not super familiar with the project structure yet, so I may not be plugging into the right section in the JS code.

@rca yes, but my solution is super simple https://gist.github.com/Germanaz0/d41b5f60dd8097405b6b

You receive an input parameter like ?t=base64 of a json with {user: _USERNAME_, pass: _PASS_, redirect_to: _URL_TO_REDIRECT }

You shall include that script into the login page.

@Germanaz0, thanks for sharing! After sending my comment I started taking a look and, as you pointed out, I found that I needed to update the login page. I took a slightly different approach and updated the login controller instead of making a stand-alone script; my change is here: https://github.com/zymbit/grafana/tree/auto-login-by-cookie-redirect

Is this worthy of a pull request? I'd be happy to make the mods necessary to get this into a merge'able state.

Any update on this?
@rca Did you create a PR?

+1 for a PR from @rca

@Germanaz0, which file are you including your .js file in?

@rca do you have any instructions on how to use your login controller mod? i am new to grafana and trying to get this working for an unattended kiosk.

BTW if anyone else needs a way to accomplish this for kiosk's, etc., chek out Grafana's authproxy. I was able to accomplish what I needed to with this and apache.

@scottfuhrman Nothing formally written, however, here's my usecase and implementation:

I wasn't looking for kiosk mode, but rather a way to embed a dashboard as an iframe on an external page. The charts are embedded pretty clean, for example:

screen shot 2016-12-14 at 10 24 06 am

On the page you're looking to embed the dashboard, you need the following markup:

<div id='grafana-dashboard' class="col-lg-12"></div>


<script type="text/javascript">
    GrafanaEmbed = {
        grafanaUrl: 'https://your.grafana.example.com',
        dashboard: 'dashboad-name',
        queryParams: {
            dashnav: 0,
            // this is a base64-encoded string of username:password
            // for example on a *NIX machine (and Mac OS X):
            // $ echo "kiosk1:supersecret" | base64
            // a2lvc2sxOnN1cGVyc2VjcmV0Cg==
            auth: 'a2lvc2sxOnN1cGVyc2VjcmV0Cg==',
            theme: 'light'
        }
    };

    (function() {
        var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true;
        d.src = GrafanaEmbed.grafanaUrl + '/public/app/features/dashboard/embed.js';
        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(d);
    })();
</script>

Hope this helps.

+1

I don't have the staffing resources for this, but I'd be happy to crowdfund the effort for this.

Thought I had this solved until I tried to use a playlist. Using authproxy works for a given dashboard but once I tried a playlist with multiple dashboards ran into the same problem. I don't understand the point of having a kiosk mode when it is impossible to implement a real kiosk application unless we completely disable viewing security?

@torkelo I've recruited @over64 to work on this for me.

How do you recommend this to be implemented? Basically what I need is to have a token in the URL string that bypasses/solves authentication. What's the best approach for this?

not sure, would have to research how such a feature should be implemented securely

Got it. We're ready once you have devised a plan :)

if you have an idea for how to do please let me know and I can evaluate it

Alright. @over64 will take a look and suggest something.

@torkelo Can you look at @over64's PR in #7431 to see if that makes sense.

Any easy workaround? I'm trying to implement @Germanaz0 solution but it isn't working for me :(

This is a must have, else one is blocked on embedding snapshot within iframe. The embedded snapshot url should support login key/token.

Hey everyone,

More recent comments in this thread are with respect to:

  • security
  • embedding within an iframe

With respect to security please consider my old comment above: https://github.com/grafana/grafana/issues/3752#issuecomment-200874453

And regarding embedding in an iframe: https://github.com/grafana/grafana/issues/3752#issuecomment-267062843

This branch (albeit quite old) contains code borrowed from Discourse that nicely sizes an iframe as well as authenticate via token as described in the comment above.

@torkelo Please let me know if you would consider an pull request incorporating the elements above.

@rca what is that embed.js in your code above?

@zoell it's some js borrowed from the Discourse project to nicely size an iframe on the page based on the embedded content with the frame.

@rca oh thanks. I could not find that file in the branch you mentioned. Is that available somewhere else?

@zoell, I will fork that repo and include that file as it should be in there.

@rca thanks that would be great.

@rca I'm sorry, I still can't find the file in the branch you mentioned before.

@TinaRen @zoell I dropped the ball on this, apologies.

However, after taking a look just now, it looks like the file has been there all along! 😬

I will be doing any additional work on this at:

https://github.com/rca/grafana

And the file is at:

https://github.com/rca/grafana/blob/embedding/public/app/features/dashboard/embed.js

If this file isn't building and ending up in the public_gen/ directory, please let me know.

Thanks!

Is it possible to at least limit grafana to localhost accesses? (to narrow panel sharing to same server site)

I would love to see someone taking a stab at this with a proper implementation that can go upstream. The solution we proposed (#7431), was never accepted :(

I'm happy to have someone from our side spend some time on this, but we'd need guidance on what would be acceptable upstream.

Managed to do this with success using Grafana's auth.proxy and Nginx's ngx_http_secure_link_module

The links I use are in the format http://grafana/?user=nayar&md5=2tutcea9nfdsfdsfdsw&expires=1505386800

Once the user clicks on it, the session cookie is set and the user can browse grafana as if he logged in normally.

Advantages:

  • Link expires after some time + security
  • Link generates hash with userid, timestamp and passkey + security
  • No need to type in password + convenience

My nginx conf is like this

server {
    listen 3001 default_server;
#   listen [::]:3001 default_server;

    server_name _;

        location / {
        set $user "";
        set $state "";

        if ($args ~ "^user=(.+)&md5") {
                    set $user $1;
                    set $state "${state}U";
                }

        secure_link $arg_md5,$arg_expires;
                secure_link_md5 "$secure_link_expires$uri$user grafanarocks";

                if ($secure_link = "") { 
                    set $state "${state}S1";
                }

                if ($secure_link = "0") { 
                    set $state "${state}S2";
                }

                add_header X-uristate "$state";

                if ($state = "US1") { return 403; }
                if ($state = "US2") { return 410; }

                add_header X-uri "$user";

                proxy_set_header X-WEBAUTH-USER $user;

                proxy_pass http://127.0.0.1:3000;
            }
    }

Feel free to contact me if you need help with this

Very interesting, @Nayar. However, my concern with this solution is that it requires a fairly large amount of customizations. I would prefer something that's built into Grafana natively.

I used a stock Grafana. Just had to recompile a stock nginx with the module enabled and that's it.

Nayar , can you please let me know whether the solution you implemented can work with all versions of grafana.i am new to Grafana. In my organization,they have already installed

Trowing out some ideas (and problems) of how to solve this.

Quickest solution:

1) add a special type (flag) on an API key so that it can be used in url to sign you in.

Proos: API keys can only be added by Org Admins.


2) make it possible for users to create api keys & url keys (variant of api key).

Problems with this is going to be oauth setups where if a user can create an api key or url key they will be able to use this after they have been removed access from Grafana (from the oauth system). As the api key or url key will not require an oauth login. This is going to be a big problem for user api keys when we eventually do them. One mitigating solution could be to only allow org admins or grafana server admins to create user api keys.

Thoughts @DanCech ? Go ahead with using an API key with an AllowUrlLogin flag?

I don't think that using the API key infrastructure is the right way to go, since it makes it difficult to add more functionality later (for example, limiting the url to a particular set of dashboards, allowing non-admins to create login links) and is likely to be confusing for users. We could certainly use the same concepts and have a similar backend structure, but it gives us more options if we separate it, and I think it will be clearer to users.

As far as the oauth login question, I think we have to figure out what the scope should be here. The same problem exists for oauth users if you also have passwords enabled, since they can set a local password and would be able to log in that way after the oauth account is disabled. The only real solution to that problem is to store that link in the user account and always send an oauth user through the oauth redirect loop before accepting their login.

I also think we need to come up with a reasonable name for this functionality. "Login URL" might be too broad and doesn't really leave room for use in future as a way to for example share a live link limited to a particular dashboard.

@mattttt would love to get your thoughts

since it makes it difficult to add more functionality later (for example, limiting the url to a particular set of dashboards, allowing non-admins to create login links)

I am hesitant to link this login feature to permissions or restrict it to specific dashboards (other than linking it to an org role or user). Because it would turn this into a share feature and set security expectations that are not fulfilled (since you can query all data from a data source that is used by an org).

The only real solution to that problem is to store that link in the user account and always send an oauth user through the oauth redirect loop before accepting their login.

Not sure how that solves "revoking" the login url / user api token. If they never try to login with oauth after their access was removed Grafana would never know.

@hemsush According to this blog post, it should work as from Grafana 2.0 upwards: https://grafana.com/blog/2015/12/07/grafana-authproxy-have-it-your-way/

I am hesitant to link this login feature to permissions or restrict it to specific dashboards (other than linking it to an org role or user). Because it would turn this into a share feature and set security expectations that are not fulfilled (since you can query all data from a data source that is used by an org).

That's a problem that needs to be solved, so my suggestion would be that this system be designed with that use-case in mind for the future.

Not sure how that solves "revoking" the login url / user api token. If they never try to login with oauth after their access was removed Grafana would never know.

My thought is that the view the client gets when using one of these links should be similar to the "anonymous" user, where they are not magically logged in as a particular user but rather get a limited Viewer account, which would totally divorce this feature from individual user accounts. That would sidestep the issue of the individual user account permissions, reduce the risk level since a link couldn't be used to make any changes, and allow centralized management of all active access links by org admins.

Solution proposal

1) Introduce new concept "Viewer URL Token", that you can add / remove from API Keys page (we can make a new page for this later). Store in new table url_token and they will function from a security perspective very similar to api keys. That is they will be generated and validated the same way api keys are. The can however be used to log you in via usage in url with "&url-auth-token=".
2) Option to restrict the token to only work with PNG render API (will be more secure as users with URL token will not be able to issue any query only view existing dashboards/panels)
3) In the future we will have to find a way to link the token to user groups and dashboard permissions, not sure how that will work yet. I think it could be good to create a dummy user for this that can be used give the "URL Token" users permissions to specific dashboards and data sources. Would hate to have to have explicit checks / joins for url tokens in permission checks.

4) Make it clear that anyone with a url token will have access to all the organisations data sources (and can technically issue any query) (unless the restrict to render api option is used).

thoughts @bergquist @DanCech

Option to restrict the token to only work with PNG render API (will be more secure as users with URL token will not be able to issue any query only view existing dashboards/panels)

I don't think PNG rendering is good solution. It will flicker and it wont look as nice as real Grafana.

I like the idea of connecting the token to an user. Easiest way to connect that token/login to user groups and dashboard folder permissions. But I think logging in using the token (tv login/view mode/tv mode) should force the user to be in Viewer role.

Make it clear that anyone with a url token will have access to all the organisations data sources (and can technically issue any query) (unless the restrict to render api option is used).

Adding information and warnings when create new tokens might help.

The problem with tying a token to a "normal" user is that it causes all sorts of problems when the user gets modified or deleted. If the url tokens are able to be members of a group in the same way users are then there would be a huge amount of flexibility in setting up individual urls with access to particular dashboards etc.

I'm not sure that it would add much complexity to the permission checks, you would be using a different table for the group members depending on whether the viewer is a regular user or a url token but the access checks throughout the system shouldn't need to change.

As far as actually enforcing access to the data sources, there is no difference here than with regular users. The end solution for that problem is to move the data source plugins to the backend and have the frontend dispatch data source requests by specifying dashboard, panel, time range and template var values and having the actual queries built on the backend after validating that the user has access and that the template var values are legitimate.

The problem with tying a token to a "normal" user is that it causes all sorts of problems when the user gets modified or deleted

The problem that anyone who knows a token (even if the account is shutdown) can view dashboards is true for both solution. Revoking access can be done by deleting user or token.

I'm not sure that it would add much complexity to the permission checks, you would be using a different table for the group members depending on whether the viewer is a regular user or a url token but the access checks throughout the system shouldn't need to change.

The checks should look the same regardless of authentication. So that's not gonna be a big problem. The UI for adding tokens/users to groups, dashboards etc might be messy and will only help a very small number of users.

As far as actually enforcing access to the data sources, there is no difference here than with regular users. The end solution for that problem is to move the data source plugins to the backend and have the frontend dispatch data source requests by specifying dashboard, panel, time range and template var values and having the actual queries built on the backend after validating that the user has access and that the template var values are legitimate.

This is where I think we should start working to provide a good experience for sharing dashboards in a secure way. Without this all solution feels like bad trades offs. I think it should be keept at bare minimum until datasource access is solved.

I also met this issue with embed dashboard in username+passwd+ldap auth mode...
Any update about this?

I am very interested in the viewer token option. If I can assist in any way I would be glad to assist.

Hello guys!
I'm trying to create an authenticated link for a dashbord, I have a backend in java and a front end in JSF, I want to put a user on the screen that, when clicked, will redirect the user directly to his dashboar, I'm not understanding the concept well, someone Do you have an example of how I can do this?

Hi,

Just an idea, trying to help. Why not add an new user API, using the Basic Auth like this
? curl https://admin:admin@localhost:3000/api/user/cookie
and get an JSON result like
{"user_name":"admin","cookie_name":"grafana_,session":"a0b1c2d3e4","remember":"da27ef425e9e0d"}

It would be secured via https, and I could use this informations to forge cookies and authorise users seeing their beautiful charts.

I look with attention your code and I think (very respectfully) it wouldn't be too difficult.
Instead sending :
user.Rands+user.Password, setting.CookieRememberName, user.Login, days, setting.AppSubUrl+"/"

to the SetSuperSecureCookie function, you can create an new function, inspired of SetSuperSecureCookie, something like this :

func (ctx *Context) NewFunc(secret, name, value string, others ...interface{}) {
   key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
   text, err := com.AESGCMEncrypt(key, []byte(value))
   return hex.EncodeToString(text)
}

The answer could be elaborate and send like in one of your function like :

func getUserUserProfile(userId int64) Response {
    query := m.GetUserProfileQuery{UserId: userId}

    if err := bus.Dispatch(&query); err != nil {
        return ApiError(500, "Failed to get user", err)
    }

    cook:= array
    result := {
        user_name: user.name,
                cookie_name:   cookie.name,
        session: s.session,
                remember: cokie.remember

    }

    return Json(200, &result)
}

I know that you are not a magician and that coding is not playing Lego but this is a very important function for me. I would try to help.

During GrafanaCon @DanCech mentioned the idea creating public playlists with a GUID key. Similar to how snapshots work today. Sharing/storing such key could be considered as safe as an API token, but limited to viewing playlists. The playlist could be associated with the creator/updater to validate dashboard view permissions.

The URL could look something like https://play.grafana.com/playlists/public/<hash>

One thing I like about this solution is that it restricts the functionality to only viewing playlists(dashboard) which I think is the biggest use case for tokens like this.

But we would still need to login the user somehow since dashboard acl/annotations etc will require server-side authentication.

I guess we can think more about this kind of solution. I mainly wanted to braindump our conversation from GrafanaCon :)

Thanks @bergquist , definitely good to get this stuff written down while it's still fresh!

The next evolution of that thought was to add the concept of a "screen" to decouple things further, so that the user could create a "screen" that then had a secret hashed url that it would connect to. This would serve the same purpose but would allow for management of the screens from within Grafana, so the user could control which playlist was shown, etc.

The end goal here would be to support a mechanism for a user to easily create an sd card or usb image that could be used to boot a dedicated raspberry pi and display the desired playlist securely from Grafana without having to jump through authentication hoops.

The end goal here would be to support a mechanism for a user to easily create an sd card or usb image that could be used to boot a dedicated raspberry pi and display the desired playlist securely from Grafana without having to jump through authentication hoops.

@DanCech This is exactly the use case I am looking for.

The next evolution of that thought was to add the concept of a "screen" to decouple things further, so that the user could create a "screen" that then had a secret hashed url that it would connect to.

@DanCech This sounds perfect!

Hi everyone,

This seems to be a little bit far away from the original issue opened by Germanaz0

Should be nice to have an automatic login passing an user token thru url, this could be a partial solution to embed iframes on sites.

and explained by adamlwgriffiths

We have our own authentication and dashboards, we want to provide the ability to pass a user from our dashboard across to Grafana - in a similar vein to how Heroku interoperates with New Relic and other services with zero-passwords. Providing a url+token authentication method is par-for-the-course.

Germanaz0 develop a js script

Right now We solved this issue with a little hack, adding a javascript that automatic login to grafana, but is a temporal solution

I understand that they need an automatic login to use grafana chart in their own site. This what I need too whithout using playlist... Would it be possible ?

If you just want to automatically log a user in your best bet is to use http://docs.grafana.org/tutorials/authproxy/

@DanCech the issue is that would allow all users to automatically log in. We want a way to only automatically log in users who have a specific URL.

@gzzo that's dependent on the design of the proxy

I'm looking for this feature in Grafana too.
I think Microsoft Power BI embed is the good solution. See links below.
https://github.com/Microsoft/PowerBI-JavaScript/wiki/Embedding-Basics
https://microsoft.github.io/PowerBI-JavaScript/demo/v2-demo/index.html

I think PowerBi solution above is based on OAuth2.
Grafana server side shall support OAuth2 and enable CORS.

@Nayar I'm new to Grafana. What do you mean stock Grafana and stock Nginx which you commented on Sep 14, 2017? Do you have any link both?

Waiting eagerly for 5.4 release I guess.

Managed to do this with success using Grafana's auth.proxy and Nginx's ngx_http_secure_link_module

The links I use are in the format http://grafana/?user=nayar&md5=2tutcea9nfdsfdsfdsw&expires=1505386800

Once the user clicks on it, the session cookie is set and the user can browse grafana as if he logged in normally.

Advantages:

  • Link expires after some time + security
  • Link generates hash with userid, timestamp and passkey + security
  • No need to type in password + convenience

My nginx conf is like this

server {
  listen 3001 default_server;
#     listen [::]:3001 default_server;

  server_name _;

        location / {
      set $user "";
      set $state "";

      if ($args ~ "^user=(.+)&md5") {
                    set $user $1;
                    set $state "${state}U";
                }

      secure_link $arg_md5,$arg_expires;
                secure_link_md5 "$secure_link_expires$uri$user grafanarocks";

                if ($secure_link = "") { 
                    set $state "${state}S1";
                }

                if ($secure_link = "0") { 
                    set $state "${state}S2";
                }

                add_header X-uristate "$state";

                if ($state = "US1") { return 403; }
                if ($state = "US2") { return 410; }

                add_header X-uri "$user";

                proxy_set_header X-WEBAUTH-USER $user;

                proxy_pass http://127.0.0.1:3000;
            }
    }

Feel free to contact me if you need help with this

Hi Nayar,
I am trying to do the integration grafana with the SaaS.
The requirement is after the user login in my web application and then click 'grafana' link in my page, it will redirect to the grafana dashboard without enter username and password.

Your comment seems meet my requirement. But I am still not clear about the authentication process.
When proxy_pass to grafana, how to verify the user is authenticated by my own application.

My web application is via X-XSRF-TOKEN to check the user authority.

I am new to the server config.It's appreciated for your help.

@torkelo / @bergquist Just a heads-up - this is how we worked around this in Screenly. It's live and works great! Thanks for the hard work, guys!

@torkelo / @bergquist Just a heads-up - this is how we worked around this in Screenly. It's live and works great! Thanks for the hard work, guys!

@vpetersson I'm not using screenly but I'm curious how you were able to implement this. I understand the generating an API token. Did you still iframe in the URL? How did you append on the Authentication token into the request headers?

@GimpMaster We just use the auth header, so no need for iframes. Since we wrote the player/browser we were able to inject the auth header in the page load.

I've just made my Kiosk mode working by using the approach from Screenly that @GimpMaster linked. I've used the Chrome Extension ModHeader where I've added the Authorization header with the Bearer API Key. But as Extensions need some time to load the sleep 10s from Setting up a kiosk on raspberry pi made the trick. This solution would also work for iframes but has to be configured on every client then.

@Nayar I've successfully adapted your suggested ngnix configuration to use _unsecure_ access. That is, it just takes the username from the query string and puts it in the X-WEBAUTH-USER. This then gets the a grafana session id and we're away. Thanks!

I'm unable to get the md5 method to work. I'm unclear on what exactly I need to made a md5 fingerprint of. Could you elaborate? Are there any tricks on making the md5?

Is possible implement any solutions to login by token/cookie/header/... in grafana 6.*?

I'm unable to do it with a simple link panel in a blank PHP

index.php

<html>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script language="javaScript">
$( document ).ready(function() {
$.ajax({
        type: "GET",
        //url: "http://page.test;/grafana/",
        url: "http://page.test;/grafana/d/o24Tt1Cik/dashboard-test?orgId=1",
        contentType: "application/json",
        xhrFields: {
            withCredentials: false
        },
        beforeSend: function(request) {
                     request.setRequestHeader("X-WEBAUTH-USER", "admin");
                },
        headers: {
            // Set any custom headers here.
        "X-WEBAUTH-USER" : "admin"
        },
        success: function(data){
                var iframeDoc = $("#monitoringframe").get(0).contentDocument;
                iframeDoc.open();
                iframeDoc.write(data);
                iframeDoc.close();
//$("#monitoringframe").attr('srcdoc',data)
        },
        error : function(err) {
            console.log('Error!', err)
        }
    });
});

</script>
<iframe name="monitoringframe" id="monitoringframe" src="about:blank" sandbox="allow-forms allow-popups allow-same-origin allow-scripts" width=100% height=600 border="0" frameborder="0" />
</body>
</html>

nginx

 server {
  listen 80;
  server_name page.test;
  root /var/www/page;
  index index.html index.htm index.php;

        location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
        location /grafana/ {

                proxy_pass http://localhost:3000/;

       }

}

grafana.ini

[auth.proxy]
# Defaults to false, but set to true to enable this feature
enabled = true
# HTTP Header name that will contain the username or email
header_name = X-WEBAUTH-USER
# HTTP Header property, defaults to `username` but can also be `email`
header_property = username
# Set to `true` to enable auto sign up of users who do not exist in Grafana DB. Defaults to `true`.
auto_sign_up = true
# If combined with Grafana LDAP integration define sync interval
ldap_sync_ttl = 60
# Limit where auth proxy requests come from by configuring a list of IP addresses.
# This can be used to prevent users spoofing the X-WEBAUTH-USER header.
# Example `whitelist = 192.168.1.1, 192.168.1.0/24, 2001::23, 2001::0/120`
whitelist = 127.0.0.1, ::1/120
# Optionally define more headers to sync other user attributes
# Example `headers = Name:X-WEBAUTH-NAME Email:X-WEBAUTH-EMAIL`
headers =

[auth.basic]
;enabled = true

I tested change my nginx config to this and set "?user=myUser" in the url, but only get a unauthorized response

location /grafana/ {

                error_log /var/www/grafana/error.log;
                access_log /var/www/grafana/access.log;

                set $user "";

                if ($args ~ "^user=(.+)") {
                    set $user $1;
                }

                add_header X-uri "$user";

                proxy_set_header X-WEBAUTH-USER $user;

                proxy_pass http://localhost:3000/;

       }

recorte

@mr0bles Everything around auth proxy is explained in the documentation. Haven't seen a solution earlier where you call the proxy with "X-WEBAUTH-USER - usually you authenticate in your proxy which in turn populate and provide the X-WEBAUTH-USER to grafana.

@marefr thanks for the answer.
I read the documentation and my API work it, but i need dynamic user auth (not the same permission for all user of the page)

This solution of @Nayar include the X-WEBAUTH-USER in the header without hardcode it, but I can't make it work

I think something broke in 6.0 or 6.1.
I updated a fully working install using auth proxy in Apache2 to 6.1 yesterday (from 5.3) and am getting exactly the same screens as @mr0bles

Please note that Grafana does seem to understand the proxy header: It does initially log the user in, but subsequent requests get a 401 response.

@Bitblade we haven't received any other reports similar to yours. Please open a new bug report since this issue is regarding a feature request.

Managed to do this with success using Grafana's auth.proxy and Nginx's ngx_http_secure_link_module

The links I use are in the format http://grafana/?user=nayar&md5=2tutcea9nfdsfdsfdsw&expires=1505386800

Once the user clicks on it, the session cookie is set and the user can browse grafana as if he logged in normally.

Advantages:

  • Link expires after some time + security
  • Link generates hash with userid, timestamp and passkey + security
  • No need to type in password + convenience

My nginx conf is like this

server {
  listen 3001 default_server;
#     listen [::]:3001 default_server;

  server_name _;

        location / {
      set $user "";
      set $state "";

      if ($args ~ "^user=(.+)&md5") {
                    set $user $1;
                    set $state "${state}U";
                }

      secure_link $arg_md5,$arg_expires;
                secure_link_md5 "$secure_link_expires$uri$user grafanarocks";

                if ($secure_link = "") { 
                    set $state "${state}S1";
                }

                if ($secure_link = "0") { 
                    set $state "${state}S2";
                }

                add_header X-uristate "$state";

                if ($state = "US1") { return 403; }
                if ($state = "US2") { return 410; }

                add_header X-uri "$user";

                proxy_set_header X-WEBAUTH-USER $user;

                proxy_pass http://127.0.0.1:3000;
            }
    }

Feel free to contact me if you need help with this

This solution doesn't work for me. Because I think each request should contain valid X-WEBAUTH-USER header. It doesn't work if you fill the header on the first request, get cookies and go with them.

I ended with the next solution:

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  client_max_body_size 10m;
  root /foo/public;

  location /grafana/ {
    auth_request /gauth;
    auth_request_set $user $upstream_http_user;

    proxy_set_header X-WEBAUTH-USER $user;
    proxy_pass http://localhost:4000/;
  }

  location / {
    try_files $uri @app;
  }

  location @app {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-SSL-Client-Cert $ssl_client_cert;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_redirect  off;
  }
}

So the solution uses auth_request nginx module. My app is responsible for access control (/gauth request) and returns username in User response header.

Hi, in the meantime, you can use my solution which is described here : https://github.com/guysoft/FullPageOS/issues/175

Here is my workaround using Grafana's Generic OAuth Authentication and PHP: https://github.com/nbayramberdiyev/grafana-generic-oauth

Hope this will help you.

just wanted to comment how i solved this using http basic auth https://github.com/grafana/grafana/issues/13706#issuecomment-540958284

Should be nice to have an automatic login passing an user token thru url, this could be a partial solution to embed iframes on sites.

@rca can you give an example with your code?I copy your code and embed.js,but it not worked.

If you're using oauth there's a way to pull off an embedded grafana without a double login

  1. Setup grafana with a single oauth provider and no other login mechanisms
  2. Set GF_AUTH_OAUTH_AUTO_LOGIN to true
  3. Host grafana at a subpath and use a reverse proxy so that grafana is served at the same host as your main app
  4. Hook your main app and grafana to the same oauth provider (to the oauth provider they will be the same "app")
  5. Embed grafana
  6. When the iframe loads, grafana will try to login automatically with oauth (which should succeed since its at the same domain as your main app, thus sharing the same auth cookie)

EDIT: You may need to set security.cookie_samesite=none in grafana for this to work correctly in some browsers (This is because in the iframe, a redirect to the oauth provider (different domain) is happening and then a redirect back to your grafana domain. Currently, firefox will not allow a samesite cookie set to lax to persist in this way. https://bugzilla.mozilla.org/show_bug.cgi?id=1454027 which means grafana looses its oauth_state cookie if cookie_samesite is not set to none)

@seanlaff I tried your solution with AWS Cognito but it returns a header that does not allow to put it in an iframe(X-Frame-Options: deny), any tips on this?

Managed to do this with success using Grafana's auth.proxy and Nginx's ngx_http_secure_link_module
The links I use are in the format http://grafana/?user=nayar&md5=2tutcea9nfdsfdsfdsw&expires=1505386800
Once the user clicks on it, the session cookie is set and the user can browse grafana as if he logged in normally.
Advantages:

  • Link expires after some time + security
  • Link generates hash with userid, timestamp and passkey + security
  • No need to type in password + convenience

My nginx conf is like this

server {
    listen 3001 default_server;
#   listen [::]:3001 default_server;

    server_name _;

        location / {
        set $user "";
        set $state "";

        if ($args ~ "^user=(.+)&md5") {
                    set $user $1;
                    set $state "${state}U";
                }

        secure_link $arg_md5,$arg_expires;
                secure_link_md5 "$secure_link_expires$uri$user grafanarocks";

                if ($secure_link = "") { 
                    set $state "${state}S1";
                }

                if ($secure_link = "0") { 
                    set $state "${state}S2";
                }

                add_header X-uristate "$state";

                if ($state = "US1") { return 403; }
                if ($state = "US2") { return 410; }

                add_header X-uri "$user";

                proxy_set_header X-WEBAUTH-USER $user;

                proxy_pass http://127.0.0.1:3000;
            }
    }

Feel free to contact me if you need help with this

This solution doesn't work for me. Because I think each request should contain valid X-WEBAUTH-USER header. It doesn't work if you fill the header on the first request, get cookies and go with them.

I ended with the next solution:

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  client_max_body_size 10m;
  root /foo/public;

  location /grafana/ {
    auth_request /gauth;
    auth_request_set $user $upstream_http_user;

    proxy_set_header X-WEBAUTH-USER $user;
    proxy_pass http://localhost:4000/;
  }

  location / {
    try_files $uri @app;
  }

  location @app {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-SSL-Client-Cert $ssl_client_cert;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_redirect  off;
  }
}

So the solution uses auth_request nginx module. My app is responsible for access control (/gauth request) and returns username in User response header.

how to use with iframe?
i am new to Grafana and NGINX
so please share the maximum details now to modify

how to use with iframe?

@pgsekaran this solution is not for iframe, but for get grafana UI without explicit grafana login for users of your app. The app in this case is proxy which knows how to login to grafana.

Hi,
Thanks for quick reply. do you have any link with reference to add the Grafana with other app using iframe.i add Grafana to my app but i am not able to set the use management.
RegardsGuna
On Tuesday, June 23, 2020, 09:54:57 PM GMT+5:30, Konstantin Kolotyuk notifications@github.com wrote:

how to use with iframe?

@pgsekaran this solution is not for iframe, but for get grafana UI without explicit grafana login for users of your app. The app in this case is proxy which knows how to login to grafana.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.

Still is it not implemented? Seriously this is a good to have feature.

@pgsekaran i have a solution for iframe, which is not very secure because it uses username-based login and relies on usernames being not easily guessable. basicaly, the username is the token.
ive written about it here, not in great detail but eouhg to get you started
https://blog.yadamiel.com/tutorials/embed-and-authenticate-grafana-in-a-iframe

@pgsekaran i have a solution for iframe, which is not very secure because it uses username-based login and relies on usernames being not easily guessable. basicaly, the username is the token.
ive written about it here, not in great detail but eouhg to get you started
https://blog.yadamiel.com/tutorials/embed-and-authenticate-grafana-in-a-iframe

Hi,

I need to setup with NGINX and iframe with auto login

The exact problem was explained in https://github.com/grafana/grafana/issues/16319#issuecomment-483272921: The session id is not returned with the first response that contains the page "skeleton". Subsequent requests do not contain the auto login token, so they fail.

We use chromium in kiosk mode to display Grafana dashboards in various places. As the same Grafana instance is also available for users, we use auth.generic_oauth (auth.basic until 5.x) to login humans and auth.proxy to log in the kiosk-mode machines:

/usr/bin/chromium --app="https://server.localdomain/grafana/d/000000004/002-the-big-picture?orgId=1&refresh=5m&autologin=lHOrdypkhxzNYb2lRaIjbNPlOCZw9gWE"

As others have noted, this does not simply work. What would work is to first call the url http://prometheus.localdomain/grafana/login?autologin=lHOrdypkhxzNYb2lRaIjbNPlOCZw9gWE (which now sets the grafana_session cookie) and then redirect to the real dashboard. But... kiosk mode 🤷 and iframes 🤦‍♂️

Our final working solution is to combine the autologin query parameter with a custom cookie. Yes, you cannot log out via the GUI as the custom cookie is not deleted, but as this mechanism is only used on the kiosk-mode machines, there was no need for this.

So here is the nginx config for the Grafana Server:

# this maps tokens to grafana users
map $arg_autologin $autologin {
    lHOrdypkhxzNYb2lRaIjbNPlOCZw9gWE "display-1";
    default "";
}

server {
    listen 80;

    server_name server.localdomain;

    # add_header cannot be used in an "if"-context
    # so we set it to an empty string here as 
    # `add_header Set-Cookie "";` just removes the complete
    # header from the response
    set $setCookieHeader "";

    # when the autologin query param is not set, use
    # the value from the cookie named `grafana_autologin`
    if ($arg_autologin = "") {
        set $arg_autologin $cookie_grafana_autologin;
    }

    # when either the autologin query param or the `grafana_autologin`
    # cookie was set, place the autologin token and the cookie path
    # in the variable. `path=/` is needed to allow deeplinking
    if ($arg_autologin != "") {
        set $setCookieHeader "grafana_autologin=$arg_autologin;path=/";
    }

    location /grafana/ {
        rewrite  ^/grafana/(.*)  /$1 break;

        # now send the Set-Cookie header when an autologin token was provided
        add_header Set-Cookie $setCookieHeader;

        # look up the autologin token in the map above and set the grafana user
        proxy_set_header X-WEBAUTH-USER $autologin;
        proxy_pass http://localhost:3000;
    }
}
Was this page helpful?
1 / 5 - 1 ratings