How do I create new resource once SCIM is integrated with my service provider? - race-condition

I'm a SCIM newbie.
In SCIM work flow, I'm just curious how to create new user on service provider side.
If I create new user into local user table on service provider side, and then send a SCIM post user request. This request might fail due to several reasons, ex: network disconnect, service shutdown, data validation fail, etc.
So I plan to use SCIM api on service provider side to create new user. The work flow will looks like below Desgin#1.
The Desgin#2 I think it's incorrect, because I can find some problme will occur in this design like race condition.
Is my conception correct?
(1) Design#1: I intend to use this. All user operations are performed by IdP side.
I can avoid some problem like race condition.
┌───────────────────┐ ┌───────────────────────┐
│ │ │ │
│ SCIM IdP Side │ │ Service Provider Side │
│ │ │ │
│ (ex: Okta, Azure )│ │ (ex: my website) │
│ │ │ │
│ [IdP] │ │ [SP] │
│ │ │ │
└────────┬──────────┘ └────────────┬──────────┘
│ │
│ │
│ │
│◄─────────────────────────────────────┤
│ (1) POST USER Through SCIM Protocol │
│ │
│ │
│ │
│ │
│ │
├─────────────────────────────────────►│
│ (2) IdP created successfully, │
│ got by SP and the create │
│ user entry in SP's database │
│ │
│ Also notify other SP by SCIM │
│ protocol(new user created) │
▼ ▼
(2) Design#2: I think this will cause race conditon, once we have lots of SP resouce creating
┌───────────────────┐ ┌───────────────────────┐
│ │ │ │
│ SCIM IdP Side │ │ Service Provider Side │
│ │ │ │
│ (ex: Okta, Azure )│ │ (ex: my website) │
│ │ │ │
│ [IDP] │ │ [SP] │
│ │ │ │
└────────┬──────────┘ └────────────┬──────────┘
│ │
│ ├────────┐
│ │ │ (1) SP create new user in
│ │ │ local Database
│ │ │
│ │ │
│ │ │
│ │ │
│◄─────────────────────────────────────┤◄───────┘
│ (2) After user is created locally, │
│ POST USER Through SCIM Protocol │
│ to remote IdP Side. │
│ │
│ │
├─────────────────────────────────────►│
│ (3) Also notify other SP by SCIM │
│ protocol(new user created) │
▼ ▼

You seem to be familiar with SAML terminology and are getting it mixed up with SCIM. To be fair they are related with some shared wording, but without reading the RFC to be 100% accurate, it can be explained in other words that might help straighten it out.
I think of a simple 1:1 SCIM relationship as a Directory Service and a Downstream Application. As I say, these are not part of the RFC but aid in understanding.
It's the responsibility of the Directory Service to hold user's identity and personal information. Because of the nature of this information, they are also often Identity Providers (Okta, AAD), but not necessarily.
When an integration between a Directory Service and a Downstream Application (using SCIM) is set up, the normal flow of user information is one way.
This is important.
Things manage users in the Directory Service. This can be any CRUD operation, and once completed in it's own database (the source of the truth about the user), the SCIM web-based calls will be made to provision, edit, or delete the user in the downstream application.
The SCIM protocol does not define the specific requests that need to be made. For example to create a new user in the downstream application, the identity service must first check to see if the user already exists and act appropriately. Something like this would be a sensible implementation:
Request user xyz#domain.com from application
If the user does not exist, make a request to create them.
If the user exists, check the returned information for differences with locally held information; if differences exist, update user in application.
Depending on your application, you may not allow any user management at all, and the users are simply managed exclusively by the SCIM protocol. This is totally fine.
However, it gets more complex if you;
Already have users in your application, and
Allow editing of information in your application
If either of these things are the case then you must take special efforts to block the users or administrators from changing fields that SCIM is responsible for. There is no automatic triggering for an application to tell the directory service that a change has occurred.
While services like Okta allow for bulk importing from a downstream application back into itself, this is a chosen implementation and not really what SCIM was intended for in this scenario.
In summary: Directory Services are the definitive source for your user information. You will get user data from this service, and will not supply data to it via SCIM, or be the trigger for creating users from your application.
Hope that explains things a bit.

Related

google cloud authenticating to the wrong project ID

I'm trying to follow the quick start code for using the perspective API but the code fails because it keeps defaulting to another project.
I have already created an appropriate service account for project A, which is my intended project. I downloaded the corresponding SA key file and my API key is affiliated with project A. But every time I run this code (at the line discovery.build()) I get an error message because it's using project B and this service is only enabled for project A.
from googleapiclient import discovery
from google.oauth2 import service_account
import os
API_KEY = 'myapikey'
SA_FILE = 'my_sa_keyfile.json'
scopes = ['https://www.googleapis.com/auth/cloud-platform']
credentials = service_account.Credentials.from_service_account_file(
SA_FILE,
scopes=scopes)
client = discovery.build(
"commentanalyzer",
"v1alpha1",
credentials=credentials,
developerKey=API_KEY,
discoveryServiceUrl="https://commentanalyzer.googleapis.com/$discovery/rest?version=v1alpha1",
static_discovery=False,
)
There's no place in discovery.build() to set my project ID. What am I doing wrong?

Remix authentication and nested route loaders

I have a Remix app with routes like defined below, where I want a, b and c to be authenticated routes:
root/
└── a/
├── b
└── c
In route a loader I check for an authenticated user:
// in a.tsx, inside `loader` function
authenticator.isAuthenticated(
{...},
failureRedirect: '...'
)
This works fine since loading /a/b triggers a loader, which redirects upon auth failure.
But do I also need to do the check authentication in loaders for routes b and c?
Since a loader is not triggered when navigating from /a/b to /a/c, is it safe to assume in c loader that user is authenticated? And if not, what's the Remix way? Calling isAuthenticated in every segment would be very cumbersome.
Thanks
Yes. With Remix, each route loader is independent. They will be called in parallel, and sometimes your ancestor loaders won't even be called.
So generally, you should protect each loader. Typically this is done via a function like const user = await requireUser(request)
The function would validate that you have a logged-in user and throw a redirect to the login page if not.
I also add a check in my express entry to see if I have the auth token before handing the request to Remix. I have a whitelist of public routes. This way ALL routes are protected by default, and I have to specifically opt out.
Anyway, Remix is working on adding a beforeLoader export for this type of scenario. https://github.com/remix-run/react-router/discussions/9564

Amplify: How to remove Auth and add new Auth resource with Api resource. Error Template error: instance of Fn::GetAtt references undefined resource

In amplify, I have added Api and a Auth (AWS Cognito) resources as follows. The Api is configured to use Auth Cognito User Pool to authenticate.
┌──────────┬───────────────┬───────────┬───────────────────┐
│ Category │ Resource name │ Operation │ Provider plugin │
├──────────┼───────────────┼───────────┼───────────────────┤
│ Auth │ testd50c8ec7 │ No Change │ awscloudformation │
├──────────┼───────────────┼───────────┼───────────────────┤
│ Api │ testApi │ No Change │ awscloudformation │
└──────────┴───────────────┴───────────┴───────────────────┘
I'm still in development and I want to change Auth (Cognito) to login with phone number. So, I have to remove Auth and add a new Auth resource (changing an existing user pool with phone log in is not allowed). When I remove the Auth resource and do a amplify push. I get an error:
Template error: instance of Fn::GetAtt references undefined resource testd50c8ec7
What should I do to remove the old and add a new Auth resource?
I struggled with this for many days so I'm typing this answer up to help myself (and the reader) hours of future frustration.
It seems like once Api is linked to an Auth user pool configuration, it always needs an active Auth Cognito User Pool resource available. So, I needed to follow these steps in order:
Remove the old Auth
amplify remove auth
ADD a new Auth resource
amplify add auth
Feel free to modify ./backend/auth/<new resource name>/parameters.json and backend/auth/<new resource name>/<new resource name>-cloudformation-template.yml to your liking based on this AWS doc
UPDATE the Api resource so it picks up the new Auth name to be created.
% amplify update api
? Please select from one of the below mentioned services: GraphQL
? Select from the options below Update auth settings
? Choose the default authorization type for the API Amazon Cognito User Pool
Use a Cognito user pool configured as a part of this project.
? Configure additional auth types? Yes
? Choose the additional authorization types you want to configure for the API API key
API key configuration
? Enter a description for the API key:
? After how many days from now the API key should expire (1-365): 7
Now, amplify status should show something like this:
┌──────────┬─────────────────┬───────────┬───────────────────┐
│ Category │ Resource name │ Operation │ Provider plugin │
├──────────┼─────────────────┼───────────┼───────────────────┤
│ Auth │ test852bbeb0 │ Create │ awscloudformation │
├──────────┼─────────────────┼───────────┼───────────────────┤
│ Api │ testApi │ Update │ awscloudformation │
├──────────┼─────────────────┼───────────┼───────────────────┤
│ Auth │ testd50c8ec7 │ Delete │ awscloudformation │
└──────────┴─────────────────┴───────────┴───────────────────┘
Now you amplify push with no errors or struggle.

How can I resolve a dependency on something on the current request?

I'm setting up dependency injection in my ASP.NET Web API application. I have a scenario where a dependency has a dependency on something on the current request (the hostname, for example).
So how can I use the current request as a dependency?
Below is what I've been able to come up with, but HttpContext.Current won't work in a self-hosted scenario.
kernel.Bind<IFoo>()
.ToMethod((context) =>
{
string hostName = HttpContext.Current.Request.Url.Host;
var foo = FooFactory.GetByHostName(hostName);
return foo;
})
.InRequestScope();
I'm currently using Ninject, but I can switch to a different IoC container if there's one that can handle this scenario better.

yesod -- password protecting staging site

I'm trying to set up a staging instance of my yesod webserver, and I was wondering if there were some easy way to make the entire site password protected. Specifically, I want to be able to prompt those who navigate to my site for credentials. After they authenticate it should function as the typical site. But if they cannot authenticate themselves they should see nothing.
To expand on #MichaelSnoyman's answer, here's how I implemented the WAI HTTP Auth middleware:
From the scaffolded site, I went to Application.hs, which has already setup some logging middleware like so:
makeApplication :: AppConfig DefaultEnv Extra -> IO Application
makeApplication conf = do
foundation <- makeFoundation conf
-- Initialize the logging middleware
logWare <- mkRequestLogger def
{ outputFormat =
if development
then Detailed True
else Apache FromSocket
, destination = RequestLogger.Logger $ loggerSet $ appLogger foundation
}
-- Create the WAI application and apply middlewares
app <- toWaiAppPlain foundation
return $ logWare app
To add HTTP auth, I referenced the Yesod book's chapter on WAI and the HttpAuth docs that Michael referenced. The docs give this as an example of using the HttpAuth middleware:
basicAuth (\u p -> return $ u == "michael" && p == "mypass") "My Realm"
I was able to just paste that at the bottom right after the logging middleware is applied:
import qualified Network.Wai.Middleware.HttpAuth as HttpAuth
makeApplication :: AppConfig DefaultEnv Extra -> IO Application
makeApplication conf = do
foundation <- makeFoundation conf
-- Initialize the logging middleware
logWare <- mkRequestLogger def
{ outputFormat =
if development
then Detailed True
else Apache FromSocket
, destination = RequestLogger.Logger $ loggerSet $ appLogger foundation
}
-- Create the WAI application and apply middlewares
app <- toWaiAppPlain foundation
return $ logWare $ HttpAuth.basicAuth (\u p -> return $ u == "michael" && p == "mypass") "My Realm" $ app
Here's what that looks like in Safari:
This kind of authentication isn't really appropriate for regular users, but its great for locking down a site meant for internal use. Its also an easy way for machines (monitoring servers, scripts) to authenticate themselves with your server.
You could use the http auth middleware.
http://hackage.haskell.org/package/wai-extra-3.0.1/docs/Network-Wai-Middleware-HttpAuth.html
Sorry for brevity, on a mobile.