Giter Club home page Giter Club logo

guard's Introduction

Build Status codecov Docker Pulls Twitter

Guard

Guard by AppsCode is a Kubernetes Webhook Authentication server. Using guard, you can log into your Kubernetes cluster using various auth providers. Guard also configures groups of authenticated user appropriately. This allows cluster administrator to setup RBAC rules based on membership in groups. Guard supports following auth providers:

Supported Versions

Kubernetes 1.9+

Installation

To install Guard, please follow the guide here.

Using Guard

Want to learn how to use Guard? Please start here.

Contribution guidelines

Want to help improve Guard? Please start here.

Acknowledgement

Support

We use Slack for public discussions. To chit chat with us or the rest of the community, join us in the AppsCode Slack team channel #guard. To sign up, use our Slack inviter.

If you have found a bug with Guard or want to request for new features, please file an issue.

guard's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

guard's Issues

multiple authentication

Guard's ability to select backends is very nice. But it only looks like one can be used at a time. Could the backend to be used be added to the token so that the auth server knows which plugin to choose for a particular token auth?

Azure AAD auth provider is using AAD group's displayName instead of unique objectId for auth

Currently guard updates AAD token of the user with display name of AAD groups the user belongs to. Several AAD groups in an organization can have the same display name. This can cause group claim based auth using Guard webhook to be unreliable.

Code here: https://github.com/appscode/guard/blob/master/auth/providers/azure/graph/graph.go#L212

Fix: Update Guard to use AAD group's unique id/object id objectId (which is guaranteed to be unique) instead of display name which might not be unique.

E.g. here: https://msdn.microsoft.com/en-us/library/azure/ad/graph/api/groups-operations

cc: @tamalsaha

Add paging to get around directoryObjects.getByIds limit of 1000

When trying to detailed description of AAD membership groups using API getbyids: https://github.com/microsoftgraph/microsoft-graph-docs/blob/master/api-reference/v1.0/api/directoryobject_getbyids.md if AAD member has more than 1000 group memerships AAD returns:
image

Therefore, guard needs to do page queries to get details of all groups the user belong to. But getbyids does not support query options like $top and $expand and returns error:
image

So in this case guard cannot rely on AAD to do paging and needs to do paging itself to fetch membership group details from AAD.

Hardcoded Oauth client/secret for google?

I was trying to something along the lines of #134, having setup guard for API control, and looking at oauth2 proxy for dashboard. I got the latter setup and realised this was authenticating from a different "client" than the API, and as such the bearer token wasn't being recognised.

Knowing guard works exclusively off the service account JSON for secrets, I went looking at the code to see how it creates a client secret from this, and was surprised to find a set of credentials hard-coded as constants in the google auth provider.

Am I missing something here, or is everyone using this auth provider making use of someone else's Oauth2 client details? (With the JSON only being used for the group API lookup)

https://github.com/appscode/guard/blob/0fd69458e855fa3cc590432acbdc310bde1a1708/auth/providers/google/google.go#L14-L21

Fix e2e tests

  • LDAP test has some race condition
------------------------------
I0621 00:00:00.232056   29604 installer_test.go:123] apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: null
  labels:
    app: guard
  name: test-guard-37x5pq
spec: {}
status: {}
---
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: null
  labels:
    app: guard
  name: guard
  namespace: test-guard-37x5pq
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  creationTimestamp: null
  labels:
    app: guard
  name: guard
  namespace: test-guard-37x5pq
rules:
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - list
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  creationTimestamp: null
  labels:
    app: guard
  name: guard
  namespace: test-guard-37x5pq
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: guard
subjects:
- kind: ServiceAccount
  name: guard
  namespace: test-guard-37x5pq
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: guard
  name: guard
  namespace: test-guard-37x5pq
spec:
  replicas: 1
  strategy: {}
  template:
    metadata:
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ""
      creationTimestamp: null
      labels:
        app: guard
    spec:
      containers:
      - args:
        - run
        - --v=3
        - --tls-ca-file=/etc/guard/pki/ca.crt
        - --tls-cert-file=/etc/guard/pki/tls.crt
        - --tls-private-key-file=/etc/guard/pki/tls.key
        - --auth-providers=azure
        - --azure.client-id=client_id
        - --azure.tenant-id=tenant_id
        - --azure.use-group-uid=false
        env:
        - name: AZURE_CLIENT_SECRET
          valueFrom:
            secretKeyRef:
              key: client-secret
              name: guard-azure-auth
        image: appscode/guard:canary
        name: guard
        ports:
        - containerPort: 8443
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8443
            scheme: HTTPS
          initialDelaySeconds: 30
        resources: {}
        volumeMounts:
        - mountPath: /etc/guard/pki
          name: guard-pki
        - mountPath: /etc/guard/auth/azure
          name: guard-azure-auth
      serviceAccountName: guard
      tolerations:
      - key: CriticalAddonsOnly
        operator: Exists
      volumes:
      - name: guard-pki
        secret:
          defaultMode: 365
          secretName: guard-pki
      - name: guard-azure-auth
        secret:
          defaultMode: 365
          secretName: guard-azure-auth
status: {}
---
apiVersion: v1
data:
  ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN1RENDQWFDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFOTVFzd0NRWURWUVFERXdKallUQWUKRncweE9EQTJNakV3TmpVMU1UTmFGdzB5T0RBMk1UZ3dOalUxTVROYU1BMHhDekFKQmdOVkJBTVRBbU5oTUlJQgpJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBNHNxSUZoRnQ1SSs2cnp4T2tDcmZxay9xCjZZcmpoZXpjaHBxVmIxR2tUOUc3VmxOVU5QQnVqbUhQMTRBSTRWcVZHRG1nbkZTSVJ6SWVuRExDU3kzT0x5Z3cKckIyVDlWV05hdUJrc0F2WkxMVUdSL2lnbVplRzNuZkdhc1JxRXE0T1U2NDhIQk5Dd2VJUjVqR2FGcXN3K0dVbwpHd1FRTDN3QURGb0RKREdDdExLVVV1S0ExRFI1UGIxTklSTmtDSStXcE1QdEk3UHlZanpPa2h6RjYzQ2RBTi9ECjNZMEgwZTB2bTlBQ1c1R0YrS25HOXM4czlwTU5XclhaNDNxTlgxV0ZiVUV3VG40U2dGMHNEZW03TnpLM21zdloKWVhDMUVublQxWlZFdUdoNEVoN1dPUTRBeVQvRDVYcnk1c0FMWDdyOE5qaXNFUit4Mnd2bi9FeVVobytNNXdJRApBUUFCb3lNd0lUQU9CZ05WSFE4QkFmOEVCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHCjl3MEJBUXNGQUFPQ0FRRUFRUjVNVVJlK2gySTVlUHc2WWhrVjZYdkNjVWROOFJ5bmpMc0dES3BqVWxIVGhoaC8KTEVJaWorLzV5ZmoyREViWlFaa0l2T1kyTW1XRE5KR0J1TlBDUnd3SmE4RGlmWlFwRzFIZ3JrbndzOGU3SzdINwpYYnNzdE1nYWtINStLTEsxcnFmMEJoSGZ2QXBGY20vVENVRmlZd20wVHJ6ZmxnUXJxZ1UwbEVTUGF5d09neW9zClBISVlSYThrUkRmT1VIUy9PdWh4R1RTMXVrajRvc2g2V1NpdmVndUNORTlUbUFzRnJkWnVXZ3M3STQzNE14SUUKeWtreHpjRTNycnNGV3UwQm9TUnRndzcxVHZLUVlCQXZHS24rOERNWlZFd2pOcWhHM05ROE94anJyTW9STEVJawoyUHZYOXp4eXdKdW1sZlVxMnhXdVVLaytrYnJWb2JLYVBUMlg0Zz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMyRENDQWNDZ0F3SUJBZ0lJZmVjc0NUL2NWOVV3RFFZSktvWklodmNOQVFFTEJRQXdEVEVMTUFrR0ExVUUKQXhNQ1kyRXdIaGNOTVRnd05qSXhNRFkxTlRFeldoY05NVGt3TmpJeE1EWTFPVFV3V2pBUk1ROHdEUVlEVlFRRApFd1p6WlhKMlpYSXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDbk5vblBhVzJECnc5K0VFdkJveUgvNi83L2JWeExmVFdncTdMWlVPZHZ5dnBaL3JmMFkveEZRbElKMFVKaTVqN2NGOHpyd2FBU3kKSGlNM0hlS2l0QTdIekhBY3hVRUJpTlc5Uk5ieHZJSnhuczUyQUJFZFB5MnlBNzBVMk9GbUtaMGlveDVGNVJWYwpzNk5IbjJzcEczWHQ4QkUzOU5DeWFNdm5MRlAvQUd6NFR3bTY2SWVQaEFVU3I2Z3dUQmYwTit0b2UyUzY5MjFjCjVzclFVekxEQWp0NU9XVVNFTUV6eFNmTURtckpJR3pzdWFrQUduNXZLTDdEaFAwVGV5M2s3YjFBeU14Mzl4N2sKQnloSnZUUm1CaWFiMnNlQVRNbmd6YTFJNkVJSnBXZDlwN01hUVNzSVBpMmt1M3dxVSt2YVpxd05QYnRIWTVvRApQci9zZEsvTWxXWWJBZ01CQUFHak9EQTJNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBUEJnTlZIUkVFQ0RBR2h3UUtZQXBnTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFDNHkwTlEKbzVUSDFndi9GemFjWnlvS0h5UURhcTNQUUM1Umw1YU96R3I3dmxSNkJxVTFEbXdLSzZnSnZENjVKVVZpT25ONAovYm5vMWt2VnYvUXZQQ015dFZDU0c5MHZJZEh0Z2h4M3dzU0ZmSnh0ZHNWcVc5TXBVNnZLNzZZU092REhxVGVnCmJxTDFTcmlaYTVCUEN1MTFXUWtVd0VVVC9ZMEEzVUJyaU1PaFdkRG5HZFNWN29Zdi81d2dBck9OVVJ1UjdSa0oKZ2JaYnM0UjBZSDQ1QllVZkcyNFNveHlyL3cxMU9GeHhtbDE2bE9TRjExTWZFTXpKMjZ6L1pCUmNYVkFoNmowYgpCcE5ZVXVvWVdxcWtWZmpwSEZicjNjdGs0QUlrQXE3K1ErMGhKcUNHUWU5a2JiL0pUczU4ekdiWGIza2RqRlZLCmNqTmJVL28zejhIZTliKysKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
  tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBcHphSnoybHRnOFBmaEJMd2FNaC8rdisvMjFjUzMwMW9LdXkyVkRuYjhyNldmNjM5CkdQOFJVSlNDZEZDWXVZKzNCZk02OEdnRXNoNGpOeDNpb3JRT3g4eHdITVZCQVlqVnZVVFc4YnlDY1o3T2RnQVIKSFQ4dHNnTzlGTmpoWmltZElxTWVSZVVWWExPalI1OXJLUnQxN2ZBUk4vVFFzbWpMNXl4VC93QnMrRThKdXVpSApqNFFGRXErb01Fd1g5RGZyYUh0a3V2ZHRYT2JLMEZNeXd3STdlVGxsRWhEQk04VW56QTVxeVNCczdMbXBBQnArCmJ5aSt3NFQ5RTNzdDVPMjlRTWpNZC9jZTVBY29TYjAwWmdZbW05ckhnRXpKNE0ydFNPaENDYVZuZmFlekdrRXIKQ0Q0dHBMdDhLbFByMm1hc0RUMjdSMk9hQXo2LzdIU3Z6SlZtR3dJREFRQUJBb0lCQUFNYzNNdmpwb3BCWnpQSwpObmQ4NUFVRUpjb3JnTXB2WW5VS0d3ck9XTzNYNW0xU0M5Z2ZSMjg4bElFZHp2b0ZWdDdoZkVObGJGRDBocXRMCi95QVFRZ1l1SkJnbEErVEhhMU11cU9HdkpLNkF5azhnQ1RHR0w0K1V5anhOSFVVTm1VTS96aHgyKzBMU2hVT2MKRUI2ZnlENDVyZGhnMGxHLzZhdlFBM0JIemIrQmtYTzh6UWlxc05Xb0Q1Q1BvQ0d4NWN4NUIvc3RkZTRwZXVPawpUUEJWOUJLVkxscmJ2eVR6eWh3eElKMUg5aEo0V1dtdytyNjBGeTNrME1Yc0dIVUdlOEkvSEh0dmlKdUNvanpCCmhvT3pya0ptb01hQ3F3STJncWJNZWFGSWFuWFQ0RUN4ay90M0wyTVBsNVYzaFU4TXpGaFFjNkpNN1J3YTk2Q1QKdFJub3R5RUNnWUVBeUM2MkV0cXgydzQ3aHFMbnNUcVRQR1BkdXJJZXBlY1h3V3ZNSE5SZUZvNThpTEs0dE9zZQplemZvc2hnRGp1L0JoVVhMamdYemdRb1NqK0huZTMxYVZkRjZoTHZMK28yQmFYcGFjVE0vT2wvVEpFbjNPUy9xCnFGeVBsVG4wMjVjZHJINkpYYUtsMFNyOVJWQlJxV2tBeTFVb1RYWGFNRHZxb0JEcmdaR1JKNDBDZ1lFQTFkWnQKREZIV0M3cG5TTjc2ZjZEZ1lpUHF5ZUxHRk1KUUV6cTdBTkNvUVZlVzVNSHlFU3hXU3pmNnZ1YTF3M1ZUcHhPZAp2dlRJa2hUdnhNQVZTOFQzSHhrWEpuZFd4MThUT2xKZmp1YkdndUV4eHhDdU40a2VibWZweXFpbWh6dXBlLzgzClBhWm1QY214NkpJRFd4VjNvWWpweXo5ZmtkbkhLN1E1N2hubXBrY0NnWUE2Q3FFc0VsRGpUTnNoakhaVlFzQ1MKRC9xb09TRDl4V1Z6NVllSERHUkRjZ2pWc2N3d1dNTzd3bGdyMng2ZEFSTkJTeDFjRG9tdDduZTNWQzdEOVhLTgowM0VNcjA3VFZOZ2k5eURtRlkvTm94OGJOSDhFQzhxa0NrSnZhQ0NBMHUzOXA2cTRoWUpDaFk1dDNIM3crc3NLCkthVEg0TlhraWxyYkVreldncGh0dVFLQmdHVmtwaEpZWDNqeUt2VTNuV21VKzdBdFBhLzZrbGZYaHlhSEM1S28KWFkrV0c1dGVod3d6akx0ZjBNcGlGTCtZUzFFR2tSNnhiTVV2bmowWm96OXBKcEFkVW9mUHdGc0tydEJSckxQUwpvei85elorSlBrSmhCd2ZzeDhOclVvTCsvQzhxWmFRYTJaSGtxRElSd2Z1WFkwRTU3VG1OVkNDUk5jcDRTNXVsCnRtTVRBb0dBQkkxNHpFOXp0VkRGQ0srV1VrK09xeGF2Sk1OUmVZeUFHSHppQjlGR1JNVjZpTDBPZFdZa2JhaGkKdFEzaXUxUVBpNTcvQXJ1Vlp1YTBEQzl1WTdyajZHeGZTVjFUeDdRSGc5S0cwM2xvVzNadGhnNWpSVWVYa2swSQo1bDJXS3JteGZYeDNCam5lYmpRd3hrSGg1Y3VudFZXSC9BSXVXUjROSCsxV1JwNDJuNE09Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
kind: Secret
metadata:
  creationTimestamp: null
  labels:
    app: guard
  name: guard-pki
  namespace: test-guard-37x5pq
---
apiVersion: v1
data:
  client-secret: Y2xpZW50X3NlY3JldA==
kind: Secret
metadata:
  creationTimestamp: null
  labels:
    app: guard
  name: guard-azure-auth
  namespace: test-guard-37x5pq
---
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    app: guard
  name: guard
  namespace: test-guard-37x5pq
spec:
  clusterIP: 10.96.10.96
  ports:
  - name: api
    port: 443
    protocol: TCP
    targetPort: 8443
  selector:
    app: guard
  type: ClusterIP
status:
  loadBalancer: {}
SIGQUIT: quit
PC=0x45d7f1 m=0 sigcode=0

goroutine 0 [idle]:
runtime.futex(0x1ff3e48, 0x0, 0x0, 0x0, 0x7fff00000000, 0x0, 0x0, 0x0, 0x7fff89e39c88, 0x410e9b, ...)
	/usr/local/go/src/runtime/sys_linux_amd64.s:530 +0x21
runtime.futexsleep(0x1ff3e48, 0x7fff00000000, 0xffffffffffffffff)
	/usr/local/go/src/runtime/os_linux.go:45 +0x4b
runtime.notesleep(0x1ff3e48)
	/usr/local/go/src/runtime/lock_futex.go:151 +0x9b
runtime.stoplockedm()
	/usr/local/go/src/runtime/proc.go:2101 +0x8c
runtime.schedule()
	/usr/local/go/src/runtime/proc.go:2493 +0x2da
runtime.park_m(0xc4204d0480)
	/usr/local/go/src/runtime/proc.go:2604 +0xb6
runtime.mcall(0x0)
	/usr/local/go/src/runtime/asm_amd64.s:351 +0x5b

goroutine 1 [chan receive, 9 minutes]:
testing.(*T).Run(0xc4208820f0, 0x14b6456, 0x7, 0x1560638, 0x47f346)
	/usr/local/go/src/testing/testing.go:825 +0x301
testing.runTests.func1(0xc420882000)
	/usr/local/go/src/testing/testing.go:1063 +0x64
testing.tRunner(0xc420882000, 0xc4207bfdf8)
	/usr/local/go/src/testing/testing.go:777 +0xd0
testing.runTests(0xc42051b3a0, 0x1fc47f0, 0x1, 0x1, 0x411eb9)
	/usr/local/go/src/testing/testing.go:1061 +0x2c4
testing.(*M).Run(0xc42056f200, 0x0)
	/usr/local/go/src/testing/testing.go:978 +0x171
main.main()
	_testmain.go:44 +0x151

goroutine 19 [runnable]:
github.com/appscode/guard/vendor/github.com/golang/glog.(*loggingT).flushDaemon(0x1ff2e80)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/golang/glog/glog.go:879 +0x8b
created by github.com/appscode/guard/vendor/github.com/golang/glog.init.0
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/golang/glog/glog.go:410 +0x203

goroutine 20 [syscall, 9 minutes]:
os/signal.signal_recv(0x0)
	/usr/local/go/src/runtime/sigqueue.go:139 +0xa6
os/signal.loop()
	/usr/local/go/src/os/signal/signal_unix.go:22 +0x22
created by os/signal.init.0
	/usr/local/go/src/os/signal/signal_unix.go:28 +0x41

goroutine 38 [chan receive, 9 minutes]:
github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/specrunner.(*SpecRunner).registerForInterrupts(0xc4205d57c0, 0xc420872300)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/specrunner/spec_runner.go:223 +0xd1
created by github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/specrunner.(*SpecRunner).Run
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/specrunner/spec_runner.go:60 +0x88

goroutine 37 [select]:
github.com/appscode/guard/vendor/github.com/onsi/gomega/internal/asyncassertion.(*AsyncAssertion).match(0xc42089f040, 0x160cfa0, 0x20114c8, 0x412701, 0x0, 0x0, 0x0, 0x20114c8)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go:139 +0x2e6
github.com/appscode/guard/vendor/github.com/onsi/gomega/internal/asyncassertion.(*AsyncAssertion).Should(0xc42089f040, 0x160cfa0, 0x20114c8, 0x0, 0x0, 0x0, 0xc42089f040)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go:48 +0x62
github.com/appscode/guard/test/e2e_test.glob..func3.3()
	/home/tamal/go/src/github.com/appscode/guard/test/e2e/installer_test.go:152 +0x154
github.com/appscode/guard/test/e2e_test.glob..func3.16.6.3()
	/home/tamal/go/src/github.com/appscode/guard/test/e2e/installer_test.go:402 +0xed
github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/leafnodes.(*runner).runSync(0xc4200a79e0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.go:110 +0x9c
github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/leafnodes.(*runner).run(0xc4200a79e0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.go:64 +0x13e
github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/leafnodes.(*ItNode).Run(0xc4203c37a0, 0x15f68c0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/leafnodes/it_node.go:26 +0x7f
github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/spec.(*Spec).runSample(0xc420882780, 0x0, 0x15f68c0, 0xc420408e40)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/spec/spec.go:203 +0x648
github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/spec.(*Spec).Run(0xc420882780, 0x15f68c0, 0xc420408e40)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/spec/spec.go:138 +0xff
github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/specrunner.(*SpecRunner).runSpec(0xc4205d57c0, 0xc420882780, 0x0)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/specrunner/spec_runner.go:200 +0x10d
github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/specrunner.(*SpecRunner).runSpecs(0xc4205d57c0, 0x1)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/specrunner/spec_runner.go:170 +0x329
github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/specrunner.(*SpecRunner).Run(0xc4205d57c0, 0x9)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/specrunner/spec_runner.go:66 +0x11b
github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/suite.(*Suite).Run(0xc420402cd0, 0x7f0633fa2c90, 0xc4208820f0, 0x14b94a0, 0x9, 0xc42051b420, 0x2, 0x2, 0x161d3a0, 0xc420408e40, ...)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/internal/suite/suite.go:62 +0x27c
github.com/appscode/guard/vendor/github.com/onsi/ginkgo.RunSpecsWithCustomReporters(0x15f7d40, 0xc4208820f0, 0x14b94a0, 0x9, 0xc42051b3c0, 0x2, 0x2, 0x2)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go:220 +0x258
github.com/appscode/guard/vendor/github.com/onsi/ginkgo.RunSpecsWithDefaultAndCustomReporters(0x15f7d40, 0xc4208820f0, 0x14b94a0, 0x9, 0xc420125788, 0x1, 0x1, 0x5b2b4bd0)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go:208 +0xab
github.com/appscode/guard/test/e2e_test.TestE2e(0xc4208820f0)
	/home/tamal/go/src/github.com/appscode/guard/test/e2e/e2e_suite_test.go:29 +0xed
testing.tRunner(0xc4208820f0, 0x1560638)
	/usr/local/go/src/testing/testing.go:777 +0xd0
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:824 +0x2e0

goroutine 39 [select, 9 minutes, locked to thread]:
runtime.gopark(0x1562a50, 0x0, 0x14b59f8, 0x6, 0x18, 0x1)
	/usr/local/go/src/runtime/proc.go:291 +0x11a
runtime.selectgo(0xc420125750, 0xc4208723c0)
	/usr/local/go/src/runtime/select.go:392 +0xe50
runtime.ensureSigM.func1()
	/usr/local/go/src/runtime/signal_unix.go:549 +0x1f4
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:2361 +0x1

goroutine 16 [IO wait]:
internal/poll.runtime_pollWait(0x7f0634035f00, 0x72, 0xc42099d850)
	/usr/local/go/src/runtime/netpoll.go:173 +0x57
internal/poll.(*pollDesc).wait(0xc420658918, 0x72, 0xffffffffffffff00, 0x15f91c0, 0x1eeb820)
	/usr/local/go/src/internal/poll/fd_poll_runtime.go:85 +0x9b
internal/poll.(*pollDesc).waitRead(0xc420658918, 0xc420a8a000, 0x4000, 0x4000)
	/usr/local/go/src/internal/poll/fd_poll_runtime.go:90 +0x3d
internal/poll.(*FD).Read(0xc420658900, 0xc420a8a000, 0x4000, 0x4000, 0x0, 0x0, 0x0)
	/usr/local/go/src/internal/poll/fd_unix.go:157 +0x17d
net.(*netFD).Read(0xc420658900, 0xc420a8a000, 0x4000, 0x4000, 0x8, 0x8, 0x3ff3)
	/usr/local/go/src/net/fd_unix.go:202 +0x4f
net.(*conn).Read(0xc420524000, 0xc420a8a000, 0x4000, 0x4000, 0x0, 0x0, 0x0)
	/usr/local/go/src/net/net.go:176 +0x6a
crypto/tls.(*block).readFromUntil(0xc42063af30, 0x7f0633f6e000, 0xc420524000, 0x5, 0xc420524000, 0x0)
	/usr/local/go/src/crypto/tls/conn.go:493 +0x96
crypto/tls.(*Conn).readRecord(0xc420998000, 0x1562b17, 0xc420998120, 0x0)
	/usr/local/go/src/crypto/tls/conn.go:595 +0xe0
crypto/tls.(*Conn).Read(0xc420998000, 0xc420881000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/go/src/crypto/tls/conn.go:1156 +0x100
bufio.(*Reader).Read(0xc4200a6480, 0xc4209102d8, 0x9, 0x9, 0xc420924ef8, 0xc420a51460, 0xc42099dd10)
	/usr/local/go/src/bufio/bufio.go:216 +0x238
io.ReadAtLeast(0x15f4d00, 0xc4200a6480, 0xc4209102d8, 0x9, 0x9, 0x9, 0xc42099dce0, 0xc42099dce0, 0x406614)
	/usr/local/go/src/io/io.go:309 +0x86
io.ReadFull(0x15f4d00, 0xc4200a6480, 0xc4209102d8, 0x9, 0x9, 0xc420924ea0, 0xc42099dd10, 0xc400000101)
	/usr/local/go/src/io/io.go:327 +0x58
github.com/appscode/guard/vendor/golang.org/x/net/http2.readFrameHeader(0xc4209102d8, 0x9, 0x9, 0x15f4d00, 0xc4200a6480, 0x0, 0xc400000000, 0x91389d, 0xc42099dfb0)
	/home/tamal/go/src/github.com/appscode/guard/vendor/golang.org/x/net/http2/frame.go:237 +0x7b
github.com/appscode/guard/vendor/golang.org/x/net/http2.(*Framer).ReadFrame(0xc4209102a0, 0xc420a89230, 0x0, 0x0, 0x0)
	/home/tamal/go/src/github.com/appscode/guard/vendor/golang.org/x/net/http2/frame.go:492 +0xa4
github.com/appscode/guard/vendor/golang.org/x/net/http2.(*clientConnReadLoop).run(0xc42099dfb0, 0x1561520, 0xc4201247b0)
	/home/tamal/go/src/github.com/appscode/guard/vendor/golang.org/x/net/http2/transport.go:1428 +0x8e
github.com/appscode/guard/vendor/golang.org/x/net/http2.(*ClientConn).readLoop(0xc4200e8820)
	/home/tamal/go/src/github.com/appscode/guard/vendor/golang.org/x/net/http2/transport.go:1354 +0x76
created by github.com/appscode/guard/vendor/golang.org/x/net/http2.(*Transport).newClientConn
	/home/tamal/go/src/github.com/appscode/guard/vendor/golang.org/x/net/http2/transport.go:579 +0x651

rax    0xca
rbx    0x1ff3d00
rcx    0x45d7f3
rdx    0x0
rdi    0x1ff3e48
rsi    0x0
rbp    0x7fff89e39c50
rsp    0x7fff89e39c08
r8     0x0
r9     0x0
r10    0x0
r11    0x286
r12    0xc420125768
r13    0xff
r14    0xff
r15    0xf
rip    0x45d7f1
rflags 0x286
cs     0x33
fs     0x0
gs     0x0
*** Test killed with quit: ran too long (10m0s).
FAIL	github.com/appscode/guard/test/e2e	600.009s
?   	github.com/appscode/guard/test/e2e/framework	[no test files]
?   	github.com/appscode/guard/test/e2e/matcher	[no test files]
?   	github.com/appscode/guard/util/kubeconfig	[no test files]

Error: guard does not provide service for github

I have configured Guard according to the documentation to work with kops.

When I now make a request with the Bearer token configured for kubectl, this is what I see in the guard logs. What's going wrong there? I assume that github or GitHub or Github is matched directly and some part is doing the wrong thing, can't tell which though.

I0419 22:16:27.629481       1 handler.go:31] Received token review request for github/my-cool-org
E0419 22:16:27.629935       1 utils.go:65] [guard does not provide service for github]
I0419 22:16:27.630271       1 utils.go:69] Stacktrace: guard does not provide service for github%!(EXTRA errors.StackTrace=[handler.go:41 <autogenerated>:1 instrument_server.go:196 server.go:1947 instrument_server.go:100 server.go:1947 instrument_server.go:76 server.go:1947 instrument_server.go:40 server.go:1947 mux.go:129 server.go:2694 server.go:3260 <autogenerated>:1 h2_bundle.go:5475 h2_bundle.go:5760 asm_amd64.s:2361])

Enable guard to use OAuth 2.0 On-Behalf-Of flow to obtain user's group memberships

Currently guard uses the Application Identity of Web API AAD application to get AAD user's group memberships: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-scenarios#web-application-to-web-api

The caveat of using this flow is that it requires the application to have certain kinds of read permissions for all users profile in AAD. This is an elevated access scenario and some tenant admins (and organizations) don't allow applications to have this level of access without user's consent.

A safer/more acceptable model is to use OAuth 2.0 On-Behalf-Of flow: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols-oauth-on-behalf-of#protocol-diagram.

In this scenario Guard to request group memberships from AAD on behalf of the user using it's application identity and user's token in exchange of token returned by AAD.

This issue is a feature request to add On-Behalf-Of flow in Guard to obtain group memberships of the user.

Fix installer

  • Don't pass password as flag in installer
  • Refactor installer to more manageable structure.

Document how to fix bad certificate issue

I0830 16:41:59.919947       1 logs.go:19] FLAG: --alsologtostderr="false"
I0830 16:41:59.919987       1 logs.go:19] FLAG: --analytics="true"
I0830 16:41:59.920016       1 logs.go:19] FLAG: --ca-cert-file="/srv/guard/pki/ca.crt"
I0830 16:41:59.920026       1 logs.go:19] FLAG: --cert-file="/srv/guard/pki/tls.crt"
I0830 16:41:59.920032       1 logs.go:19] FLAG: --help="false"
I0830 16:41:59.920039       1 logs.go:19] FLAG: --key-file="/srv/guard/pki/tls.key"
I0830 16:41:59.920067       1 logs.go:19] FLAG: --log_backtrace_at=":0"
I0830 16:41:59.920075       1 logs.go:19] FLAG: --log_dir=""
I0830 16:41:59.920080       1 logs.go:19] FLAG: --logtostderr="true"
I0830 16:41:59.920086       1 logs.go:19] FLAG: --ops-addr=":56790"
I0830 16:41:59.920091       1 logs.go:19] FLAG: --stderrthreshold="2"
I0830 16:41:59.920098       1 logs.go:19] FLAG: --v="3"
I0830 16:41:59.920105       1 logs.go:19] FLAG: --vmodule=""
I0830 16:41:59.920112       1 logs.go:19] FLAG: --web-address=":9844"
I0830 16:42:45.430823       1 logs.go:19] http: TLS handshake error from 1.1.2.6:34028: remote error: tls: bad certificate
I0830 16:43:00.483658       1 logs.go:19] http: TLS handshake error from 1.1.2.6:34062: remote error: tls: bad certificate
^Cadmin@ip-172-20-48-207:~$ sudo tail -f /var/log/kube-apiserver.log | grep auth
E0830 16:56:46.430468       6 authentication.go:58] Unable to authenticate the request due to an error: [invalid bearer token, [invalid bearer token, [invalid bearer token, invalid bearer token, Post https://10.7.3.3:9844/apis/authentication.k8s.io/v1beta1/tokenreviews: x509: certificate is valid for 1.27.55.255, not 10.7.3.3]]]
  1. First we need to check what IP is in the webhook config file. That is the IP used by api server to connect to the guard server.
  2. We need to check the CN, SANS in the server.crt. If that is not matching, we need to regenerate the server certificates.
    $ guard init server --ips=<ip1>,<ip2> --domains=<dns1>,<dns2>
    Then regenerate the installer.yaml . This includes a Secret called guard-pki which is used to mount the server.crt, etc.
    Then kubectl apply the new installer.yaml. This should restart the guard server with the updated cert and fix the bad certificate issue.

passwords not in tokens

It would be good if guard could provide a token mechanism that didn't involve storing usernames/passwords in files. This is unacceptable to some sites security policies.

Is the google auth actually working/tested?

I am trying to set up the google auth for this, and am running into a few problems:

  1. The google token is an access token that expire. Am I expected to update my token every time it expires? or am I doing something wrong? Which token do i copy and paste into my kube/config?

  2. the admin.directory.group scope only works for people who have admin permissions in g suite. If a normal user (non-admin) tries to use this, the groups api call wont work.

Am I doing this wrong or something? Does anyone have google auth working?

NTP sync causing periodic crashes

NTP sync was introduced 0.1.0 via #83

Since upgrading to 0.1.0+ (including latest version) we see periodically the following:

F0529 18:25:05.766404 1 server.go:44] read udp [--IP REDACTED--]->128.138.141.172:123: i/o timeout goroutine 27 [running]: github.com/appscode/guard/vendor/github.com/golang/glog.stacks(0xc420339900, 0xc42019a320, 0x69, 0xa0) /go/src/github.com/appscode/guard/vendor/github.com/golang/glog/glog.go:766 +0xcf github.com/appscode/guard/vendor/github.com/golang/glog.(*loggingT).output(0x22972e0, 0xc400000003, 0xc4200ad4a0, 0x21e0a82, 0x9, 0x2c, 0x0) /go/src/github.com/appscode/guard/vendor/github.com/golang/glog/glog.go:717 +0x322 github.com/appscode/guard/vendor/github.com/golang/glog.(*loggingT).printDepth(0x22972e0, 0xc400000003, 0x1, 0xc420b04f98, 0x1, 0x1) /go/src/github.com/appscode/guard/vendor/github.com/golang/glog/glog.go:646 +0x12a github.com/appscode/guard/vendor/github.com/golang/glog.(*loggingT).print(0x22972e0, 0xc400000003, 0xc420b04f98, 0x1, 0x1) /go/src/github.com/appscode/guard/vendor/github.com/golang/glog/glog.go:637 +0x5a github.com/appscode/guard/vendor/github.com/golang/glog.Fatal(0xc420b04f98, 0x1, 0x1) /go/src/github.com/appscode/guard/vendor/github.com/golang/glog/glog.go:1125 +0x53 github.com/appscode/guard/server.Server.ListenAndServe.func1(0xc420526480, 0xc420366900) /go/src/github.com/appscode/guard/server/server.go:44 +0xf1 created by github.com/appscode/guard/server.Server.ListenAndServe /go/src/github.com/appscode/guard/server/server.go:41 +0xe1b

I note via host 128.138.141.172 this is utcnist2.colorado.edu.

I see the default for clock-check-interval is five minutes. Our errors are not that frequent, not are they regular, so I don't think this can be put down to an overly restrictive security group on our part. It happens approximately 10-20 times per week.

Ideally - i/o timeouts should be dealt with, rather than any error here causing a fatal exception.

Alternatively - a simple way to turn this off should be provided. Best I can see right now is to make clock-check-interval big.

panic: assignment to entry in nil map

Hi folks!

I use guard v0.2.0 and I try to integrate it with our LDAP server (I did everything according to the official guide), however when I try to obtain a token I've got the error:

$ guard get token -o ldap --ldap.auth-choice 0 --ldap.username=evgeny.shmarnev --ldap.password=foobar -v=3 --analytics=false
I0711 10:31:22.971536    9002 logs.go:19] FLAG: --alsologtostderr="false"
I0711 10:31:22.973452    9002 logs.go:19] FLAG: --analytics="false"
I0711 10:31:22.973482    9002 logs.go:19] FLAG: --help="false"
I0711 10:31:22.973499    9002 logs.go:19] FLAG: --ldap.auth-choice="0"
I0711 10:31:22.973513    9002 logs.go:19] FLAG: --ldap.disable-pa-fx-fast="true"
I0711 10:31:22.973527    9002 logs.go:19] FLAG: --ldap.krb5-config="/etc/krb5.conf"
I0711 10:31:22.973538    9002 logs.go:19] FLAG: --ldap.password="foobar"
I0711 10:31:22.973549    9002 logs.go:19] FLAG: --ldap.realm=""
I0711 10:31:22.973559    9002 logs.go:19] FLAG: --ldap.spn=""
I0711 10:31:22.973570    9002 logs.go:19] FLAG: --ldap.username="evgeny.shmarnev"
I0711 10:31:22.973586    9002 logs.go:19] FLAG: --log_backtrace_at=":0"
I0711 10:31:22.973599    9002 logs.go:19] FLAG: --log_dir=""
I0711 10:31:22.973612    9002 logs.go:19] FLAG: --logtostderr="false"
I0711 10:31:22.973623    9002 logs.go:19] FLAG: --organization="ldap"
I0711 10:31:22.973636    9002 logs.go:19] FLAG: --stderrthreshold="0"
I0711 10:31:22.973647    9002 logs.go:19] FLAG: --v="3"
I0711 10:31:22.973660    9002 logs.go:19] FLAG: --vmodule=""
panic: assignment to entry in nil map

goroutine 1 [running]:
github.com/appscode/guard/util/kubeconfig.AddAuthInfo(0x7fffaea93203, 0xf, 0xc42063c000, 0x6e61443530333237, 0x6c6969)
	/home/tamal/go/src/github.com/appscode/guard/util/kubeconfig/utils.go:41 +0x10b
github.com/appscode/guard/auth/providers/ldap.(*TokenOptions).addAuthInfo(0xc4203170b0, 0xc420043bf0, 0x24, 0xc4203e4dc0, 0xc4205ec630)
	/home/tamal/go/src/github.com/appscode/guard/auth/providers/ldap/kubectl.go:148 +0x75
github.com/appscode/guard/auth/providers/ldap.(*TokenOptions).IssueToken(0xc4203170b0, 0x4, 0x7fffaea931d9)
	/home/tamal/go/src/github.com/appscode/guard/auth/providers/ldap/kubectl.go:76 +0x94
github.com/appscode/guard/commands.NewCmdGetToken.func1(0xc4205f2780, 0xc4200de700, 0x0, 0x8)
	/home/tamal/go/src/github.com/appscode/guard/commands/token.go:51 +0x152
github.com/appscode/guard/vendor/github.com/spf13/cobra.(*Command).execute(0xc4205f2780, 0xc4200de400, 0x8, 0x8, 0xc4205f2780, 0xc4200de400)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/spf13/cobra/command.go:766 +0x2c1
github.com/appscode/guard/vendor/github.com/spf13/cobra.(*Command).ExecuteC(0xc4201b7400, 0xc4205f2f00, 0xc4205f2c80, 0xc4205a0570)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/spf13/cobra/command.go:852 +0x30a
github.com/appscode/guard/vendor/github.com/spf13/cobra.(*Command).Execute(0xc4201b7400, 0x1841c00, 0xc42004c118)
	/home/tamal/go/src/github.com/appscode/guard/vendor/github.com/spf13/cobra/command.go:800 +0x2b
main.main()
	/home/tamal/go/src/github.com/appscode/guard/main.go:14 +0x48

My setup:

  • kubernetes v1.9.7 (deployed via kubeadm)
  • guard v0.2.0 - deployed inside of kubernetes cluster
$ kubectl get po -n kube-system | grep guard
guard-5f678c4ddd-xh9jk                                          1/1       Running   0          57m

If you need any additional info, feel free to ask me.

Don't query for groups if AAD token already contains groups

Issue:
If a user is member of more groups than the overage limit, then Azure AD does not emit groups claim in the token. Instead, it includes an overage claim in the token that indicates to the application to query Graph API to retrieve the user’s group membership.

However, for users that don't belong to a lot of AAD groups those groups are returned in the token. In that case Guard does not need to make a second call to fetch groups and instead can use groups already returned in the token.

E.g.:

{
  "aud": "spn:12f61069-cba6-4cc2-dae8-8a7b8ccf41c4",
  "iss": "https://sts.windows.net/<ID_OF_TENANT_T2>/",
  "iat": 1530828265,
  "nbf": 1530828265,
  "exp": 1530832165,
  "acr": "1",
  "aio": "AWQAm/8HAAAASW3Mtsad0xxTH9GT5Cg5Z3OyEoSFjuhrP4OKeZO",
  "amr": [
    "pwd"
  ],
  "appid": "aa2z65ee-9ad4-4645-ab40-95f461599z37",
  "appidacr": "0",
  "email": "<redacted>",
  "family_name": "<redacted>",
  "given_name": "<redacted>",
  "groups": [
    "2a9de113-37eb-4b6c-8de7-7f734146f19b",
    "63ac0747-cd2a-4d31-b38f-9a43bde872b4",
  ],
  "idp": "https://sts.windows.net/<ID_OF_TENANT_T1>/",
  "ipaddr": "31.235.137.123",
  "name": "<redacted>",
  "oid": "<redacted>",
  "scp": "user_impersonation",
  "unique_name": "<redacted>",
  "ver": "1.0"
}

Fix:
Validate to check that groups are present in the token. If they are don't make a second call to Graph for fetching groups.

Support B2B auth for Azure provider by supporting both `oid` or `upn` claims in the token

Scenario:
User's identity can exist in their organization's AAD tenant (T1) and their AAD application + groups can live in another tenant (T2).

In this case during device login flow AAD returns a token that's missing UPN: https://docs.microsoft.com/en-us/azure/active-directory/b2b/claims-mapping probably due to:

Exert from article above:

For B2B collaboration users, mapping NameID and UPN cross-tenant are prevented for security reasons.

Example of one such token:

{
  "aud": "spn:12f61069-cba6-4cc2-dae8-8a7b8ccf41c4",
  "iss": "https://sts.windows.net/<ID_OF_TENANT_T2>/",
  "iat": 1530828265,
  "nbf": 1530828265,
  "exp": 1530832165,
  "acr": "1",
  "aio": "AWQAm/8HAAAASW3Mtsad0xxTH9GT5Cg5Z3OyEoSFjuhrP4OKeZO",
  "amr": [
    "pwd"
  ],
  "appid": "aa2z65ee-9ad4-4645-ab40-95f461599z37",
  "appidacr": "0",
  "email": "<redacted>",
  "family_name": "<redacted>",
  "given_name": "<redacted>",
  "groups": [
    "2a9de113-37eb-4b6c-8de7-7f734146f19b",
    "63ac0747-cd2a-4d31-b38f-9a43bde872b4",
  ],
  "idp": "https://sts.windows.net/<ID_OF_TENANT_T1>/",
  "ipaddr": "31.235.137.123",
  "name": "<redacted>",
  "oid": "<redacted>",
  "scp": "user_impersonation",
  "unique_name": "<redacted>",
  "ver": "1.0"
}

Issue:
Currently Guard relies on upn to fetch user's group. However, this will only work if the user and the application is in the same AAD tenant. This is because for B2B collaboration users, mapping NameID and UPN cross-tenant are prevented for security reasons.

Fix:
Overall oid is a much more reliable way to query user's properties and works cross tenant. Guard can continue using upn but provide an option to be setup using upn or fallback to using oid if upn is not present in the token.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.