DEV Community

Cover image for Power Apps - Making a Captcha
david wyatt
david wyatt

Posted on

Power Apps - Making a Captcha

Now I know what you are asking, why do I need a Captcha in Power Apps, and erm, well you don't. But that's not the point, the point is maybe I'm a bit different? well yes but the point is to learn. Developers often have personal projects, most with no point other than to practice their skills, and that's what I like to do.

I haven't made a Power App for a while so I wanted something to make, and while thinking about how to deal with a Captcha on Blue Prism I had a thought, could I make a Captcha in Power Apps.

So that was my challenge, could I come up with 5 totally different ways to beat an RPA bot.

challenge accepted gif


1. The Match Me

match me gif

This one was inspired by those annoying bike combination locks, if they can beat me half the time surely they can beat the Bot.

I setup up 2 banks of 4 icons, then first bank sets out the required combination, the second is the one you need edit to replicate the first.

OnVisible

Set(vbNew,true);
ClearCollect(colIcons,
    {icon:Icon.ArrowDown,id:1},
    {icon:Icon.ArrowLeft,id:2},
    {icon:Icon.ArrowUp,id:3},
    {icon:Icon.ArrowRight,id:4},
    {icon:Icon.Blocked,id:5}

);
ClearCollect(colSequence,Shuffle(colIcons));
ClearCollect(colPosition,
    {Value:1,id:1,match:false},
    {Value:2,id:2,match:false},
    {Value:3,id:3,match:false},
    {Value:4,id:4,match:false});

Enter fullscreen mode Exit fullscreen mode

The code creates the a collection for our first bank of icons, we then shuffle that collection to make it random (colSequence). The second is a kind of empty collection that we populate when editing the second bank of icons (colPosition).

To edit the icon from the second bank, on each icon I added below code:

OnSelect

If(Index(colPosition,1).Value=CountRows(colIcons),
    Set(viNewPosition,1);
,
    Set(viNewPosition,Index(colPosition,1).Value+1);
);

Patch(colPostion,{id:1},
    {
        Value:viNewPosition
    }
)
Enter fullscreen mode Exit fullscreen mode

The If part handles looping over from 4 back to 1, then we patch the colPosition with the icons position id.

Then to show the icon we use:
Icon

If(vbNew,
    Icon.QuestionMark
,
    Index(colIcons,Index(colPostion,1).Value).icon
)
Enter fullscreen mode Exit fullscreen mode

Finally I have a submit button that checks the 2 banks of icons match.

ForAll(Sequence(4),
    If(Index(colSequence,ThisRecord.Value).id=Index(colPosition,ThisRecord.Value).Value,
        Patch(colPostion,{id:Index(colPosition,ThisRecord.Value).id},
            {
                match:true
            }
        )
    ,
        Patch(colPostion,{id:Index(colPostion,ThisRecord.Value).id},
            {
                match:false
            }
        )
    )
);

If(CountRows(Filter(colPosition, match=true))=CountRows(colPostion),
    Notify("You are human",NotificationType.Success,1000)
,
    Notify("You are robot",NotificationType.Error,1000)
)
Enter fullscreen mode Exit fullscreen mode

It uses a sequence to loop over the nth id of the colSequence collection matches the nth value of the colPosition collection you edit. if they match we patch the colPosition to match:true.

Finally we count the rows with match=true and check to see it matches the length of colSequence.

2. The Order Me

order me gif

This one I originally used in a previous project to build a sort order for returned data, the requirement is to order the items into numerical order.

The main component is a gallery, with the collection set on the screen OnVisible:

ClearCollect(colRandom,Shuffle(Sequence(4)));
ClearCollect(colOrder,
    {
        id:1,
        order:Index(colRandom,1).Value,
        match:false
    },
    {
        id:2,
        order:Index(colRandom,2).Value,
        match:false
    },
    {
        id:3,
        order:Index(colRandom,3).Value,
        match:false
    },
    {
        id:4,
        order:Index(colRandom,4).Value,
        match:false
    }
)
Enter fullscreen mode Exit fullscreen mode

The first collection is the random order, the second is the collection that we reorder. The id is th value shown in the gallery and what we need to get into the right order. The order is how we make the gallery start radominsied. By using the Index we take the 1st from the random array and add to first in the second collection and so on.

The items for the gallery is then put in a random order like this:

Sort(colOrder,order,SortOrder.Ascending)
Enter fullscreen mode Exit fullscreen mode

To change order the up arrow has:

Set(viNewOrder,ThisItem.order-1);
Patch(colOrder,LookUp(colOrder, order=viNewOrder),
    {
        order:viNewOrder+1
    }
);
Patch(colOrder,ThisItem,
    {
        order:viNewOrder
    }
);
Enter fullscreen mode Exit fullscreen mode

and the down arrow:

Set(viNewOrder,ThisItem.order+1);
Patch(colOrder,LookUp(colOrder, order=viNewOrder),
    {
        order:viNewOrder-1
    }
);
Patch(colOrder,ThisItem,
    {
        order:viNewOrder
    }
);
Enter fullscreen mode Exit fullscreen mode

This swaps the order number with the item before/after.

Finally we check the order with the below code OnSelct on the Submit button

ForAll(Sequence(4),
    If(Index(Sort(colOrder,order),ThisRecord.Value).order=Index(Sort(colOrder,order),ThisRecord.Value).id,
        Patch(colOrder,{id:Index(Sort(colOrder,order),ThisRecord.Value).id},
            {
                match:true
            }
        )
    ,
        Patch(colOrder,{id:Index(Sort(colOrder,order),ThisRecord.Value).id},
                {
                    match:false
                }
            )
    )
);

If(CountRows(Filter(colOrder, match=true))=CountRows(colOrder),
    Notify("You are human",NotificationType.Success,1000)
,
    Notify("You are robot",NotificationType.Error,1000)
)
Enter fullscreen mode Exit fullscreen mode

It loops of the collection and checks to see if the id is the same as the order key. If it is it patches match key to true, if not then patches to false.
Finally count the rows of match true and check it is all of the items in the collection.

3. Follow Me

follow me gif

Confession, this is my favourite one 😎 The objective is to follow the random sequence.

The main component is a gallery set to 3 columns with a simple collection as the items. The Collection also has a key that is patched so that we can turn on the light to follow:

ClearCollect(colFollowSetup,
    {
        id:1,  
        on:false
    },
    {
        id:2,  
        on:false
    },
    {
        id:3,  
        on:false
    },
     {
        id:4,  
        on:false
    },
     {
        id:5,  
        on:false
    },
    {
        id:6,  
        on:false
    },
    {
        id:7,  
        on:false
    }, 
    {
        id:8,  
        on:false
    },
    {
        id:9,  
        on:false
    }
);
Set(vbTimer,false);
Set(viCurrent,0);
ClearCollect(colRandom,FirstN(Shuffle(Sequence(9)),5));
Enter fullscreen mode Exit fullscreen mode

We set the timer to start false, current 0 which tracks the iterations of the sequence and then a collection for the random sequence to follow.

To start we set the start timer to true to start it and reset the collection we use to track the users presses:

Set(vbTimer,true);
Clear(colFollow);
Enter fullscreen mode Exit fullscreen mode

On the gallery number we have the below OnPress to add the item to the collection recording the users presses:

Collect(colFollow,ThisItem.id)
Enter fullscreen mode Exit fullscreen mode

gallery

Next the cool bit, the timer. Every second it increments our iteration, it turns off the previous light and turns on the next from the random sequence using the patch. Then it checks to see if end of sequence to finish.

Set(viCurrent,viCurrent+1);

If(viCurrent>1,
    Patch(colFollowSetup,{id:Index(colRandom,viCurrent-1).Value},{
        on:false
        }
    );
);
If(viCurrent>CountRows(colRandom),
    Set(viCurrent,0);
    Set(vbTimer,false);
,

    Patch(colFollowSetup,{id:Index(colRandom,viCurrent).Value},{
        on:true
        }
    )
)
Enter fullscreen mode Exit fullscreen mode

Then we validate it by concatenating the random sequence collection with the users created collection and check to see if they match:

If(Concat(colRandom,Value,",")=Concat(colFollow,Value,","),
    Notify("You are human",NotificationType.Success,1000)
,
    Notify("You are robot",NotificationType.Error,1000)
)
Enter fullscreen mode Exit fullscreen mode

4. The Write My Colour

colour write gif

One of the things that a bot will struggle with is colours, so this one leverages a random colour. How do we show a random colour, well there is a free API for that.

There are few but the one I'm demoing is dicebear, they provide the ability to create avatar images with a url and query.

Example to get the avatar leo from the adventure series you create image with below url:

https://api.dicebear.com/7.x/adventurer/svg?seed=Leo

leo avatar

And guess what, some of them let you set the colour as a parameter.

So the demo creates an array of colours and selects one at random

ClearCollect(colColours,
    {code:"4472C4",colour:"blue",id:1},
    {code:"C739B3",colour:"purple",id:2},
    {code:"FF0000",colour:"red",id:3},
    {code:"ED7D31",colour:"orange",id:4},
    {code:"FBFB23",colour:"yellow",id:5},
    {code:"00B050",colour:"green",id:6},
    {code:"6B4845",colour:"brown",id:7}
);
Set(voColour,Index(colColours,RandBetween(1,CountRows(colColours))));
Enter fullscreen mode Exit fullscreen mode

The image src is then:

"https://api.dicebear.com/7.x/fun-emoji/svg?seed=Bear&backgroundColor="&voColour.code
Enter fullscreen mode Exit fullscreen mode

Finally the validation is very easy, does the impact match the random record (converted to lowercase to make case insensitive):

If(voColour.colour=Lower(inType.Text),
    Notify("You are human",NotificationType.Success,1000)
,
    Notify("You are robot",NotificationType.Error,1000)
)
Enter fullscreen mode Exit fullscreen mode

5. The Matthew Devaney

find cat gif

And we finally are back to what a Captcha normally is, image recognition.

Again we need a free Image API, and in this case I'm using the Cat API, which is nice enough to give me free cat images (Im sure Matt could create his own with his library 😎)

The plan was to randomly select x cats and y dogs (yep there is a Dog API too) and you select only cats. Unfortunately with the Cat API the random image returned a JSON object not a image url, I was going to look for another API but I ran out of time and ability to look at cats so I had to bodge.

I hardcode some dog urls and found a sequence of cat urls and used them. I then combined the 2 collections, randomised them and returned the first 9:

ClearCollect(colAnimals,
    {url:"https://cdn2.thedogapi.com/images/CZfQK3eOk.jpg",type:"dog",id:10},
    {url:"https://cdn2.thedogapi.com/images/9NJbNWAW9.jpg",type:"dog",id:11},
    {url:"https://cdn2.thedogapi.com/images/B8zP8i5W5.jpg",type:"dog",id:12},
    {url:"https://cdn2.thedogapi.com/images/e10KCAlSG.jpg",type:"dog",id:13},
    {url:"https://cdn2.thedogapi.com/images/qh7cGcv83.jpg",type:"dog",id:14},
    {url:"https://cdn2.thedogapi.com/images/dSE-HKnRP.jpg",type:"dog",id:15},
    {url:"https://cdn2.thedogapi.com/images/w4bONqP_O.jpg",type:"dog",id:16},
    {url:"https://cdn2.thedogapi.com/images/awSN72EWf.png",type:"dog",id:17},
    {url:"https://cdn2.thedogapi.com/images/ssY_amKgW.jpg",type:"dog",id:18}
);
ForAll(Sequence(9),
    Collect(colAnimals,
        {
            url:"https://cdn2.thecatapi.com/images/b0"&ThisRecord.Value&".jpg",
            type:"cat",
            id:ThisRecord.Value
        }
    )
);
ClearCollect(colOptions,FirstN(Shuffle(colAnimals),9));
Enter fullscreen mode Exit fullscreen mode

The images are added to a gallery (like the follow me), and then on the image press the image id is saved to another collection. To stop a image being added multiple times I added a piece of logic to check if the collection already contained the item.

If(IsEmpty(Filter(colSelect,id=ThisItem.id)),
    Collect(colSelect,ThisItem)
);
Enter fullscreen mode Exit fullscreen mode

The validation step is then to count the 2 collections filtered to cat, if they are the same length then every cat has been selected, if not then the have missed one (that's why we only allow each image once to be selected):

If(
    (CountRows(Filter(colOptions,type="cat"))=CountRows(Filter(colSelect,type="cat")))&&
    IsEmpty(Filter(colSelect,type="dog"))
,
    Notify("You are human",NotificationType.Success,1000)
,
    Notify("You are robot",NotificationType.Error,1000)
);
Clear(colSelect);
Enter fullscreen mode Exit fullscreen mode

Hopefully I inspired you to do some random projects, I learned some new techniques, and I'm now thinking this could be a cool interview activity for Power App Developers, so win win.

Solution available to download and look at here

Further Reading

Top comments (5)

Collapse
 
jonathannaylor profile image
Jonathan Naylor

You have given wonderful concepts about developing a best captcha option for a business website. I am developing the new variation of theacademicpapers.co.uk/ to optimize the contact and order form. Currently, it is using Google's invisible captcha option but I think that it is not the appropriate way to get rid of spammy messages. The option which you provided, seems the best option to move ahead with as this is not only fency but also will increase the user engagement on the contact form.

Collapse
 
reubenbode profile image
Reuben Bode • Edited

Thanks for sharing it with us. I appreciate you. I know that as a student, every student hates to write dissertations; they make me so confused, so to get rid of this problem, a few months ago, I found this ukwritings.com/dissertation-help website online, which offers dissertation writing at a very affordable price. If you are also like me, then you can also use their service for the best results at a very cheap price.

Collapse
 
jaloplo profile image
Jaime López

One of the greatest articles I've read ever!!! It's funny, clear, concise, and to the point. And, shows what it has to show.

Congrats!!!!!

Collapse
 
wyattdave profile image
david wyatt

Thank you ❤️

Collapse
 
balagmadhu profile image
Bala Madhusoodhanan

Love the dicebear API. for a second i thought you had used AI builder.....

thanks for the inspiring and fun tutorials !!!