We have an MVC app that connects to the Exchange server. We used to connect to an on premises server using this code to create the service:
if (string.IsNullOrEmpty(Current.UserPassword))
{
throw new UnauthorizedAccessException("Exchange access requires Authentication by Password");
}
return new ExchangeService
{
Credentials = new NetworkCredential(Current.User.LoginName, Current.UserPassword),
Url = new Uri(ConfigurationManager.AppSettings["ExchangeServiceUrl"]),
};
This worked fine, but now our IT department is migrating the Exchange server to the cloud, and some users are on the cloud server while others are on premises. So I changed the code to this:
if (string.IsNullOrEmpty(Current.UserPassword))
{
throw new UnauthorizedAccessException("Exchange access requires Authentication by Password");
}
var user = ConfigurationManager.AppSettings["ExchangeUser"];
var password = ConfigurationManager.AppSettings["ExchangePassword"];
var exchangeService = new ExchangeService(ExchangeVersion.Exchange2010_SP2)
{
Credentials = new NetworkCredential(user, password),
};
exchangeService.AutodiscoverUrl(Current.EmaiLuser + "#calamos.com", RedirectionCallback);
exchangeService.Credentials = new NetworkCredential(Current.EmaiLuser + "#calamos.com", Current.UserPassword);
return exchangeService;
I am using a service account to do the autodiscovery ( for some reason it doesn't work with a regular account) and then I am changing the credentials of the service to the user that logs in, so he can access the inbox. The problem is that , randomly, the server returns "The request failed. The remote server returned an error: (401) Unauthorized.".
I asked the IT department to check the Exchange logs, but there is nothing there about this error, so I don't know how to fix it...
So by cloud do you mean Office365 ?
I am using a service account to do the autodiscovery ( for some reason it doesn't work with a regular account)
For the users in the cloud you need to ensure the request are sent to the cloud servers maybe enable tracing https://msdn.microsoft.com/en-us/library/office/dd633676(v=exchg.80).aspx and then have a look at where the failed requests are being routed. From what you are saying your discovery is going to always point to your internal servers which is why the request will fail for the cloud based users. You need to have a way of identifying the users that are in the cloud and I would suggest you then just use the single Office365 Endpoint (eg you don't need Autodiscover for that) https//outlook.office365.com/EWS/Exchange.asmx
Related
I am trying to create a namespace on a K8s cluster on Azure using teh fabric8 java client . Here is the code
#Before
public void setUpK8sClient() {
apiServer = "";
config = new ConfigBuilder().withMasterUrl(apiServer).withUsername("user").withPassword("pass").build();
client = new DefaultKubernetesClient(config);
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
}
#Test
public void getClientVersion() {
System.out.println("Client version "+client.getApiVersion());
}
#Test
public void createNamespace() {
Namespace myns = client.namespaces().createNew()
.withNewMetadata()
.withName("myns")
.addToLabels("a", "label")
.endMetadata()
.done();
System.out.println("Namespace version " + myns.getStatus());
}
This gives me the following error
i
o.fabric8.kubernetes.client.KubernetesClientException: Failure executing: POST at: "https://...api/v1/namespaces. Message: Unauthorized! Token may have expired! Please log-in again. Unauthorized
What did I miss?
Since you are working on Azure, I guess you could follow the instructions to configure kubectl and then use the token from the kubeconfig file to access the cluster from the fabric8 client.
That token is probably an admin token, so you can also create new credentials (user/password) if you want to limit what the fabric8 client could do. API requests are tied to either a normal user or a service account, or are treated as anonymous requests.
Normal users are assumed to be managed by an outside, independent service (private keys, third parties like Google Accounts, even a file with a list of usernames and passwords). Kubernetes does not have objects which represent normal user accounts.
Service accounts are users managed by the Kubernetes API, bound to specific namespaces. Service accounts are tied to a set of credentials stored as Secrets. To manually create a service account, simply use the kubectl create serviceaccount ACCOUNT_NAME command. This creates a service account in the current namespace and an associated secret that holds the public CA of the API server and a signed JSON Web Token (JWT).
I'm trying to use google Oauth 2 to authenticate with google calendar API for a web server running on AWS EC2.
When I generated the credentials I selected 'OAuth Client ID' and then 'Web Application'. For the Authorised redirect URIs I have entered:
http://ec2-XX-XX-XX-XXX.eu-west-1.compute.amazonaws.com
(I've blanked out the IP of my EC2 instance). I have checked this is the correct URL that I want the callback to go to.
The link that is generated in the server logs is of the form:
https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=XXXXXXXXXXXX-XXXXXXXXXXXXXX.apps.googleusercontent.com&redirect_uri=http://localhost:47258/Callback&response_type=code&scope=https://www.googleapis.com/auth/calendar.readonly
When I follow the link I get the error
'Error: redirect_uri_mismatch'.
I've read this SO question and have checked that I am using HTTP and there is no trialing '/'
I suspect that the URL generated should not have 'localhost' in it but I've reset the client_secret.json several times and each time I restart tomcat with the new client secret I still get a link with localhost but just over a different port.
Locally, I had selected Credentials type of 'other' previously and was not given an option for the Authorised redirect URI. I did try this for the EC2 instance but this won't give me the control I want over the redirect URI and sends the redirect over localhost.
Google throws redirect_uri_mismatch when the uri (including ports) supplied with the request doesn't match the one registered with the application.
Make sure you registered the Authorised redirect URIs and Authorised JavaScript origins on the web console correctly.
This is a sample configuration that works for me.
In case you are seeing this error while making API call from your server to get tokens.
Short Answer 👇 - What solved my problem
use string postmessage in place of actual redirectUri that you configured on cloud console.
Here is my initilization of OAuth2 client that worked for me.
// import {Auth, google} from 'googleapis`;
const clientID = process.env.GOOGLE_OAUTH_CLIENT_ID;
const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET;
oauthClient = new google.auth.OAuth2(clientID,clientSecret,'postmessage');
My Case
On the frontend, I am using react to prompt the user for login with google with the authentication-code flow. On success, this returns code in the payload that needs to be posted to the google API server to get token - Access Token, Refresh Token, ID Token etc.
I am using googleapis package on my server. Here is how I am retrieving user info from google
// import {Auth, google} from 'googleapis`;
const clientID = process.env.GOOGLE_OAUTH_CLIENT_ID;
const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET;
oauthClient = new google.auth.OAuth2(clientID,clientSecret,'postmessage');
/*
get tokens from google to make api calls on behalf of user.
#param: code -> code posted to backend from the frontend after the user successfully grant access from consent screen
*/
const handleGoogleAuth = (code: string) => {
oauthClient.getToken(code, async (err, tokens: Auth.Credentials) {
if (err) throw new Error()
// get user information
const tokenInfo = await oauthClient.verifyIdToken({
idToken: tokens.id_token
});
const {email, given_name, family_name, email} = tokenInfo.getPayload();
// do whatever you want to do with user informaton
}
}
When creating a Oath client ID, DO NOT select web application, Select "Other". This way, the Redirect URI is not required.
I'm trying to implement a process which combines Google sign-in on client side (Web page) with server side verification and query user data (Java server).
What I did:
In Google developer console, added an OAuth 2.0 client IDs credential.
Implemented the sign-in on the web page and got the ID token after successful login.
Implemented the authentication with a backend server as explained here:
https://developers.google.com/identity/sign-in/web/backend-auth. This part also works and I can verify the authentication and get the user's e-mail address.
What I need to do now is getting the user's profile information, i.e. first and last name and access the app folder, to store relevant application data.
This is my server side code. I marked the part where I need help:
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(HTTP_TRANSPORT, JSON_FACTORY)
.setAudience(Arrays.asList(service.getClientId()))
.build();
GoogleIdToken idToken = null;
try {
idToken = verifier.verify(token); // token is the ID token received from the client
} catch (GeneralSecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (idToken != null) {
GoogleIdToken.Payload payload = idToken.getPayload();
payload.getEmail() <== This works
/*
Here I need to query Google API per the available application scopes: profile, app storage etc.
*/
}
Is it possible to use the API at this stage? If not, can I request access token here? Should I use the Client ID or do I need a different type of credential (like API key or Service account)?
ID Token represents authentication, not authorization. So you won't be able to access Google APIs just with ID Token.
In order to make requests to Google APIs from server side, do following on client side.
var auth2 = gapi.auth2.getAuthInstance();
auth2.grantOfflineAccess({
scope: 'SCOPES_COMES_HERE'
}).then(function(resp) {
// send `resp.code` to server to exchange it with
// credentials (access_token, refresh_token
});
The code is the key to exchange with access_token.
You might be inclined to implement authentication and authorization at the same time, but Google's recommendation is to separate them and request permissions as they are needed (incremental authorization). Leave the current code and add above + server side that handles code to exchange with access_token.
Detailed doc: https://developers.google.com/identity/sign-in/web/server-side-flow
I have IdentityServer with Membership Reboot and IdentityManager running on a remote server, I've used the Admin UI of IdentityManager to setup a user, and add roles & claims to said user.
I'm developing a WebApi/SPA project that will use the remote server for Auth. Using fiddler I can request a token from the IdentityManagner on the remote box and use this token to against the local WebApi where Authorization is required. If the token is valid the WebApi processes like normal, if the token is bogus I get a 401. Works great.
The problem is when I want additional information about the user none of the claims or identity information is coming across. I'm not sure if the problem is at the IdentityServer side, The WebApi side, or if I'm not doing something correctly when getting my token.
I didn't realize we needed put the claims in the Scope definition. Incase anyone else stumbles upon this I changed my scope to the following
var scopes = new List<Scope>
{
new Scope
{
Enabled = true,
Name = "publicApi",
Description = "Access to our public API",
Type = ScopeType.Resource,
IncludeAllClaimsForUser = true, //I'll filter this down later
}
};
scopes.AddRange(StandardScopes.All);
return scopes;
Further details can be found here
I am connecting to CRM with the intention of retrieving a list of picklist values. On my development machine I am working under my own login name and all works fine. On the test server, the code executes under the NETWORK SERVICE account. When it connects to the CRM web service everything is great. When it connects to the metadata service I get 401 Unauthorised messages.
This is the first time I have used the metadata service so I am hoping someone can tell me why I get the error. The connection is configured using the code below and the failure happens when you try to retrieve the picklist data.
CrmAuthenticationToken token = new CrmAuthenticationToken();
token.OrganizationName = config.AppSettings.Settings["CrmTargetOrganisation"].Value;
token.AuthenticationType = 0;
MetadataService service = new MetadataService();
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
service.Url = config.AppSettings.Settings["CrmMetadataServiceUrl"].Value;
service.CrmAuthenticationTokenValue = token;
service.UnsafeAuthenticatedConnectionSharing = true;
I suspect it might be a Kerberos / delegation issue, to make sure it is try replacing DefaultCredentials with
new System.Security.Net.NetworkCredentials("username","password","domain");
See if that still gives you a 401.
This is the quick way I normally try to see if it is kerbos/security related.
I need a bit more information about your environment to make any other intelligent comments.
Hope it helps.
In my case (yes, we still use CRM 4), the website in IIS wasn't bound to the hostname being used to access the metadata service on port 5555.