I'm building an app for Microsoft Teams which requires the user to sign-in inside a bot dialog or message extension. The overall sign-in process works fine. I'm retrieving the token via BotFrameworkAdapter.getUserToken() and if no token is available, the user will be prompted to login based on BotFrameworkAdapter.getSignInLink().
Usually, the token is a JWT. This allows me to decode the claims and verify that it contains all scopes that are currently necessary to use the app. If I'm adding a Graph API permission in the future (e.g. for a new feature), I can therefore prompt the user to sign-in again so that the app doesn't break for users who are already signed in with lesser permissions.
Since recently, tokens are increasingly not issued as plain JWT. Rather, they look similar to an example from the Graph API documentation:
EwAoA8l6BAAU7p9QDpi/D7xJLwsTgCg3TskyTaQAAXu71AU9f4aS4rOK5xoO/SU5HZKSXtCsDe0Pj7uSc5Ug008qTI+a9M1tBeKoTs7tHzhJNSKgk7pm5e8d3oGWXX5shyOG3cKSqgfwuNDnmmPDNDivwmi9kmKqWIC9OQRf8InpYXH7NdUYNwN+jljffvNTewdZz42VPrvqoMH7hSxiG7A1h8leOv4F3Ek/oeJX6U8nnL9nJ5pHLVuPWD0aNnTPTJD8Y4oQTp5zLhDIIfaJCaGcQperULVF7K6yX8MhHxIBwek418rKIp11om0SWBXOYSGOM0rNNN59qNiKwLNK+MPUf7ObcRBN5I5vg8jB7IMoz66jrNmT2uiWCyI8MmYDZgAACPoaZ9REyqke+AE1/x1ZX0w7OamUexKF8YGZiw+cDpT/BP1GsONnwI4a8M7HsBtDgZPRd6/Hfqlq3HE2xLuhYX8bAc1MUr0gP9KuH6HDQNlIV4KaRZWxyRo1wmKHOF5G5wTHrtxg8tnXylMc1PKOtaXIU4JJZ1l4x/7FwhPmg9M86PBPWr5zwUj2CVXC7wWlL/6M89Mlh8yXESMO3AIuAmEMKjqauPrgi9hAdI2oqnLZWCRL9gcHBida1y0DTXQhcwMv1ORrk65VFHtVgYAegrxu3NDoJiDyVaPZxDwTYRGjPII3va8GALAMVy5xou2ikzRvJjW7Gm3XoaqJCTCExN4m5i/Dqc81Gr4uT7OaeypYTUjnwCh7aMhsOTDJehefzjXhlkn//2eik+NivKx/BTJBEdT6MR97Wh/ns/VcK7QTmbjwbU2cwLngT7Ylq+uzhx54R9JMaSLhnw+/nIrcVkG77Hi3neShKeZmnl5DC9PuwIbtNvVge3Q+V0ws2zsL3z7ndz4tTMYFdvR/XbrnbEErTDLWrV6Lc3JHQMs0bYUyTBg5dThwCiuZ1evaT6BlMMLuSCVxdBGzXTBcvGwihFzZbyNoX+52DS5x+RbIEvd6KWOpQ6Ni+1GAawHDdNUiQTQFXRxLSHfc9fh7hE4qcD7PqHGsykYj7A0XqHCjbKKgWSkcAg==
Although this looks like Base64-encoded data and it even says jwt above the snippet in the documentation, this is clearly not a plain JWT. Honestly, I'm not quite sure what it is, however, I assume it is a JWS or a JWT encoded with a nonce.
I'm thinking that I should probably treat this token as opaque, however, because the sign-in (and sign-in link generation) is hidden behind the bot framework I don't see another opportunity for adding permissions to an already deployed application, i.a. verifying that an access token provides the required permissions. Since my app also acts on behalf of the user at a later point in time, it's also not viable to prompt for sign-in after I'm receiving a 403er response from the Graph API.
Is there a way to decode these kinds of tokens or instead make sure that my app will only receive decodable JWTs?
If not, is there an alternative for verifying access token permissions (scopes)?
Related
I'm in the process of converting an internal C# API that uses a legacy SOBO integration to one that uses OAuth 2.0 as required by DocuSign. The legacy application makes all requests to DocuSign using DocuSign credentials of an application user (a non-person) and injects SOBO data (email address) in the appropriate header when we need to generate an embedded sending URI and have it appear that the envelope was sent by a real person without using their credentials.
I have successfully converted another (non-SOBO) internal API using JWT grant, but I do not know how replicate the SOBO dependent workflow. Have read Matt King's article "From the Trenches: OAuth 2.0 and the new SOBO", but still a bit confused. I know how to extract userId from email address as mentioned in the article, but not sure what do with it. Similar to my legacy application, I am using a application user to get consent and ask for JWT token without prompting internal users for their consent. Is this still possible using OAuth 2.0?
Happy to help here. Once you've retrieved the user's apiUserName / userId you'll want to add it into the JWT assertion's sub line.
When you go to generate the token, if consent has not been provided by or for the user, our system will send back an error of "Consent_Required." See https://www.docusign.com/blog/developers/oauth-jwt-granting-consent for different methods of granting consent.
For individual consent, the user needs to go to a specific URL containing your clientId, redirectUri, and some additional scopes. If they hadn't previously consented to the scopes in your OAuth request they will be prompted to do this once. You can confirm consent has been applied by having them visit the same link afterwards, which will send them directly to your redirectUri.
Domain consent works a little differently -- you need to have an organization, a claimed domain, and the users you're providing consent on behalf of need to have an email address that is part of that claimed domain.
Happy to connect and walk you through it -- if you can open a ticket at support.docusign.com and in the details request they reach out to me I should be able to have the case transferred and work with you from there.
Regards,
Matt King
Currently I have this setup:
At login, and in every subsequent request after login, a mobile application that I have built uses Basic Authentication to authenticate the user with a web service that serves the app with information it requests.
On every request the Authorization header is inspected, the password and username are extracted from the header, the password is hashed using a proprietary DLL (so the web service doesn't actually contain the hashing algorithm) and compared to the hashed password associated with the username that is stored in the database.
I have now been asked to include Azure AD SSO in the login options.
After reading much about the topic, this looks seems to me like the setup:
I'm curious about a few things:
Is this setup correct, more or less?
Does the app send the Identity Token to the web service? If so, how does the webservice validate that token?
Is it correct that the webservice can match the Azure Identity to the DB user using one of the claims in the Security Token?
Where do Access Token fit in this picture?
Thanks for the help!
(Side Note: I know that Basic Authentication is not the preferred way to go in the first scenario. This was a temporary decision till we developed the token handling code, it only works using HTTPS and this is an internal application - you wouldn't be able to activate the app unless you have a code we give you)
I have little experience in azure ad but I think we could talk about your case.
First, whatever id token and access token are both jwt token, so to your web service application, you need to use jwt decode library to decrypt the token and get claims it contains. Here we need to know the difference between id token and access token, and I think you'll know that for your web service application, if it's more likely to play the role of an api application, you need to use access token because this token also contains user information. Then you need to add code in your program to decode the token and check if it's a 'valid' token for the request.(Because you've used azure ad to achieve the login part, you don't need to use your custom login part.)
Next, the signing in feature provided by azure ad requires to use account and password in the tenant which azure ad belongs to, the user accounts may look like xx#xx.onmicrosoft.com, it doesn't required to keep sycn with the accounts in your database, so it's difficult and needless for you to compare the user name obtained from the decoded token with those in your database. Because when your web service received the token(id or access token), that means someone has passed the authentication from azure ad. The token contains user information including role, expired time etc. You need to check if the token has expired and if has the correct scope. (Let's see a common situation, microsoft provides many graph apis to call, when accessing these api, we need to provide access token in request head with matching scope, e.g. https://graph.microsoft.com/v1.0/me
requires a delegated api permission of User.Read)
To sum up here, if your web service just required the users in your database to sign in then can be access, id token and access token are both suitable for you because they both contains user name like 'xx#xx.onmicrosoft.com', what you need to do is decode the token and check if the token has expired and whether this user exists in your database(you may set up a mapping between them).
Is there any way that a third-party app can logically use Touch ID to authenticate to a web service that uses OAuth2?
Say I own a web service that requires authentication using OAuth2. It supports both the implicit and authorization-code grants (although I could add support for other grants if necessary).
A third party has a mobile app that uses this web service. It opens a native web view to authenticate, and loads my auth URL in it. The user enters their username/password on my domain, and I return an OAuth token back to the app.
If this app wants to implement Touch ID to speed up authentication, is there a way to do it that makes sense with OAuth2?
My understanding is that the purpose of the OAuth2 implicit and auth-code grants is to prevent the parent app from having access to the user's credentials. It only gets access to the resulting OAuth token, and that's only valid for a limited time.
With Touch ID, you would typically store the password using Keychain Services. So this obviously requires you to have access to the password.
I suppose they could store the OAuth token in the keychain instead of the password, but then this would only be valid for a short time.
The only answer I've come up with so far is what you allude to at the end: store the OAuth tokens -- but also a long-lived refresh token. How long that refresh token can live is definitely dependent on your specific security needs.
I don't know about any standard flow yet but here are some common considerations. Simply storing long-term credentials (passwords or refresh tokens, even encrypted at rest) would be mixing up security contexts in a way that is hard to audit. When using any local authentication (app-specific unlock PIN, any biometrics, or simply system unlock) it's important to do it in a way that can be verified by the server. So the first step would be device authentication, every instance of your app should use unique client id/client credentials (I suggest to implement Dynamic Client Registration Protocol to help with that but there could be other options). Then, it's a good idea to generate some piece of verifiable key information directly on the device, put it into secure storage (protected by whatever local unlocking mechanism and invalidated whenever biometrics changes or) and use it to generate a MAC of some kind, for example a JWT as a part of jwt-bearer flow (or some new extension to OAuth assertion framework). JWT tokens could include additional metadata (claims) that can provide more context to the server, like it can make informed decisions to force re-authentication in some cases.
To restate:
Device is authorized and issued an unique client credentials pair.
Locally-generated key is saved to the encrypted storage and protected by some local unlock mechanism (system lockscreen, PIN, biometrics, etc.)
The key gets registered with the server and tied to the device.
On unlocking the key is used to generate a JWT that is used as assertion for authenticating with the server.
Seems pretty standard to me, maybe someone should write up a BCP for this after thinking through all the implementation details, current practice, and security considerations.
It's funny but I can't find description of using HTTP requests to receive Google+ authentication code for offline access without iOS Google+ library.
Though example of it's integration is pretty straightforward, I don't want to add additional 20mb of weight to my iOS app.
I tried the approach described here
https://developers.google.com/accounts/docs/OAuth2WebServer#offline
but in case of server clientId I receive message abount incorrect redirect uri.
Could someone give me advice about it?
There are two approaches you could take that may/may not work for you.
Installed app flow with internally stored client secret
Web flow within a web view.
Installed app flow
You will must likely need to use the offline OAuth v2 / installed app flow which has redirect URI that has something like urn:ietf:wg:oauth:2.0:oob enabling you to redirect back to the browser, from there you can get a code to exchange for tokens.
In your case, I'm guessing you didn't create the right client type (installed application) which is preventing the *:oob redirect.
It's less secure to handle sign in in this way - if the user can extract the client secret from your app, they can do bad things like authorize a malicious 3P app with access to your application data.
Web signin flow
An alternative would be to use the web signin flow from within a WebView, something that Apple may reject your app for and which is also insecure.
What you would do is host the sign-in solution on your web server, use the JavaScript web signin flow to initiate sign in, request offline access, then exchange the resulting code for an access token and refresh token.
You would then store the refresh token / access token on the device and exchange it for an access token when you need access to the user data or exchange the refresh token server-side and pass the bearer token back to the iOS app when it needs access. This again is bad because it can expose other apps to user authorization credentials and could potentially allow a malicious 3P to access user data.
I can't recommend either approach. Is there a reason other than the app size impact that you can't use the library? I'll see if there is a good way to avoid the file size hit from the framework.
Goal:
Allow a user to authentication with Facebook into an iOS application which requires access to a protected web service that I'm running.
Assumptions:
There is a native authentication (and registration) system in place for those users that opt not to use Facebook for sign in.
Details:
Assume we want to offer the option for a user to sign in with Facebook without creating a separate account/credential for our system.
Because we support our own native auth mechanism (username and password) we have our own user IDs and issue an authentication token that is used for subsequent interactions after the initial credential validation.
I'm surprised that Facebook doesn't have best practices for this in their developer documentation. All the existing documentation is either assuming you are building FB auth into a website, or a standalone mobile app with no service that requires authentication.
Here's my initial thoughts on how this would be designed but want validation on whether it's correct.
Client pops the Facebook iOS Login
UI User signs in with Facebook credentials and gets access token
iOS App passes access token to our server
Our server talks to FB graph API using access token to (a) validate the token and (b) get the FB user ID for that access token.
e.g. Our server would call https://graph.facebook.com/me/?access_token=XYZ which would return profile info in a JSON object
Assuming it's valid, our server extracts the User ID from the JSON object and checks whether the user already has an account. If so, we issue our own auth ticket to client to use for that session. If user doesn't have an account, we create a new one with the Facebook User ID, assign our own unique UserID and issue our auth ticket.
Client then passes auth ticket back on subsequent interactions that need authentication.
This seems like the right approach to me but not sure if I'm missing something insanely basic and going down the wrong (complicated) path.
I just dealt with this myself, and here's the part that bit me:
In your step 5... It's possible for a user to register for an account with you entirely separate from their Facebook ID, right? Then some other time they log in with Facebook.... And you just created them a second account and lost their first one.
There needs to be a way to be logged in to your web service, then log in to facebook, and capture the association between the facebook ID and the local account.
Apart from that, your plan sounds solid.
Update: Facebook has added a doc outlining such a scenario HERE
Use https to transmit the auth token to your server, as stated by Facebook
Sharing of Access Tokens
Our Data Policies explicitly prohibit any sharing of an Access Token
for your app with any other app. However, we do allow developers to
share Tokens between a native implementation and a server
implementation of the same App (ie. using the same App ID) as long as
the transfer takes place using HTTPS.
One problem I can see with this strategy, is that somebody can give you an access token obtained for a different facebook app. As far as I know, there's no way to verify that the access token is for your application, so you'll just go on and use it.
It doesn't sound very harmful, though. Generally people/apps try to protect the access tokens, rather than sharing them.
One possible exploit of this would be, for somebody to create their own site or mobile app, obtain access tokens for their users and try to authenticate them, using your API. If this succeeds (the user is has a facebook account in your site), the malicious site will be able to use your API impersonating the user.
It's a bit of a long shot, but I think it could work.
Edit: It looks like there is a way to validate the access token after all. See the answer by #Daaniel on question Get application id from user access token (or verify the source application for a token).
your solution totally works.
Maybe an alternative: why not just get the email on the client from the initial social service request and send to your web service? The web service could just store the email, and maybe a social_provider as well. I understand that your web service will not be able to validate where the email came from, but isn't there a high-trust relationship between your web service and your client? If there is, seems like you can depend on the email coming from the right place. Someone please let me know what obvious thing I'm missing that makes the email-based approach silly...